Add support for (re-)compression of buffered content and CGI responses.
authorFabian Keil <fk@fabiankeil.de>
Thu, 23 Jun 2011 14:01:01 +0000 (14:01 +0000)
committerFabian Keil <fk@fabiankeil.de>
Thu, 23 Jun 2011 14:01:01 +0000 (14:01 +0000)
For now it has to be explicitly enabled with the configure option --enable-compression.

cgi.c
cgi.h
configure.in
jcc.c
parsers.c
project.h

diff --git a/cgi.c b/cgi.c
index 024a6e4..0583f88 100644 (file)
--- a/cgi.c
+++ b/cgi.c
@@ -1,4 +1,4 @@
-const char cgi_rcs[] = "$Id: cgi.c,v 1.129 2010/05/24 11:38:22 fabiankeil Exp $";
+const char cgi_rcs[] = "$Id: cgi.c,v 1.130 2011/04/19 13:00:47 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/cgi.c,v $
@@ -49,6 +49,10 @@ const char cgi_rcs[] = "$Id: cgi.c,v 1.129 2010/05/24 11:38:22 fabiankeil Exp $"
 #include <limits.h>
 #include <assert.h>
 
+#ifdef FEATURE_COMPRESSION
+#include <zlib.h>
+#endif
+
 #include "project.h"
 #include "cgi.h"
 #include "list.h"
@@ -1479,6 +1483,59 @@ static void get_locale_time(char *buf, size_t buffer_size)
 
 }
 
