Better handling of gzip compressed files

v0.9.0
Jindra Dolezy 10 years ago
parent 4596834b34
commit 2ec0e15427
  1. 11
      Makefile
  2. 46
      httpd/httpd.c
  3. 3
      httpd/httpd.h
  4. 47
      httpd/httpdespfs.c

@ -166,18 +166,17 @@ flash: $(TARGET_OUT) $(FW_BASE)
$(Q) $(ESPTOOL) --port $(ESPPORT) --baud $(ESPBAUD) write_flash 0x00000 $(FW_BASE)/0x00000.bin 0x40000 $(FW_BASE)/0x40000.bin $(Q) $(ESPTOOL) --port $(ESPPORT) --baud $(ESPBAUD) write_flash 0x00000 $(FW_BASE)/0x00000.bin 0x40000 $(FW_BASE)/0x40000.bin
webpages.espfs: html/ html/wifi/ espfs/mkespfsimage/mkespfsimage webpages.espfs: html/ html/wifi/ espfs/mkespfsimage/mkespfsimage
ifeq ($(GZIP_COMPRESSION),"yes") ifeq ($(COMPRESS_W_YUI),"yes")
$(Q) rm -rf html_compressed; $(Q) rm -rf html_compressed;
$(Q) cp -r html html_compressed; $(Q) cp -r html html_compressed;
ifeq ($(COMPRESS_W_YUI),"yes")
$(Q) echo "Compression assets with yui-compressor. This may take a while..." $(Q) echo "Compression assets with yui-compressor. This may take a while..."
$(Q) for file in `find html_compressed -type f -name "*.js"`; do $(YUI-COMPRESSOR) --type js $$file -o $$file; done $(Q) for file in `find html_compressed -type f -name "*.js"`; do $(YUI-COMPRESSOR) --type js $$file -o $$file; done
$(Q) for file in `find html_compressed -type f -name "*.css"`; do $(YUI-COMPRESSOR) --type css $$file -o $$file; done $(Q) for file in `find html_compressed -type f -name "*.css"`; do $(YUI-COMPRESSOR) --type css $$file -o $$file; done
$(Q) awk "BEGIN {printf \"YUI compression ratio was: %.2f%%\\n\", (`du -b -s html_compressed/ | sed 's/\([0-9]*\).*/\1/'`/`du -b -s html/ | sed 's/\([0-9]*\).*/\1/'`)*100}" $(Q) awk "BEGIN {printf \"YUI compression ratio was: %.2f%%\\n\", (`du -b -s html_compressed/ | sed 's/\([0-9]*\).*/\1/'`/`du -b -s html/ | sed 's/\([0-9]*\).*/\1/'`)*100}"
endif
$(Q) cd html_compressed; find . -type f -regex ".*/.*\.\(html\|css\|js\)" -exec sh -c "gzip -n {}; mv {}.gz {}" \;; cd ..; # mkespfsimage will compress html, css and js files with gzip by default if enabled
# override with -g cmdline parameter
$(Q) cd html_compressed; find | ../espfs/mkespfsimage/mkespfsimage > ../webpages.espfs; cd ..; $(Q) cd html_compressed; find | ../espfs/mkespfsimage/mkespfsimage > ../webpages.espfs; cd ..;
$(Q) awk "BEGIN {printf \"GZIP compression ratio was: %.2f%%\\n\", (`du -b -s html_compressed/ | sed 's/\([0-9]*\).*/\1/'`/`du -b -s html/ | sed 's/\([0-9]*\).*/\1/'`)*100}"
else else
$(Q) cd html; find | ../espfs/mkespfsimage/mkespfsimage > ../webpages.espfs; cd .. $(Q) cd html; find | ../espfs/mkespfsimage/mkespfsimage > ../webpages.espfs; cd ..
endif endif
@ -196,7 +195,7 @@ clean:
$(Q) make -C espfs/mkespfsimage/ clean $(Q) make -C espfs/mkespfsimage/ clean
$(Q) rm -rf $(FW_BASE) $(Q) rm -rf $(FW_BASE)
$(Q) rm -f webpages.espfs $(Q) rm -f webpages.espfs
ifeq ($(GZIP_COMPRESSION),"yes") ifeq ($(COMPRESS_W_YUI),"yes")
$(Q) rm -rf html_compressed $(Q) rm -rf html_compressed
endif endif

