* When the "begin_burst() / end_burst()" semantics are in use, perform
authorArt Cancro <ajc@citadel.org>
Tue, 22 Feb 2005 05:15:28 +0000 (05:15 +0000)
committerArt Cancro <ajc@citadel.org>
Tue, 22 Feb 2005 05:15:28 +0000 (05:15 +0000)
  gzip compression when the client indicates support for it.

webcit/ChangeLog
webcit/calendar.c
webcit/configure.in
webcit/context_loop.c
webcit/webcit.c
webcit/webcit.h
webcit/webserver.c

index 5db06e7abc57b942852e1de8d31a4cd19be94748..84fa12225d926abae6e28b1e16ed74b636f5d259 100644 (file)
@@ -1,4 +1,8 @@
 $Log$
+Revision 603.2  2005/02/22 05:15:28  ajc
+* When the "begin_burst() / end_burst()" semantics are in use, perform
+  gzip compression when the client indicates support for it.
+
 Revision 603.1  2005/02/21 23:00:04  ajc
 * begin_burst() / end_burst() semantics now apply to all pages which are
   output using the "include_html_head" option of output_headers() (which
@@ -2433,3 +2437,4 @@ Sun Dec  6 19:50:55 EST 1998 Art Cancro <ajc@uncnsrd.mt-kisco.ny.us>
 
 1998-12-03 Nathan Bryant <bryant@cs.usm.maine.edu>
        * webserver.c: warning fix
+
index f7e48d8bfca4b35737a9d077f6a6a6c8391e1567..bb8afc103d34683e854ec7224d81ba74890915e6 100644 (file)
@@ -931,8 +931,8 @@ void do_freebusy(char *req) {
        if (buf[0] != '1') {
                wprintf("HTTP/1.0 404 %s\n", &buf[4]);
                output_headers(0, 0, 0, 0, 0, 0, 0);
-               wprintf("Content-Type: text/plain\n");
-               wprintf("\n");
+               wprintf("Content-Type: text/plain\r\n");
+               wprintf("\r\n");
                wprintf("%s\n", &buf[4]);
                return;
        }
index 42955013bb8bca8e7d537c7d7e1e5f1b1b2511e4..b8c6dd3fa743fdcc4f179202a1900c16aa2e5d8a 100644 (file)
@@ -7,6 +7,7 @@ AC_PROG_INSTALL
 AC_PREFIX_DEFAULT(/usr/local/webcit)
 
 AC_ARG_WITH(with_libical, [  --with-libical          use libical calendaring library])
+AC_ARG_WITH(with_zlib, [  --with-zlib          use zlib compression if present])
 AC_ARG_WITH(with_newt, [  --with-newt          use newt window library])
 AC_ARG_WITH(ssl,
        [  --with-ssl=PATH     Specify path to OpenSSL installation ],
@@ -118,6 +119,22 @@ main() {
        )
 fi
 
+
+
+dnl Checks for the zlib compression library.
+if test "x$with_zlib" != xno ; then
+        AC_CHECK_HEADERS(zlib.h,
+                [AC_CHECK_LIB(z, zlibVersion,
+                        [ok_zlib=yes],,
+        )])
+fi
+
+if test "x$ok_zlib" = xyes ; then
+        LIBS="-lz $LIBS"
+        AC_DEFINE(HAVE_ZLIB)
+fi
+
+
 dnl Checks for the newt window library.
 if test "x$with_newt" != xno ; then
        AC_CHECK_HEADERS(newt.h,
index 12a641e1aa8a8768c08ee14ea4fd1decbb4bed28..fdd97a819a10c60c240c82b0b4ebf07e598e9700 100644 (file)
@@ -113,7 +113,7 @@ void do_housekeeping(void)
                pthread_mutex_unlock(&sessions_to_kill->SessionMutex);
                sptr = sessions_to_kill->next;
                free(sessions_to_kill);
-               sessions_to_kill = sessions_to_kill->next;
+               sessions_to_kill = sptr;
                --num_sessions;
        }
 
@@ -267,6 +267,7 @@ void context_loop(int sock)
        char buf[SIZ], hold[SIZ];
        int desired_session = 0;
        int got_cookie = 0;
+       int gzip_ok = 0;
        struct wcsession *TheSession, *sptr;
        char httpauth_string[SIZ];
        char httpauth_user[SIZ];
@@ -283,6 +284,15 @@ void context_loop(int sock)
        do {
                if (req_gets(sock, buf, hold) < 0) return;
 
+               /*
+                * Can we compress?
+                */
+               if (!strncasecmp(buf, "Accept-encoding:", 16)) {
+                       if (strstr(&buf[16], "gzip")) {
+                               gzip_ok = 1;
+                       }
+               }
+
                /*
                 * Browser-based sessions use cookies for session authentication
                 */
@@ -416,6 +426,7 @@ void context_loop(int sock)
        pthread_setspecific(MyConKey, (void *)TheSession);
        TheSession->http_sock = sock;
        TheSession->lastreq = time(NULL);                       /* log */
+       TheSession->gzip_ok = gzip_ok;
        session_loop(req);                              /* do transaction */
        pthread_mutex_unlock(&TheSession->SessionMutex);        /* unbind */
 
index fb5fb8b3cf1712c5fb99f0d04e2fee22c614a3a7..90777a928bff14375d6b814441bfd6afe2bce071 100644 (file)
@@ -363,13 +363,13 @@ void output_headers(      int do_httpheaders,     /* 1 = output HTTP headers
        httpdate(httpnow, time(NULL));
 
        if (do_httpheaders) {
-               wprintf("Content-type: text/html\n"
+               wprintf("Content-type: text/html\r\n"
                        "Server: %s / %s\n", SERVER, serv_info.serv_software
                );
                if (!cache)
-                       wprintf("Connection: close\n"
-                               "Pragma: no-cache\n"
-                               "Cache-Control: no-store\n"
+                       wprintf("Connection: close\r\n"
+                               "Pragma: no-cache\r\n"
+                               "Cache-Control: no-store\r\n"
                        );
        }
 
@@ -377,9 +377,9 @@ void output_headers(        int do_httpheaders,     /* 1 = output HTTP headers
                        WC->wc_password, WC->wc_roomname);
 
        if (unset_cookies) {
-               wprintf("Set-cookie: webcit=%s; path=/\n", unset);
+               wprintf("Set-cookie: webcit=%s; path=/\r\n", unset);
        } else {
-               wprintf("Set-cookie: webcit=%s; path=/\n", cookie);
+               wprintf("Set-cookie: webcit=%s; path=/\r\n", cookie);
                if (server_cookie != NULL) {
                        wprintf("%s\n", server_cookie);
                }
@@ -441,9 +441,9 @@ void output_headers(        int do_httpheaders,     /* 1 = output HTTP headers
  */
 void http_redirect(char *whichpage) {
        wprintf("HTTP/1.0 302 Moved Temporarily\n");
-       wprintf("Location: %s\n", whichpage);
-       wprintf("URI: %s\n", whichpage);
-       wprintf("Content-type: text/html\n\n");
+       wprintf("Location: %s\r\n", whichpage);
+       wprintf("URI: %s\r\n", whichpage);
+       wprintf("Content-type: text/html\r\n\r\n");
        wprintf("<html><body>\n");
        wprintf("you really want to be <A HREF=\"%s\">here</A> now\n",
                whichpage);
@@ -456,11 +456,9 @@ void check_for_instant_messages()
 {
        char buf[SIZ];
 
-       lprintf(9, "Checking for instant messages...\n");
        serv_puts("NOOP");
        serv_gets(buf);
        if (buf[3] == '*') WC->HaveInstantMessages = 1;
-       lprintf(9, "...done\n");
 }
 
 
@@ -477,11 +475,11 @@ void http_transmit_thing(char *thing, size_t length, char *content_type,
        else {
                output_headers(0, 0, 0, 0, 0, 0, 0);
        }
-       wprintf("Content-type: %s\n"
-               "Content-length: %ld\n"
-               "Server: %s\n"
-               "Connection: close\n"
-               "\n",
+       wprintf("Content-type: %s\r\n"
+               "Content-length: %ld\r\n"
+               "Server: %s\r\n"
+               "Connection: close\r\n"
+               "\r\n",
                content_type,
                (long) length,
                SERVER
@@ -505,8 +503,8 @@ void output_static(char *what)
        fp = fopen(buf, "rb");
        if (fp == NULL) {
                wprintf("HTTP/1.0 404 %s\n", strerror(errno));
-               wprintf("Content-Type: text/plain\n");
-               wprintf("\n");
+               wprintf("Content-Type: text/plain\r\n");
+               wprintf("\r\n");
                wprintf("Cannot open %s: %s\n", what, strerror(errno));
        } else {
                if (!strncasecmp(&what[strlen(what) - 4], ".gif", 4))
@@ -542,7 +540,7 @@ void output_static(char *what)
 
                fstat(fileno(fp), &statbuf);
                bytes = statbuf.st_size;
-               /* lprintf(3, "Static: %s, (%s; %ld bytes)\n",
+               /* lprintf(3, "Static: %s, (%s; %ld bytes)\r\n",
                        what, content_type, bytes); */
                bigbuffer = malloc(bytes + 2);
                fread(bigbuffer, bytes, 1, fp);
@@ -592,8 +590,8 @@ void output_image()
                /*
                wprintf("HTTP/1.0 404 %s\n", &buf[4]);
                output_headers(0, 0, 0, 0, 0, 0, 0);
-               wprintf("Content-Type: text/plain\n"
-                       "\n"
+               wprintf("Content-Type: text/plain\r\n"
+                       "\r\n"
                        "Error retrieving image: %s\n",
                        &buf[4]
                );
@@ -629,8 +627,8 @@ void output_mimepart()
        } else {
                wprintf("HTTP/1.0 404 %s\n", &buf[4]);
                output_headers(0, 0, 0, 0, 0, 0, 0);
-               wprintf("Content-Type: text/plain\n");
-               wprintf("\n");
+               wprintf("Content-Type: text/plain\r\n");
+               wprintf("\r\n");
                wprintf("Error retrieving part: %s\n", &buf[4]);
        }
 
index 43dc8f6b708707824c829081389e019cbaa73fbd..aaf670b8fe09b34487018ad131fd8b0852b44c2b 100644 (file)
@@ -233,6 +233,7 @@ struct wcsession {
 
        size_t burst_len;
        char *burst;
+       int gzip_ok;                    /* Nonzero if Accept-encoding: gzip */
 };
 
 #define extract(dest,source,parmnum)   extract_token(dest,source,parmnum,'|')
index 2cdb0bbdfbce15344ed6350a4db70d88c1f06135..ef9733141e353d134f92d194f2c69dee5056965e 100644 (file)
 #include "webcit.h"
 #include "webserver.h"
 
+#ifdef HAVE_ZLIB
+#include <zlib.h>
+#endif
+
 #ifndef HAVE_SNPRINTF
 int vsnprintf(char *buf, size_t max, const char *fmt, va_list argp);
 #endif
@@ -75,8 +79,7 @@ int ig_tcp_server(char *ip_addr, int port_number, int queue_len)
        sin.sin_family = AF_INET;
        if (ip_addr == NULL) {
                sin.sin_addr.s_addr = INADDR_ANY;
-       }
-       else {
+       } else {
                sin.sin_addr.s_addr = inet_addr(ip_addr);
        }
 
@@ -92,8 +95,7 @@ int ig_tcp_server(char *ip_addr, int port_number, int queue_len)
 
        s = socket(PF_INET, SOCK_STREAM, (getprotobyname("tcp")->p_proto));
        if (s < 0) {
-               lprintf(1, "Can't create a socket: %s\n",
-                      strerror(errno));
+               lprintf(1, "Can't create a socket: %s\n", strerror(errno));
                exit(errno);
        }
        /* Set some socket options that make sense. */
@@ -129,7 +131,7 @@ int client_read_to(int sock, char *buf, int bytes, int timeout)
 
 #ifdef HAVE_OPENSSL
        if (is_https) {
-               return(client_read_ssl(buf, bytes, timeout));
+               return (client_read_ssl(buf, bytes, timeout));
        }
 #endif
 
@@ -140,8 +142,7 @@ int client_read_to(int sock, char *buf, int bytes, int timeout)
                tv.tv_sec = timeout;
                tv.tv_usec = 0;
 
-               retval = select((sock) + 1,
-                               &rfds, NULL, NULL, &tv);
+               retval = select((sock) + 1, &rfds, NULL, NULL, &tv);
                if (FD_ISSET(sock, &rfds) == 0) {
                        return (0);
                }
@@ -150,8 +151,8 @@ int client_read_to(int sock, char *buf, int bytes, int timeout)
 
                if (rlen < 1) {
                        lprintf(2, "client_read() failed: %s\n",
-                              strerror(errno));
-                       return(-1);
+                               strerror(errno));
+                       return (-1);
                }
                len = len + rlen;
        }
@@ -165,19 +166,20 @@ int client_read_to(int sock, char *buf, int bytes, int timeout)
 }
 
 
-ssize_t client_write(const void *buf, size_t count) {
+ssize_t client_write(const void *buf, size_t count)
+{
 
        if (WC->burst != NULL) {
-               WC->burst = realloc(WC->burst, (WC->burst_len + count + 2));
+               WC->burst =
+                   realloc(WC->burst, (WC->burst_len + count + 2));
                memcpy(&WC->burst[WC->burst_len], buf, count);
                WC->burst_len += count;
-               return(count);
+               return (count);
        }
-
 #ifdef HAVE_OPENSSL
        if (is_https) {
-               client_write_ssl((char *)buf, count);
-               return(count);
+               client_write_ssl((char *) buf, count);
+               return (count);
        }
 #endif
 #ifdef HTTP_TRACING
@@ -185,11 +187,12 @@ ssize_t client_write(const void *buf, size_t count) {
        write(2, buf, count);
        write(2, "\033[30m", 5);
 #endif
-       return(write(WC->http_sock, buf, count));
+       return (write(WC->http_sock, buf, count));
 }
 
 
-void begin_burst(void) {
+void begin_burst(void)
+{
        if (WC->burst != NULL) {
                free(WC->burst);
                WC->burst = NULL;
@@ -198,11 +201,75 @@ void begin_burst(void) {
        WC->burst = malloc(SIZ);
 }
 
-void end_burst(void) {
+
+/*
+ * compress_gzip() uses the same calling syntax as compress2(), but it
+ * creates a stream compatible with HTTP "Content-encoding: gzip"
+ */
+#ifdef HAVE_ZLIB
+#define DEF_MEM_LEVEL 8
+#define OS_CODE 0x03   /* unix */
+int ZEXPORT compress_gzip(Bytef * dest, uLongf * destLen,
+                         const Bytef * source, uLong sourceLen, int level)
+{
+       const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
+
+       /* write gzip header */
+       sprintf((char *) dest, "%c%c%c%c%c%c%c%c%c%c",
+               gz_magic[0], gz_magic[1], Z_DEFLATED,
+               0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /*xflags */ ,
+               OS_CODE);
+
+       /* normal deflate */
+       z_stream stream;
+       int err;
+       stream.next_in = (Bytef *) source;
+       stream.avail_in = (uInt) sourceLen;
+       stream.next_out = dest + 10L;   // after header
+       stream.avail_out = (uInt) * destLen;
+       if ((uLong) stream.avail_out != *destLen)
+               return Z_BUF_ERROR;
+
+       stream.zalloc = (alloc_func) 0;
+       stream.zfree = (free_func) 0;
+       stream.opaque = (voidpf) 0;
+
+       err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
+                          DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
+       if (err != Z_OK)
+               return err;
+
+       err = deflate(&stream, Z_FINISH);
+       if (err != Z_STREAM_END) {
+               deflateEnd(&stream);
+               return err == Z_OK ? Z_BUF_ERROR : err;
+       }
+       *destLen = stream.total_out + 10L;
+
+       /* write CRC and Length */
+       uLong crc = crc32(0L, source, sourceLen);
+       int n;
+       for (n = 0; n < 4; ++n, ++*destLen) {
+               dest[*destLen] = (int) (crc & 0xff);
+               crc >>= 8;
+       }
+       uLong len = stream.total_in;
+       for (n = 0; n < 4; ++n, ++*destLen) {
+               dest[*destLen] = (int) (len & 0xff);
+               len >>= 8;
+       }
+       err = deflateEnd(&stream);
+       return err;
+}
+#endif
+
+void end_burst(void)
+{
        size_t the_len;
        char *the_data;
 
-       if (WC->burst == NULL) return;
+       if (WC->burst == NULL)
+               return;
 
        the_len = WC->burst_len;
        the_data = WC->burst;
@@ -210,9 +277,33 @@ void end_burst(void) {
        WC->burst_len = 0;
        WC->burst = NULL;
 
+#ifdef HAVE_ZLIB
+       /* Handle gzip compression */
+       if (WC->gzip_ok) {
+               char *compressed_data = NULL;
+               uLongf compressed_len;
+
+               compressed_len = (uLongf) ((the_len * 101) / 100) + 100;
+               compressed_data = malloc(compressed_len);
+
+               if (compress_gzip((Bytef *) compressed_data,
+                                 &compressed_len,
+                                 (Bytef *) the_data,
+                                 (uLongf) the_len, 9) == Z_OK) {
+                       wprintf("Content-encoding: gzip\r\n");
+                       free(the_data);
+                       the_data = compressed_data;
+                       the_len = compressed_len;
+               } else {
+                       free(compressed_data);
+               }
+       }
+#endif                         /* HAVE_ZLIB */
+
        wprintf("Content-length: %d\r\n\r\n", the_len);
        client_write(the_data, the_len);
        free(the_data);
+       return;
 }
 
 
@@ -279,7 +370,8 @@ void start_daemon(int do_close_stdio)
                exit(0);
 }
 
-void spawn_another_worker_thread() {
+void spawn_another_worker_thread()
+{
        pthread_t SessThread;   /* Thread descriptor */
        pthread_attr_t attr;    /* Thread attributes */
        int ret;
@@ -295,16 +387,16 @@ void spawn_another_worker_thread() {
         * 64-bit Linux.
         */
        if ((ret = pthread_attr_setstacksize(&attr, 1024 * 1024))) {
-               lprintf(1, "pthread_attr_setstacksize: %s\n", strerror(ret));
+               lprintf(1, "pthread_attr_setstacksize: %s\n",
+                       strerror(ret));
                pthread_attr_destroy(&attr);
        }
 
        /* now create the thread */
        if (pthread_create(&SessThread, &attr,
-                       (void *(*)(void *)) worker_entry, NULL)
-                  != 0) {
-               lprintf(1, "Can't create thread: %s\n",
-                       strerror(errno));
+                          (void *(*)(void *)) worker_entry, NULL)
+           != 0) {
+               lprintf(1, "Can't create thread: %s\n", strerror(errno));
        }
 
        /* free up the attributes */
@@ -348,10 +440,11 @@ int main(int argc, char **argv)
                case 'c':
                        server_cookie = malloc(SIZ);
                        if (server_cookie != NULL) {
-                               strcpy(server_cookie, "Set-cookie: wcserver=");
-                               if (gethostname(
-                                  &server_cookie[strlen(server_cookie)],
-                                  200) != 0) {
+                               strcpy(server_cookie,
+                                      "Set-cookie: wcserver=");
+                               if (gethostname
+                                   (&server_cookie[strlen(server_cookie)],
+                                    200) != 0) {
                                        lprintf(2, "gethostname: %s\n",
                                                strerror(errno));
                                        free(server_cookie);
@@ -379,21 +472,21 @@ int main(int argc, char **argv)
        }
        /* Tell 'em who's in da house */
        lprintf(1, SERVER "\n"
-"Copyright (C) 1996-2005 by the Citadel/UX development team.\n"
-"This software is distributed under the terms of the GNU General Public\n"
-"License.  If you paid for this software, someone is ripping you off.\n\n");
+               "Copyright (C) 1996-2005 by the Citadel/UX development team.\n"
+               "This software is distributed under the terms of the GNU General Public\n"
+               "License.  If you paid for this software, someone is ripping you off.\n\n");
 
        if (chdir(WEBCITDIR) != 0)
                perror("chdir");
 
-        /*
-         * Set up a place to put thread-specific data.
-         * We only need a single pointer per thread - it points to the
-         * wcsession struct to which the thread is currently bound.
-         */
-        if (pthread_key_create(&MyConKey, NULL) != 0) {
-                lprintf(1, "Can't create TSD key: %s\n", strerror(errno));
-        }
+       /*
+        * Set up a place to put thread-specific data.
+        * We only need a single pointer per thread - it points to the
+        * wcsession struct to which the thread is currently bound.
+        */
+       if (pthread_key_create(&MyConKey, NULL) != 0) {
+               lprintf(1, "Can't create TSD key: %s\n", strerror(errno));
+       }
 
        /*
         * Set up a place to put thread-specific SSL data.
@@ -402,9 +495,9 @@ int main(int argc, char **argv)
         * transactions.
         */
 #ifdef HAVE_OPENSSL
-        if (pthread_key_create(&ThreadSSL, NULL) != 0) {
-                lprintf(1, "Can't create TSD key: %s\n", strerror(errno));
-        }
+       if (pthread_key_create(&ThreadSSL, NULL) != 0) {
+               lprintf(1, "Can't create TSD key: %s\n", strerror(errno));
+       }
 #endif
 
        /*
@@ -438,7 +531,7 @@ int main(int argc, char **argv)
 #endif
 
        /* Start a few initial worker threads */
-       for (i=0; i<(MIN_WORKER_THREADS); ++i) {
+       for (i = 0; i < (MIN_WORKER_THREADS); ++i) {
                spawn_another_worker_thread();
        }
 
@@ -451,7 +544,8 @@ int main(int argc, char **argv)
 /*
  * Entry point for worker threads
  */
-void worker_entry(void) {
+void worker_entry(void)
+{
        int ssock;
        int i = 0;
        int time_to_die = 0;
@@ -462,12 +556,13 @@ void worker_entry(void) {
                fail_this_transaction = 0;
                ssock = accept(msock, NULL, 0);
                if (ssock < 0) {
-                       lprintf(2, "accept() failed: %s\n", strerror(errno));
+                       lprintf(2, "accept() failed: %s\n",
+                               strerror(errno));
                } else {
                        /* Set the SO_REUSEADDR socket option */
                        i = 1;
                        setsockopt(ssock, SOL_SOCKET, SO_REUSEADDR,
-                               &i, sizeof(i));
+                                  &i, sizeof(i));
 
                        /* If we are an HTTPS server, go crypto now. */
 #ifdef HAVE_OPENSSL