From 1119c5a2105e50c95e5e679702aed80af310ba82 Mon Sep 17 00:00:00 2001 From: Art Cancro Date: Tue, 22 Feb 2005 05:15:28 +0000 Subject: [PATCH] * When the "begin_burst() / end_burst()" semantics are in use, perform gzip compression when the client indicates support for it. --- webcit/ChangeLog | 5 ++ webcit/calendar.c | 4 +- webcit/configure.in | 17 ++++ webcit/context_loop.c | 13 ++- webcit/webcit.c | 44 +++++----- webcit/webcit.h | 1 + webcit/webserver.c | 189 +++++++++++++++++++++++++++++++----------- 7 files changed, 200 insertions(+), 73 deletions(-) diff --git a/webcit/ChangeLog b/webcit/ChangeLog index 5db06e7ab..84fa12225 100644 --- a/webcit/ChangeLog +++ b/webcit/ChangeLog @@ -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 1998-12-03 Nathan Bryant * webserver.c: warning fix + diff --git a/webcit/calendar.c b/webcit/calendar.c index f7e48d8bf..bb8afc103 100644 --- a/webcit/calendar.c +++ b/webcit/calendar.c @@ -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; } diff --git a/webcit/configure.in b/webcit/configure.in index 42955013b..b8c6dd3fa 100644 --- a/webcit/configure.in +++ b/webcit/configure.in @@ -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, diff --git a/webcit/context_loop.c b/webcit/context_loop.c index 12a641e1a..fdd97a819 100644 --- a/webcit/context_loop.c +++ b/webcit/context_loop.c @@ -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 */ diff --git a/webcit/webcit.c b/webcit/webcit.c index fb5fb8b3c..90777a928 100644 --- a/webcit/webcit.c +++ b/webcit/webcit.c @@ -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("\n"); wprintf("you really want to be here 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]); } diff --git a/webcit/webcit.h b/webcit/webcit.h index 43dc8f6b7..aaf670b8f 100644 --- a/webcit/webcit.h +++ b/webcit/webcit.h @@ -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,'|') diff --git a/webcit/webserver.c b/webcit/webserver.c index 2cdb0bbdf..ef9733141 100644 --- a/webcit/webserver.c +++ b/webcit/webserver.c @@ -43,6 +43,10 @@ #include "webcit.h" #include "webserver.h" +#ifdef HAVE_ZLIB +#include +#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 -- 2.39.2