Bonjour,
Une question un peu technique pour ceux qui savent coder en C du
HTTP 1.1
Contexte: à mes rares heures perdues, je bricole un petit serveur
FastCGI pour expérimenter sur mes idées exposées
ici
Plus tard, (si j'arrive à quelque chose) l'essentiel du code de ce serveur sera réflexif et auto-généré -et bien évidemment en libre sous license GPLv3 (ou LGPL?), mais pour l'instant, j'en suis juste à faire le "runtime" initial en C.
Mon souci est de produire un contenu compressé. De sorte que si la requête HTTP courante contient dans son entête
Accept-Encoding: gzip
(ce qui est le cas des navigateurs courants) je veux, dans certains cas, effectivement compresser selon l'algorithme gzip.
Ma question précise est: comment produire (en C) un contenu gzippé à partir du contenu initial en mémoire (donc un
char*buf de longueur
long buflen)
Je pensais que il faut bien évidemment utiliser
<zlib.h> (et la traditionnelle
zlib) et notamment sa fonction
compress. Est ce que cette fonction nommée
compress implémente bien la compression
gzip du RFC 1952?
Plus précisément, je crois faire la chose suivante. Pour simplifier mon FASTCGI a déjà généré l'entête dans un
char* headbuf de longueur
int headlen supposée suffisamment longue, donc a exécuté quelque chose du genre de
strcpy(headbuf,
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html\r\n");
En réalité, le code est plus complexe car il gère convenablement la longueur, alloue les chaînes par des GC_MALLOC_ATOMIC (car j'utilise le GC de Boehm), etc..
Ensuite il génère le corps dans un
char* bodybuf de longueur suffisante
int bodylen, donc un document HTML avec pour simplifier quelque chose du genre de
sprintf(bodybuf, "<html><head><title>mon titre</title></head>\n"
"<body>salut %d fois</body></html>\n", compteur);
Dans le cas simple (sans compression gzip), j'envoie l'entête et le corps simplement par
/* ajout de la longueur du corps au bout de l'entête */
sprintf(headbuf+strlen(headbuf),"Content-Length: %d\r\n", strlen(bodybuf));
/* envoi de l'entête avec la ligne vide finale */
FCGX_FPrintF(out, "%s\r\n", headbuf);
/* envoi du corps */
FCGX_PutStr (bodybuf, strlen(bodybuf), out);
et ca marche bien.
Dans certains cas, je désire compresser (si la requête contient Accept-Encoding: gzip et si bodybuf a plus de 1000 octets). J'alloue donc un tampon pour le résultat de la compression
long zlen = 9*headlen/8+40;
char* zbuf = malloc(zlen);
memset(zbuf, 0, zlen);
long bodylen=strlen(bodybuf);
je le comprime par
compress((Bytef *) zbuf, &zlen, (Bytef *)bodybuf, bodylen);
je complète l'entête convenablement par
sprintf(headbuf+strlen(headbuf),
"Content-Length: %d\r\n"
"Content-Encoding: gzip\r\n",
zlen);
Enfin j'envoie l'entête et le corps par
FCGX_FPrintF(out, "%s\r\n", headbuf);
FCGX_PutStr (zbuf, zlen, out);
Et ca ne marche pas (le navigateur n'affiche rien). J'imagine que j'ai mal compressé par gzip le contenu, càd qu'en plus du résultat de compress il faut envoyer un entête (mais je ne me suis pas encore plongé dans RFC 1952
Si vous avez un avis pertinent, faites moi signe.
Désolé d'avoir embêté tous les autres (mais je réponds bien plus souvent sur Léa que je ne pose une question... )
Pour des raisons non exposées ici (ça serait plus simple en MetaOcaml qui hélàs est mort), je tiens à coder en C de bas niveau.
----
Basile STARYNKEVITCH
Membre de l'
APRIL « promouvoir et défendre le logiciel libre » - adhérez vous aussi à l'APRIL!
Projet logiciel libre:
RefPerSys