[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