[OpenWrt-Devel] [PATCH 2/2] uhttpd: add support for gzipped content encoding

Andrej Krpic ak77 at tnode.com
Thu Oct 1 18:22:14 EDT 2015


this patch implements gzip content encoding. It is used when "gzip" 
string is present in a Accept-Encoding header of a request and the 
decision to use chunked encoding in a response was already made.
Adds zlib dependency.

Signed-off-by: Andrej Krpic <ak77 at tnode.com>
---
 CMakeLists.txt |  7 +++++++
 client.c       | 16 ++++++++++++++--
 gzip.c         | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
 main.c         | 16 +++++++++++++++-
 uhttpd.h       | 17 +++++++++++++++++
 utils.c        | 13 ++++++++++++-
 6 files changed, 114 insertions(+), 4 deletions(-)
 create mode 100644 gzip.c

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8c285dc..1dd24b8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -10,6 +10,7 @@ ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64 -Os -Wall -Werror -Wmissing-declarations
 OPTION(TLS_SUPPORT "TLS support" ON)
 OPTION(LUA_SUPPORT "Lua support" ON)
 OPTION(UBUS_SUPPORT "ubus support" ON)
+OPTION(GZIP_SUPPORT "gzip encoding support" ON)
 
 IF(APPLE)
   INCLUDE_DIRECTORIES(/opt/local/include)
@@ -27,6 +28,12 @@ IF(TLS_SUPPORT)
 	ADD_DEFINITIONS(-DHAVE_TLS)
 ENDIF()
 
+IF(GZIP_SUPPORT)
+	SET(SOURCES ${SOURCES} gzip.c)
+	SET(LIBS ${LIBS} "z")
+	ADD_DEFINITIONS(-DHAVE_ZLIB)
+ENDIF()
+
 CHECK_FUNCTION_EXISTS(getspnam HAVE_SHADOW)
 IF(HAVE_SHADOW)
     ADD_DEFINITIONS(-DHAVE_SHADOW)
diff --git a/client.c b/client.c
index 8569b21..96eecd6 100644
--- a/client.c
+++ b/client.c
@@ -61,6 +61,7 @@ void uh_http_header(struct client *cl, int code, const char *summary)
 {
 	struct http_request *r = &cl->request;
 	const char *enc = "Transfer-Encoding: chunked\r\n";
+	const char *cenc = "Content-Encoding: gzip\r\n";
 	const char *conn;
 
 	cl->http_code = code;
@@ -70,14 +71,19 @@ void uh_http_header(struct client *cl, int code, const char *summary)
 	else
 		chunked_init(cl);
 
+	if (!cl->request.respond_gzipped || !uh_gzip_init(cl)) {
+		cl->request.respond_gzipped = false;
+		cenc = "";
+	}
+
 	if (r->connection_close)
 		conn = "Connection: close";
 	else
 		conn = "Connection: Keep-Alive";
 
-	ustream_printf(cl->us, "%s %03i %s\r\n%s\r\n%s",
+	ustream_printf(cl->us, "%s %03i %s\r\n%s\r\n%s%s",
 		http_versions[cl->request.version],
-		code, summary, conn, enc);
+		code, summary, conn, enc, cenc);
 
 	if (!r->connection_close)
 		ustream_printf(cl->us, "Keep-Alive: timeout=%d\r\n", conf.http_keepalive);
@@ -279,6 +285,7 @@ static bool tls_redirect_check(struct client *cl)
 		*ptr = 0;
 
 	cl->request.respond_chunked = false;
+	cl->request.respond_gzipped = false;
 	cl->request.connection_close = true;
 
 	uh_http_header(cl, 307, "Temporary Redirect");
@@ -359,6 +366,11 @@ static void client_parse_header(struct client *cl, char *data)
 			uh_header_error(cl, 400, "Bad Request");
 			return;
 		}
+#ifdef HAVE_ZLIB
+	} else if (!strcmp(data, "accept-encoding") && strstr(val, "gzip")) {
+		if (!conf.no_gzip_encoding && r->respond_chunked)
+			r->respond_gzipped = true;
+#endif
 	} else if (!strcmp(data, "transfer-encoding")) {
 		if (!strcmp(val, "chunked"))
 			r->transfer_chunked = true;
diff --git a/gzip.c b/gzip.c
new file mode 100644
index 0000000..c4c5969
--- /dev/null
+++ b/gzip.c
@@ -0,0 +1,49 @@
+#include <zlib.h>
+#include <string.h>
+#include "uhttpd.h"
+
+static int ustream_gzip_write(struct ustream *s, const char *buf, int len, bool more)
+{
+	struct client *cl = container_of(s, struct client, gzip);
+	z_stream *zs = &cl->zstream;
+	bool flush = !more && len == 0 && buf && *buf == 0;
+
+	char *gzbuf;
+	int gzlen, ret;
+
+	zs->avail_in = len;
+	zs->next_in = (z_const Bytef *)buf;
+
+	gzlen = len > 0 ? len : 1024;
+	gzbuf = calloc(gzlen, sizeof(char));
+	if (!gzbuf)
+		return -1;
+more:
+	zs->avail_out = gzlen;
+	zs->next_out = (Bytef *)gzbuf;
+
+	ret = deflate(zs, flush ? Z_FINISH : Z_SYNC_FLUSH);
+
+	ustream_write(&cl->chunked, gzbuf, gzlen - zs->avail_out, more);
+
+	if (flush && ret == Z_OK)
+		goto more;
+	else if (ret == Z_STREAM_END)
+		 ret = deflateEnd(zs);
+
+	free(gzbuf);
+	return len;
+}
+
+bool uh_gzip_init(struct client *cl) {
+	ustream_init_defaults(&cl->gzip);
+	cl->gzip.write = &ustream_gzip_write;
+
+	cl->zstream.zalloc = Z_NULL;
+	cl->zstream.zfree = Z_NULL;
+	cl->zstream.opaque = Z_NULL;
+
+	return deflateInit2(&cl->zstream, Z_DEFAULT_COMPRESSION,
+			    Z_DEFLATED, 16 | MAX_WBITS,
+			    8, Z_DEFAULT_STRATEGY) == Z_OK;
+}
diff --git a/main.c b/main.c
index ed47486..a1138dc 100644
--- a/main.c
+++ b/main.c
@@ -162,6 +162,9 @@ static int usage(const char *name)
 		"	-d string       URL decode given string\n"
 		"	-r string       Specify basic auth realm\n"
 		"	-m string       MD5 crypt given string\n"
+#ifdef HAVE_ZLIB
+		"	-Z 		Disable gzip content encoding.\n"
+#endif
 		"\n", name
 	);
 	return 1;
