[PATCH] httpd: add option to send files using gzip content-encoding if available

Peter Korsgaard jacmet at sunsite.dk
Fri Jul 23 15:22:12 UTC 2010


From: Peter Korsgaard <peter.korsgaard at barco.com>

With this option enabled, httpd sends compressed data if supported by
the client and a pre-compressed <file>.gz is available.

bloat-o-meter:
function                                             old     new   delta
send_file_and_exit                                   667     833    +166
handle_incoming_and_exit                            2983    3121    +138
.rodata                                           126806  126876     +70
send_headers                                         754     803     +49
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 4/0 up/down: 423/0)             Total: 423 bytes

Signed-off-by: Peter Korsgaard <peter.korsgaard at barco.com>
---
 networking/Config.src |    8 +++++++
 networking/httpd.c    |   51 ++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 58 insertions(+), 1 deletions(-)

diff --git a/networking/Config.src b/networking/Config.src
index 2d29c42..8604c53 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 8ad7e88..55b11c8 100644
--- a/networking/httpd.c
+++ b/networking/httpd.c
@@ -284,6 +284,10 @@ struct globals {
 #if ENABLE_FEATURE_HTTPD_PROXY
 	Htaccess_Proxy *proxy;
 #endif
+#if ENABLE_FEATURE_HTTPD_GZIP
+	smallint supports_gzip; /* client can handle gzip */
+	smallint gzipped;       /* file is gzipped */
+#endif
 };
 #define G (*ptr_to_globals)
 #define verbose           (G.verbose          )
@@ -326,6 +330,8 @@ enum {
 #define hdr_cnt           (G.hdr_cnt          )
 #define http_error_page   (G.http_error_page  )
 #define proxy             (G.proxy            )
+#define supports_gzip     (G.supports_gzip    )
+#define gzipped           (G.gzipped          )
 #define INIT_G() do { \
 	SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
 	IF_FEATURE_HTTPD_BASIC_AUTH(g_realm = "Web Server Authentication";) \
@@ -1034,10 +1040,18 @@ static void send_headers(int responseNum)
 #endif
 			"Last-Modified: %s\r\n%s %"OFF_FMT"u\r\n",
 				tmp_str,
+#if ENABLE_FEATURE_HTTPD_GZIP
+				gzipped ? "Transfer-length:" :
+#endif
 				"Content-length:",
 				file_size
 		);
 	}
+#if ENABLE_FEATURE_HTTPD_GZIP
+	if (gzipped)
+		len += sprintf(iobuf + len, "Content-Encoding: gzip\r\n");
+#endif
+
 	iobuf[len++] = '\r';
 	iobuf[len++] = '\n';
 	if (infoString) {
@@ -1507,6 +1521,26 @@ static NOINLINE void send_file_and_exit(const char *url, int what)
 	int fd;
 	ssize_t count;
 
+#if ENABLE_FEATURE_HTTPD_GZIP
+	if (supports_gzip) {
+		char *gzurl;
+
+		/* does <url>.gz exist? Then use it instead */
+		gzurl = alloca(strlen(url) + strlen(".gz") + 1);
+		sprintf(gzurl, "%s.gz", url);
+		fd = open(gzurl, O_RDONLY);
+		if (fd != -1) {
+			struct stat sb;
+
+			fstat(fd, &sb);
+			file_size = sb.st_size;
+			gzipped = 1;
+		}
+		else
+			fd = open(url, O_RDONLY);
+	}
+	else
+#endif
 	fd = open(url, O_RDONLY);
 	if (fd < 0) {
 		if (DEBUG)
@@ -1590,7 +1624,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)
+	if (what == SEND_BODY
+#if ENABLE_FEATURE_HTTPD_GZIP
+		|| gzipped
+#endif
+		)
 		range_start = 0; /* err pages and ranges don't mix */
 	range_len = MAXINT(off_t);
 	if (range_start) {
@@ -2055,6 +2093,17 @@ 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) {
+					s = skip_whitespace(s);
+					if (STRNCASECMP(s, "gzip") == 0)
+							supports_gzip = 1;
+					s = skip_non_whitespace(s);
+				}
+			}
+#endif
 		} /* while extra header reading */
 	}
 
-- 
1.7.1



More information about the busybox mailing list