@ -66,19 +66,6 @@ static const MimeMap mimeTypes[]={
{NULL, "text/html"}, //default value {NULL, "text/html"}, //default value
}; };
// The static files with the following extensions from the HTML folder will be compressed and served with GZIP compression
// Add any other file types you want compressed here (and don't forget to modify the Makefile too)
#ifdef GZIP_COMPRESSION
static const char * gzippedFileTypes[] = {
"js",
"html",
"css",
NULL
};
static const char gzipNonSupportedMessage[] = "<html><head></head><body>Your browser does not accept gzip-compressed data.</body></html>";
#endif
//Returns a static char* to a mime type for a given url to a file. //Returns a static char* to a mime type for a given url to a file.
const char ICACHE_FLASH_ATTR *httpdGetMimetype(char *url) { const char ICACHE_FLASH_ATTR *httpdGetMimetype(char *url) {
int i=0; int i=0;
@ -92,39 +79,6 @@ const char ICACHE_FLASH_ATTR *httpdGetMimetype(char *url) {
return mimeTypes[i].mimetype; return mimeTypes[i].mimetype;
} }
#ifdef GZIP_COMPRESSION
//Sends Content-encoding header if the requested file was GZIP compressed
//If the client does not sent the Accept-encoding, send out a static html message.
const char* sendGZIPEncodingIfNeeded(HttpdConnData *connData) {
int i=0;
char acceptEncodingBuffer[64];
//Go find the extension
char *ext=connData->url+(strlen(connData->url)-1);
while (ext!=connData->url && *ext!='.') ext--;
if (*ext=='.') ext++;
//ToDo: os_strcmp is case sensitive; we may want to do case-intensive matching here...
while (gzippedFileTypes[i]!=NULL) {
if (os_strcmp(ext, gzippedFileTypes[i])==0) {
//when serving gzipped files check the browser's "Accept-Encoding" header
//if the client does not advertises that he accepts GZIP send a warning message (telnet users for e.g.)
httpdGetHeader(connData, "Accept-Encoding", acceptEncodingBuffer, 64);
if (os_strstr(acceptEncodingBuffer, "gzip") == NULL) {
//No Accept-Encoding: gzip header present
return gzipNonSupportedMessage;
} else {
httpdHeader(connData, "Content-Encoding", "gzip");
return NULL;
}
}
i++;
}
return NULL;
}
#endif
//Looks up the connData info for a specific esp connection //Looks up the connData info for a specific esp connection
static HttpdConnData ICACHE_FLASH_ATTR *httpdFindConnData(void *arg) { static HttpdConnData ICACHE_FLASH_ATTR *httpdFindConnData(void *arg) {
int i; int i;

@ -56,9 +56,6 @@ int httpdUrlDecode(char *val, int valLen, char *ret, int retLen);
int ICACHE_FLASH_ATTR httpdFindArg(char *line, char *arg, char *buff, int buffLen); int ICACHE_FLASH_ATTR httpdFindArg(char *line, char *arg, char *buff, int buffLen);
void ICACHE_FLASH_ATTR httpdInit(HttpdBuiltInUrl *fixedUrls, int port); void ICACHE_FLASH_ATTR httpdInit(HttpdBuiltInUrl *fixedUrls, int port);
const char *httpdGetMimetype(char *url); const char *httpdGetMimetype(char *url);
#ifdef GZIP_COMPRESSION
const char* sendGZIPEncodingIfNeeded(HttpdConnData *connData);
#endif
void ICACHE_FLASH_ATTR httpdStartResponse(HttpdConnData *conn, int code); void ICACHE_FLASH_ATTR httpdStartResponse(HttpdConnData *conn, int code);
void ICACHE_FLASH_ATTR httpdHeader(HttpdConnData *conn, const char *field, const char *val); void ICACHE_FLASH_ATTR httpdHeader(HttpdConnData *conn, const char *field, const char *val);
void ICACHE_FLASH_ATTR httpdEndHeaders(HttpdConnData *conn); void ICACHE_FLASH_ATTR httpdEndHeaders(HttpdConnData *conn);

@ -14,6 +14,11 @@ Connector to let httpd use the espfs filesystem to serve the files in it.
#include <esp8266.h> #include <esp8266.h>
#include "httpdespfs.h" #include "httpdespfs.h"
#include "espfs.h" #include "espfs.h"
#include "espfsformat.h"
// The static files marked with FLAG_GZIP are compressed and will be served with GZIP compression.
// If the client does not advertise that he accepts GZIP send following warning message (telnet users for e.g.)
static const char *gzipNonSupportedMessage = "HTTP/1.0 501 Not implemented\r\nServer: esp8266-httpd/"HTTPDVER"\r\nContent-Type: text/plain\r\nContent-Length: 52\r\n\r\nYour browser does not accept gzip-compressed data.\r\n";
//This is a catch-all cgi function. It takes the url passed to it, looks up the corresponding //This is a catch-all cgi function. It takes the url passed to it, looks up the corresponding
@ -23,9 +28,8 @@ int ICACHE_FLASH_ATTR cgiEspFsHook(HttpdConnData *connData) {
EspFsFile *file=connData->cgiData; EspFsFile *file=connData->cgiData;
int len; int len;
char buff[1024]; char buff[1024];
#ifdef GZIP_COMPRESSION char acceptEncodingBuffer[64];
const char *gzipSendResult = NULL; int isGzip;
#endif
if (connData->conn==NULL) { if (connData->conn==NULL) {
//Connection aborted. Clean up. //Connection aborted. Clean up.
@ -39,17 +43,32 @@ int ICACHE_FLASH_ATTR cgiEspFsHook(HttpdConnData *connData) {
if (file==NULL) { if (file==NULL) {
return HTTPD_CGI_NOTFOUND; return HTTPD_CGI_NOTFOUND;
} }
// The gzip checking code is intentionally without #ifdefs because checking
// for FLAG_GZIP (which indicates gzip compressed file) is very easy, doesn't
// mean additional overhead and is actually safer to be on at all times.
// If there are no gzipped files in the image, the code bellow will not cause any harm.
// Check if requested file was GZIP compressed
isGzip = espFsFlags(file) & FLAG_GZIP;
if (isGzip) {
// Check the browser's "Accept-Encoding" header. If the client does not
// advertise that he accepts GZIP send a warning message (telnet users for e.g.)
httpdGetHeader(connData, "Accept-Encoding", acceptEncodingBuffer, 64);
if (os_strstr(acceptEncodingBuffer, "gzip") == NULL) {
//No Accept-Encoding: gzip header present
httpdSend(connData, gzipNonSupportedMessage, -1);
espFsClose(file);
return HTTPD_CGI_DONE;
}
}
connData->cgiData=file; connData->cgiData=file;
httpdStartResponse(connData, 200); httpdStartResponse(connData, 200);
httpdHeader(connData, "Content-Type", httpdGetMimetype(connData->url)); httpdHeader(connData, "Content-Type", httpdGetMimetype(connData->url));
#ifdef GZIP_COMPRESSION if (isGzip) {
gzipSendResult = sendGZIPEncodingIfNeeded(connData); httpdHeader(connData, "Content-Encoding", "gzip");
if (gzipSendResult != NULL) {
httpdEndHeaders(connData);
httpdSend(connData, gzipSendResult, os_strlen(gzipSendResult));
return HTTPD_CGI_DONE;
} }
#endif
httpdHeader(connData, "Cache-Control", "max-age=3600, must-revalidate"); httpdHeader(connData, "Cache-Control", "max-age=3600, must-revalidate");
httpdEndHeaders(connData); httpdEndHeaders(connData);
return HTTPD_CGI_MORE; return HTTPD_CGI_MORE;
@ -101,6 +120,14 @@ int ICACHE_FLASH_ATTR cgiEspFsTemplate(HttpdConnData *connData) {
tpd->tplArg=NULL; tpd->tplArg=NULL;
tpd->tokenPos=-1; tpd->tokenPos=-1;
if (tpd->file==NULL) { if (tpd->file==NULL) {
espFsClose(tpd->file);
os_free(tpd);
return HTTPD_CGI_NOTFOUND;
}
if (espFsFlags(tpd->file) & FLAG_GZIP) {
os_printf("cgiEspFsTemplate: Trying to use gzip-compressed file %s as template!\n", connData->url);
espFsClose(tpd->file);
os_free(tpd);
return HTTPD_CGI_NOTFOUND; return HTTPD_CGI_NOTFOUND;
} }
connData->cgiData=tpd; connData->cgiData=tpd;

Loading…
Cancel
Save