+
+#ifdef FEATURE_COMPRESSION
+/*********************************************************************
+ *
+ * Function    :  compress_buffer
+ *
+ * Description :  Compresses the content of a buffer with zlib's deflate
+ *                Allocates a new buffer for the result, free'ing it is
+ *                up to the caller.
+ *
+ *                XXX: We should add a config option for the
+ *                     compression level.
+ *
+ *
+ * Parameters  :
+ *          1  :  buffer = buffer whose content should be compressed
+ *          2  :  buffer_length = length of the buffer
+ *
+ * Returns     :  NULL on error, otherwise a pointer to the compressed
+ *                content of the input buffer.
+ *
+ *********************************************************************/
+char *compress_buffer(char *buffer, size_t *buffer_length)
+{
+   char *compressed_buffer;
+   size_t new_length = *buffer_length;
+
+   compressed_buffer = malloc(new_length);
+   if (NULL == compressed_buffer)
+   {
+      log_error(LOG_LEVEL_FATAL,
+         "Out of memory allocation compression buffer.");
+   }
+
+   if (Z_OK != compress2((Bytef *)compressed_buffer, &new_length,
+         (Bytef *)buffer, *buffer_length, Z_DEFAULT_COMPRESSION))
+   {
+      log_error(LOG_LEVEL_ERROR, "Error in compress2()");
+      freez(compressed_buffer);
+      return NULL;
+   }
+
+   log_error(LOG_LEVEL_RE_FILTER,
+      "Compressed content from %d to %d bytes.", *buffer_length, new_length);
+
+   *buffer_length = new_length;
+
+   return compressed_buffer;
+
+}
+#endif
+
+
 /*********************************************************************
  *
  * Function    :  finish_http_response
@@ -1524,6 +1581,23 @@ struct http_response *finish_http_response(const struct client_state *csp, struc
    {
       rsp->content_length = rsp->body ? strlen(rsp->body) : 0;
    }
+
+#ifdef FEATURE_COMPRESSION
+   if (!err && (csp->flags & CSP_FLAG_CLIENT_SUPPORTS_DEFLATE)
+      && (rsp->content_length > LOWER_LENGTH_LIMIT_FOR_COMRPESSION))
+   {
+      char *compressed_content;
+
+      compressed_content = compress_buffer(rsp->body, &rsp->content_length);
+      if (NULL != compressed_content)
+      {
+         freez(rsp->body);
+         rsp->body = compressed_content;
+      }
+      err = enlist_unique_header(rsp->headers, "Content-Encoding", "deflate");
+   }
+#endif
+
    if (!err)
    {
       snprintf(buf, sizeof(buf), "Content-Length: %d", (int)rsp->content_length);
diff --git a/cgi.h b/cgi.h
index 1989386..4cd21fc 100644 (file)
--- a/cgi.h
+++ b/cgi.h
@@ -1,6 +1,6 @@
 #ifndef CGI_H_INCLUDED
 #define CGI_H_INCLUDED
-#define CGI_H_VERSION "$Id: cgi.h,v 1.36 2009/05/16 13:27:20 fabiankeil Exp $"
+#define CGI_H_VERSION "$Id: cgi.h,v 1.37 2009/06/11 11:44:25 fabiankeil Exp $"
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/cgi.h,v $
@@ -101,6 +101,9 @@ extern jb_err get_string_param(const struct map *parameters,
                                const char **pparam);
 extern char   get_char_param(const struct map *parameters,
                              const char *param_name);
+#ifdef FEATURE_COMPRESSION
+extern char *compress_buffer(char *buffer, size_t *buffer_length);
+#endif
 
 /*
  * Text generators
index b55b8ec..583ede8 100644 (file)
@@ -1,6 +1,6 @@
 dnl Process this file with autoconf to produce a configure script.
 dnl 
-dnl $Id: configure.in,v 1.156 2011/05/27 11:37:45 fabiankeil Exp $
+dnl $Id: configure.in,v 1.157 2011/05/27 11:37:57 fabiankeil Exp $
 dnl 
 dnl Written by and Copyright (C) 2001-2010 the
 dnl Privoxy team. http://www.privoxy.org/
@@ -32,7 +32,7 @@ dnl =================================================================
 dnl AutoConf Initialization
 dnl =================================================================
 
-AC_REVISION($Revision: 1.156 $)
+AC_REVISION($Revision: 1.157 $)
 AC_INIT(jcc.c)
 
 if test ! -f config.h.in; then
@@ -935,6 +935,19 @@ if test $enableval2 = yes; then
   fi  
 fi
 
+AC_ARG_ENABLE(compression,
+[  --enable-compression            Allow Privoxy to compress buffered content if the client supports it. Requires zlib support.],
+[enableval2=$enableval],
+[enableval2=no])
+if test $enableval2 = yes; then
+  if test $have_zlib = "yes"; then
+    echo Enabling compression support.
+    AC_DEFINE(FEATURE_COMPRESSION,1,[Define to 1 to use compression through the zlib library.])
+  else
+   AC_MSG_WARN([No zlib found. Privoxy will not be able to (re-)compressed buffered content.])
+  fi
+fi
+
 
 # If we have libpcre and either we also have pcreposix or
 # we don't need pcreposix, then link pcre dynamically; else
diff --git a/jcc.c b/jcc.c
index f8f281f..56e07d8 100644 (file)
--- a/jcc.c
+++ b/jcc.c
@@ -1,4 +1,4 @@
-const char jcc_rcs[] = "$Id: jcc.c,v 1.351 2011/05/03 10:15:54 fabiankeil Exp $";
+const char jcc_rcs[] = "$Id: jcc.c,v 1.352 2011/05/27 11:34:39 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/jcc.c,v $
@@ -2061,6 +2061,19 @@ static void chat(struct client_state *csp)
                   {
                      csp->content_length = (size_t)(csp->iob->eod - csp->iob->cur);
                   }
+#ifdef FEATURE_COMPRESSION
+                  else if ((csp->flags & CSP_FLAG_CLIENT_SUPPORTS_DEFLATE)
+                     && (csp->content_length > LOWER_LENGTH_LIMIT_FOR_COMRPESSION))
+                  {
+                     char *compressed_content = compress_buffer(p, (size_t *)&csp->content_length);
+                     if (compressed_content != NULL)
+                     {
+                        freez(p);
+                        p = compressed_content;
+                        csp->flags |= CSP_FLAG_BUFFERED_CONTENT_DEFLATED;
+                     }
+                  }
+#endif
 
                   if (JB_ERR_OK != update_server_headers(csp))
                   {
index 1184560..a973078 100644 (file)
--- a/parsers.c
+++ b/parsers.c
@@ -1,4 +1,4 @@
-const char parsers_rcs[] = "$Id: parsers.c,v 1.222 2011/04/19 13:00:47 fabiankeil Exp $";
+const char parsers_rcs[] = "$Id: parsers.c,v 1.223 2011/06/23 13:57:47 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/parsers.c,v $
@@ -1187,6 +1187,18 @@ jb_err update_server_headers(struct client_state *csp)
    }
 #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
 
+#ifdef FEATURE_COMPRESSION
+   if ((JB_ERR_OK == err)
+      && (csp->flags & CSP_FLAG_BUFFERED_CONTENT_DEFLATED))
+   {
+      err = enlist_unique_header(csp->headers, "Content-Encoding", "deflate");
+      if (JB_ERR_OK == err)
+      {
+         log_error(LOG_LEVEL_HEADER, "Added header: Content-Encoding: deflate");
+      }
+   }
+#endif
+
    return err;
 }
 
@@ -2607,6 +2619,12 @@ static jb_err server_last_modified(struct client_state *csp, char **header)
  *********************************************************************/
 static jb_err client_accept_encoding(struct client_state *csp, char **header)
 {
+#ifdef FEATURE_COMPRESSION
+   if (strstr(*header, "deflate"))
+   {
+      csp->flags |= CSP_FLAG_CLIENT_SUPPORTS_DEFLATE;
+   }
+#endif
    if ((csp->action->flags & ACTION_NO_COMPRESSION) != 0)
    {
       log_error(LOG_LEVEL_HEADER, "Suppressed offer to compress content");
index 8b0e68c..dd38cf8 100644 (file)
--- a/project.h
+++ b/project.h
@@ -1,7 +1,7 @@
 #ifndef PROJECT_H_INCLUDED
 #define PROJECT_H_INCLUDED
 /** Version string. */
-#define PROJECT_H_VERSION "$Id: project.h,v 1.163 2011/02/19 13:58:48 fabiankeil Exp $"
+#define PROJECT_H_VERSION "$Id: project.h,v 1.164 2011/04/19 13:00:47 fabiankeil Exp $"
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/project.h,v $
@@ -187,6 +187,12 @@ typedef int jb_err;
  */
 #define CGI_PARAM_LEN_MAX 500U
 
+/**
+ * Minimum length which a buffer has to reach before
+ * Privoxy bothers to (re-)compress it. Completely arbitrary.
+ */
+#define LOWER_LENGTH_LIMIT_FOR_COMRPESSION 1024U
+
 /**
  * Buffer size for capturing struct hostent data in the
  * gethostby(name|addr)_r library calls. Since we don't
@@ -810,6 +816,17 @@ struct reusable_connection
  */
 #define CSP_FLAG_REUSED_CLIENT_CONNECTION           0x00100000U
 
+/**
+ * Flag for csp->flags: Set if the supports deflate compression.
+ */
+#define CSP_FLAG_CLIENT_SUPPORTS_DEFLATE            0x00200000U
+
+/**
+ * Flag for csp->flags: Set if the content has been deflated by Privoxy
+ */
+#define CSP_FLAG_BUFFERED_CONTENT_DEFLATED          0x00400000U
+
+
 /*
  * Flags for use in return codes of child processes
  */