@@ -177,6 +180,7 @@ static void init_defaults_pre(void)
 	conf.realm = "Protected Area";
 	conf.cgi_prefix = "/cgi-bin";
 	conf.cgi_path = "/sbin:/usr/sbin:/bin:/usr/bin";
+	conf.no_gzip_encoding = 0;
 }
 
 static void init_defaults_post(void)
@@ -228,7 +232,7 @@ int main(int argc, char **argv)
 	init_defaults_pre();
 	signal(SIGPIPE, SIG_IGN);
 
-	while ((ch = getopt(argc, argv, "afqSDRXC:K:E:I:p:s:h:c:l:L:d:r:m:n:N:x:i:t:k:T:A:u:U:")) != -1) {
+	while ((ch = getopt(argc, argv, "afqSDRXC:K:E:I:p:s:h:c:l:L:d:r:m:n:N:x:i:t:k:T:A:u:U:Z")) != -1) {
 		switch(ch) {
 #ifdef HAVE_TLS
 		case 'C':
@@ -422,6 +426,16 @@ int main(int argc, char **argv)
 			                "ignoring -%c\n", ch);
 			break;
 #endif
+#if HAVE_ZLIB
+		case 'Z':
+			conf.no_gzip_encoding = 1;
+			break;
+#else
+		case 'Z':
+			fprintf(stderr, "uhttpd: zlib support not compiled, "
+					"ignoring -%c\n", ch);
+			break
+#endif
 		default:
 			return usage(argv[0]);
 		}
diff --git a/uhttpd.h b/uhttpd.h
index dd41c25..2d0a0a7 100644
--- a/uhttpd.h
+++ b/uhttpd.h
@@ -36,6 +36,9 @@
 #ifdef HAVE_TLS
 #include <libubox/ustream-ssl.h>
 #endif
+#ifdef HAVE_ZLIB
+#include <zlib.h>
+#endif
 
 #include "utils.h"
 
@@ -70,6 +73,9 @@ struct config {
 	int script_timeout;
 	int ubus_noauth;
 	int ubus_cors;
+#if HAVE_ZLIB
+	int no_gzip_encoding;
+#endif
 };
 
 struct auth_realm {
@@ -113,6 +119,7 @@ struct http_request {
 	bool expect_cont;
 	bool connection_close;
 	bool respond_chunked;
+	bool respond_gzipped;
 	uint8_t transfer_chunked;
 	const struct auth_realm *realm;
 };
@@ -245,6 +252,10 @@ struct client {
 
 	enum client_state state;
 	bool tls;
+#ifdef HAVE_ZLIB
+	struct ustream gzip;
+	z_stream zstream;
+#endif
 
 	int http_code;
 	struct http_request request;
@@ -311,6 +322,12 @@ bool uh_create_process(struct client *cl, struct path_info *pi, char *url,
 int uh_plugin_init(const char *name);
 void uh_plugin_post_init(void);
 
+#if HAVE_ZLIB
+bool uh_gzip_init(struct client *cl);
+#else
+bool uh_gzip_init(struct client *cl) { return false; }
+#endif
+
 static inline void uh_client_ref(struct client *cl)
 {
 	cl->refcount++;
diff --git a/utils.c b/utils.c
index 572beb9..49d7b9b 100644
--- a/utils.c
+++ b/utils.c
@@ -44,6 +44,10 @@ void uh_chunk_write(struct client *cl, const void *data, int len)
 
 	if (!cl->request.respond_chunked)
 		ustream_write(cl->us, data, len, true);
+#if HAVE_ZLIB
+	else if (cl->request.respond_gzipped)
+		ustream_write(&cl->gzip, data, len, true);
+#endif
 	else
 		ustream_write(&cl->chunked, data, len, true);
 }
@@ -56,6 +60,10 @@ void uh_chunk_vprintf(struct client *cl, const char *format, va_list arg)
 	uloop_timeout_set(&cl->timeout, conf.network_timeout * 1000);
 	if (!cl->request.respond_chunked)
 		ustream_vprintf(cl->us, format, arg);
+#if HAVE_ZLIB
+	else if (cl->request.respond_gzipped)
+		ustream_vprintf(&cl->gzip, format, arg);
+#endif
 	else
 		ustream_vprintf(&cl->chunked, format, arg);
 }
@@ -76,7 +84,10 @@ void uh_chunk_eof(struct client *cl)
 
 	if (cl->state == CLIENT_STATE_CLEANUP)
 		return;
-
+#if HAVE_ZLIB
+	if (cl->request.respond_gzipped)
+		ustream_write(&cl->gzip, "", 0, false);
+#endif
 	ustream_printf(cl->us, "0\r\n\r\n");
 }
 
-- 
2.4.6
_______________________________________________
openwrt-devel mailing list
openwrt-devel at lists.openwrt.org
https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel



More information about the openwrt-devel mailing list