Author Topic: GZIP compression support for webserver  (Read 6291 times)

Offline kingston

  • Newbie
  • *
  • Posts: 26
    • View Profile
GZIP compression support for webserver
« on: July 01, 2015, 12:18:49 PM »
Hello,

i am currently working on a project which has very limited storage in the SPI-flash but still needs a "fancy" website with a lot of javascript.
As javascript libraries are often over hundred kilobytes in size they waste space needed for other things.

I remembered reading about gzip compression for webservers but always thought the server would need to compress the files before sending them to the client.
After stumbling across another article it became clear to me that this is not the case and the only thing needed is an entry in the HTTP-header to inform the client that the data is compressed.

With this in mind I modified the HTTP-stack to fill in the needed information:

Code: [Select]
#if defined SUPPORT_GZIP_SERVING
static unsigned short fnAddContentTypeGZIP(CHAR *ptrBuffer, const CHAR *ptrType)
{
    CHAR *ptrContent = ptrBuffer;
    ptrContent = uStrcpy(ptrContent, "Content-Type: ");
    ptrContent = uStrcpy(ptrContent, ptrType);
    ptrContent = uStrcpy(ptrContent, "\r\n");
    ptrContent = uStrcpy(ptrContent, "Content-Encoding: gzip\r\n\r\n");

    return ((ptrContent - (CHAR *)ptrBuffer) - 2);
}
#endif

static unsigned short fnAddMimeContent(unsigned char *ptrBuffer, unsigned char ucMimeType)
{
    switch (ucMimeType) {
    #if defined MIME_TXT
    case MIME_TXT:
        return (fnAddContentType((CHAR *)ptrBuffer, "text/plain"));
    #endif
    #if defined MIME_CSS
    case MIME_CSS:
        return (fnAddContentType((CHAR *)ptrBuffer, "text/css"));
    #endif
    #if defined MIME_JAVA_SCRIPT
    case MIME_JAVA_SCRIPT:
        return (fnAddContentType((CHAR *)ptrBuffer, "application/javascript"));
    #endif
    #if defined MIME_PNG
    case MIME_PNG:
        return (fnAddContentType((CHAR *)ptrBuffer, "image/png"));
    #endif
    #if defined MIME_JPG
    case MIME_JPG:
        return (fnAddContentType((CHAR *)ptrBuffer, "image/jpg"));
    #endif
    #if defined MIME_BMP
    case MIME_BMP:
        return (fnAddContentType((CHAR *)ptrBuffer, "image/bmp"));
    #endif
    #if defined MIME_GIF
    case MIME_GIF:
        return (fnAddContentType((CHAR *)ptrBuffer, "image/gif"));
    #endif
#if defined SUPPORT_GZIP_SERVING
    case MIME_JAVA_GZIP:
          return (fnAddContentTypeGZIP((CHAR *)ptrBuffer, "application/javascript"));
        break;
    case MIME_CSS_GZIP:
          return (fnAddContentTypeGZIP((CHAR *)ptrBuffer, "text/css"));
        break;
    case MIME_HTML_GZIP:
          return (fnAddContentTypeGZIP((CHAR *)ptrBuffer, "text/html"));
        break;
#endif
    default:
        return 0;
    }
}
#endif

The function fnAddContentTypeGZIP adds the mime-type and an additional string "Content-Encoding: gzip" to the HTTP-header.

The new defines e.g. MIME_JAVA_GZIP have to be defined at the end of config.h for this to work. I added custom file extensions like jgz for compressed java and cgz for compressed css:

Code: [Select]
#if defined OPSYS_CONFIG                                                 // this is only set in the hardware module
    #if defined ETH_INTERFACE                                            // if we support Ethernet we define some constants for its (TCP/IP) use
        const unsigned char cucNullMACIP[MAC_LENGTH] = { 0, 0, 0, 0, 0, 0 };
        const unsigned char cucBroadcast[MAC_LENGTH] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; // used also for broadcast IP
    #endif

    #if defined SUPPORT_MIME_IDENTIFIER
    const CHAR *cMimeTable[] = {                                         // keep the ordering in the table to match the MIME type defines below!!
        (const CHAR *)"HTM",                                             // HTML file - will be interpreted by web server
        (const CHAR *)"JPG",                                             // JPG image
        (const CHAR *)"GIF",                                             // GIF image
        (const CHAR *)"CSS",                                             // CSS Cascading Style Sheets
        (const CHAR *)"JS",                                              // Java script
        (const CHAR *)"BIN",                                             // binary data file
        (const CHAR *)"TXT",                                             // text data file
(const CHAR *)"ICO",                                             // icon
        (const CHAR *)"BMP",                                             // BMP image
#ifdef  SUPPORT_GZIP_SERVING
(const CHAR *)"JGZ",                                             // gzipped javascript
(const CHAR *)"CGZ",                                             // gzipped css
(const CHAR *)"HGZ",                                             // gzipped html
#endif
        (const CHAR *)"???",                                             // all other types will be displayed as unknown
    };
    #endif
#endif

// File type identifiers
//
#define MIME_HTML                  0                                     // this and any lower types will be parsed by the web server
#define MIME_JPG                   1
#define MIME_GIF                   2
#define MIME_CSS                   3
#define MIME_JAVA_SCRIPT           4
#define MIME_BINARY                5
#define MIME_TXT                   6
#define MIME_ICON                  7
#define MIME_BMP                   8
#ifdef SUPPORT_GZIP_SERVING
#define MIME_JAVA_GZIP    9
#define MIME_CSS_GZIP    10
#define MIME_HTML_GZIP    11
#define UNKNOWN_MIME               12                                     // this entry is needed to terminate the list
#else
#define UNKNOWN_MIME               9                                     // this entry is needed to terminate the list
#endif

The new mime types get set when you write a file with one of the extensions to your storage.

I was able to get the filesize down to a fifth of the original so this really speeds up things. (179k -> 31k)

The only caveat I could find is that the original insert mechanisms with the pound-sign will not work when using a compressed HTML-file but as I am not using them anymore it doesn't really matter to me.

Maybe this will help others too when dealing with limited storage.

Kind Regards

Paul




Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3236
    • View Profile
    • uTasker
Re: GZIP compression support for webserver
« Reply #1 on: July 01, 2015, 12:49:34 PM »
Hello Paul

That is interesting. Simply storing a compressed file and telling the client (web browser) to decompress it not only saves space in the embedded storage medium but reduces the file size to send - making is doubly effective.

As you point out, the content will be sent without any parsing (since its content is compressed the web server can't look through it and modify/add content as it often does with HTML source) but the method seems perfect for javascript libraries and maybe large compressed images (if these can be sent as GZIPs).

Thanks!

Regards

Mark