diff --git a/Makefile b/Makefile index 87d0c1f..bc57738 100644 --- a/Makefile +++ b/Makefile @@ -38,6 +38,20 @@ EXTRA_INCDIR = include \ # libraries used in this project, mainly provided by the SDK LIBS = c gcc hal phy pp net80211 wpa main lwip +# If GZIP_COMPRESSION is set to "yes" then the static css, js, and html files will be compressed with gzip before added to the espfs image +# and will be served with gzip Content-Encoding header. +# This could speed up the downloading of these files, but might break compatibility with older web browsers not supporting gzip encoding +# because Accept-Encoding is simply ignored. Enable this option if you have large static files to serve (for e.g. JQuery, Twitter bootstrap) +# By default only js, css and html files are compressed. +# If you have text based static files with different extensions what you want to serve compressed then you will need to add the extension to the following places: +# - Add the extension to this Makefile at the webpages.espfs target to the find command +# - Add the extension to the gzippedFileTypes array in the user/httpd.c file +# +# Adding JPG or PNG files (and any other compressed formats) is not recommended, because GZIP compression does not works effectively on compressed files. + +#Static gzipping is disabled by default. +#GZIP_COMPRESSION = "yes" + # compiler flags using during compilation of source files CFLAGS = -Os -ggdb -std=c99 -Werror -Wpointer-arith -Wundef -Wall -Wl,-EL -fno-inline-functions \ -nostdlib -mlongcalls -mtext-section-literals -D__ets__ -DICACHE_FLASH \ @@ -101,6 +115,10 @@ Q := @ vecho := @echo endif +ifeq ($(GZIP_COMPRESSION),"yes") +CFLAGS += -DGZIP_COMPRESSION +endif + vpath %.c $(SRC_DIR) define compile-objects @@ -144,8 +162,15 @@ flash: $(FW_FILE_1) $(FW_FILE_2) $(Q) $(ESPTOOL) -cp $(ESPPORT) -cb $(ESPBAUD) -ca 0x40000 -cf firmware/0x40000.bin -v webpages.espfs: html/ html/wifi/ mkespfsimage/mkespfsimage - cd html; find . | ../mkespfsimage/mkespfsimage > ../webpages.espfs; cd .. - +ifeq ($(GZIP_COMPRESSION),"yes") + $(Q) rm -rf html_compressed; + $(Q) cp -r html html_compressed; + $(Q) cd html_compressed; find . -type f -regex ".*/.*\.\(html\|css\|js\)" -exec sh -c "gzip -n {}; mv {}.gz {}" \;; cd ..; + $(Q) cd html_compressed; find | ../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 + $(Q) cd html; find | ../mkespfsimage/mkespfsimage > ../webpages.espfs; cd .. +endif mkespfsimage/mkespfsimage: mkespfsimage/ make -C mkespfsimage diff --git a/user/httpd.c b/user/httpd.c index 557f67b..46ccce5 100644 --- a/user/httpd.c +++ b/user/httpd.c @@ -75,6 +75,19 @@ static const MimeMap mimeTypes[]={ {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[] = "Your browser does not accept gzip-compressed data."; +#endif + //Returns a static char* to a mime type for a given url to a file. const char ICACHE_FLASH_ATTR *httpdGetMimetype(char *url) { int i=0; @@ -88,6 +101,39 @@ const char ICACHE_FLASH_ATTR *httpdGetMimetype(char *url) { 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 static HttpdConnData ICACHE_FLASH_ATTR *httpdFindConnData(void *arg) { int i; diff --git a/user/httpd.h b/user/httpd.h index ce6c070..fb4778b 100644 --- a/user/httpd.h +++ b/user/httpd.h @@ -14,6 +14,7 @@ #define HTTPD_METHOD_GET 1 #define HTTPD_METHOD_POST 2 + typedef struct HttpdPriv HttpdPriv; typedef struct HttpdConnData HttpdConnData; typedef struct HttpdPostData HttpdPostData; @@ -58,6 +59,9 @@ int httpdUrlDecode(char *val, int valLen, char *ret, int retLen); int ICACHE_FLASH_ATTR httpdFindArg(char *line, char *arg, char *buff, int buffLen); void ICACHE_FLASH_ATTR httpdInit(HttpdBuiltInUrl *fixedUrls, int port); 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 httpdHeader(HttpdConnData *conn, const char *field, const char *val); void ICACHE_FLASH_ATTR httpdEndHeaders(HttpdConnData *conn); diff --git a/user/httpdespfs.c b/user/httpdespfs.c index d2e67ac..3b6f002 100644 --- a/user/httpdespfs.c +++ b/user/httpdespfs.c @@ -31,6 +31,9 @@ int ICACHE_FLASH_ATTR cgiEspFsHook(HttpdConnData *connData) { EspFsFile *file=connData->cgiData; int len; char buff[1024]; +#ifdef GZIP_COMPRESSION + const char *gzipSendResult = NULL; +#endif if (connData->conn==NULL) { //Connection aborted. Clean up. @@ -47,6 +50,14 @@ int ICACHE_FLASH_ATTR cgiEspFsHook(HttpdConnData *connData) { connData->cgiData=file; httpdStartResponse(connData, 200); httpdHeader(connData, "Content-Type", httpdGetMimetype(connData->url)); +#ifdef GZIP_COMPRESSION + gzipSendResult = sendGZIPEncodingIfNeeded(connData); + 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"); httpdEndHeaders(connData); return HTTPD_CGI_MORE;