From 7a2ba329b9235af4c647e0a88c1db023b51cf8a4 Mon Sep 17 00:00:00 2001
From: Peter Korsgaard <jacmet@sunsite.dk>
Date: Sun, 25 Jul 2010 03:20:53 +0200
Subject: httpd: optional support for gzip-compressed pages

function                                             old     new   delta
send_file_and_exit                                   662     761     +99
handle_incoming_and_exit                            2756    2830     +74
send_headers                                         603     654     +51
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 3/0 up/down: 224/0)             Total: 224 bytes

Signed-off-by: Peter Korsgaard <jacmet@sunsite.dk>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
---
 networking/Config.src |  8 ++++++++
 networking/httpd.c    | 56 +++++++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 60 insertions(+), 4 deletions(-)

diff --git a/networking/Config.src b/networking/Config.src
index 2d29c423a..8604c53e9 100644
--- a/networking/Config.src
+++ b/networking/Config.src
@@ -270,6 +270,14 @@ config FEATURE_HTTPD_PROXY
 	  Then a request to /url/myfile will be forwarded to
 	  http://hostname[:port]/new/path/myfile.
 
+config FEATURE_HTTPD_GZIP
+	bool "Support for GZIP content encoding"
+	default y
+	depends on HTTPD
+	help
+	  Makes httpd send files using GZIP content encoding if the
+	  client supports it and a pre-compressed <file>.gz exists.
+
 config IFCONFIG
 	bool "ifconfig"
 	default y
diff --git a/networking/httpd.c b/networking/httpd.c
index 12bad597a..cad45cd5d 100644
--- a/networking/httpd.c
+++ b/networking/httpd.c
@@ -277,6 +277,10 @@ struct globals {
 #if ENABLE_FEATURE_HTTPD_PROXY
 	Htaccess_Proxy *proxy;
 #endif
+#if ENABLE_FEATURE_HTTPD_GZIP
+	/* client can handle gzip / we are going to send gzip */
+	smallint content_gzip;
+#endif
 };
 #define G (*ptr_to_globals)
 #define verbose           (G.verbose          )
@@ -319,6 +323,11 @@ enum {
 #define hdr_cnt           (G.hdr_cnt          )
 #define http_error_page   (G.http_error_page  )
 #define proxy             (G.proxy            )
+#if ENABLE_FEATURE_HTTPD_GZIP
+# define content_gzip     (G.content_gzip     )
+#else
+# define content_gzip     0
+#endif
 #define INIT_G() do { \
 	SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
 	IF_FEATURE_HTTPD_BASIC_AUTH(g_realm = "Web Server Authentication";) \
@@ -1027,10 +1036,14 @@ static void send_headers(int responseNum)
 #endif
 			"Last-Modified: %s\r\n%s %"OFF_FMT"u\r\n",
 				tmp_str,
-				"Content-length:",
+				content_gzip ? "Transfer-length:" : "Content-length:",
 				file_size
 		);
 	}
+
+	if (content_gzip)
+		len += sprintf(iobuf + len, "Content-Encoding: gzip\r\n");
+
 	iobuf[len++] = '\r';
 	iobuf[len++] = '\n';
 	if (infoString) {
@@ -1500,7 +1513,22 @@ static NOINLINE void send_file_and_exit(const char *url, int what)
 	int fd;
 	ssize_t count;
 
-	fd = open(url, O_RDONLY);
+	if (content_gzip) {
+		/* does <url>.gz exist? Then use it instead */
+		char *gzurl = xasprintf("%s.gz", url);
+		fd = open(gzurl, O_RDONLY);
+		free(gzurl);
+		if (fd != -1) {
+			struct stat sb;
+			fstat(fd, &sb);
+			file_size = sb.st_size;
+		} else {
+			IF_FEATURE_HTTPD_GZIP(content_gzip = 0;)
+			fd = open(url, O_RDONLY);
+		}
+	} else {
+		fd = open(url, O_RDONLY);
+	}
 	if (fd < 0) {
 		if (DEBUG)
 			bb_perror_msg("can't open '%s'", url);
@@ -1583,8 +1611,11 @@ static NOINLINE void send_file_and_exit(const char *url, int what)
 			url, found_mime_type);
 
 #if ENABLE_FEATURE_HTTPD_RANGES
-	if (what == SEND_BODY)
-		range_start = 0; /* err pages and ranges don't mix */
+	if (what == SEND_BODY /* err pages and ranges don't mix */
+	 || content_gzip /* we are sending compressed page: can't do ranges */  ///why?
+	) {
+		range_start = 0;
+	}
 	range_len = MAXINT(off_t);
 	if (range_start) {
 		if (!range_end) {
@@ -2047,6 +2078,23 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
 					}
 				}
 			}
+#endif
+#if ENABLE_FEATURE_HTTPD_GZIP
+			if (STRNCASECMP(iobuf, "Accept-Encoding:") == 0) {
+				char *s = iobuf + sizeof("Accept-Encoding:")-1;
+				while (*s) {
+					///is "Accept-Encoding: compress,gzip" valid?
+					// (that is, no space after ',') -
+					// this code won't handle that
+					s = skip_whitespace(s);
+					if (STRNCASECMP(s, "gzip") == 0)
+						content_gzip = 1;
+					/* Note: we do not support "gzip;q=0"
+					 * method of _disabling_ gzip
+					 * delivery */
+					s = skip_non_whitespace(s);
+				}
+			}
 #endif
 		} /* while extra header reading */
 	}
-- 
cgit v1.2.3