mirror of
https://github.com/owntone/owntone-server.git
synced 2025-01-29 15:36:00 -05:00
[httpd] Reduce memcpy when gzipping a response
This commit is contained in:
parent
0c6af89807
commit
f7185db035
189
src/httpd.c
189
src/httpd.c
@ -139,6 +139,68 @@ struct stream_ctx *g_st;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
static struct evbuffer *
|
||||||
|
gzip(struct evbuffer *in)
|
||||||
|
{
|
||||||
|
struct evbuffer *out;
|
||||||
|
struct evbuffer_iovec iovec[1];
|
||||||
|
z_stream strm;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
strm.zalloc = Z_NULL;
|
||||||
|
strm.zfree = Z_NULL;
|
||||||
|
strm.opaque = Z_NULL;
|
||||||
|
|
||||||
|
// Set up a gzip stream (the "+ 16" in 15 + 16), instead of a zlib stream (default)
|
||||||
|
ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY);
|
||||||
|
if (ret != Z_OK)
|
||||||
|
{
|
||||||
|
DPRINTF(E_LOG, L_HTTPD, "zlib setup failed: %s\n", zError(ret));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
strm.next_in = evbuffer_pullup(in, -1);
|
||||||
|
strm.avail_in = evbuffer_get_length(in);
|
||||||
|
|
||||||
|
out = evbuffer_new();
|
||||||
|
if (!out)
|
||||||
|
{
|
||||||
|
DPRINTF(E_LOG, L_HTTPD, "Could not allocate evbuffer for gzipped reply\n");
|
||||||
|
goto out_deflate_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For avoiding memcpy. We only reserve length of input buffer, since we don't
|
||||||
|
// want to gzip if the result is larger than raw
|
||||||
|
ret = evbuffer_reserve_space(out, strm.avail_in, iovec, 1);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
DPRINTF(E_LOG, L_HTTPD, "Could not reserve memory for gzipped reply\n");
|
||||||
|
goto out_evbuf_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
strm.next_out = iovec[0].iov_base;
|
||||||
|
strm.avail_out = iovec[0].iov_len;
|
||||||
|
|
||||||
|
ret = deflate(&strm, Z_FINISH);
|
||||||
|
if (ret != Z_STREAM_END)
|
||||||
|
goto out_evbuf_free;
|
||||||
|
|
||||||
|
iovec[0].iov_len -= strm.avail_out;
|
||||||
|
|
||||||
|
evbuffer_commit_space(out, iovec, 1);
|
||||||
|
deflateEnd(&strm);
|
||||||
|
|
||||||
|
return out;
|
||||||
|
|
||||||
|
out_evbuf_free:
|
||||||
|
evbuffer_free(out);
|
||||||
|
|
||||||
|
out_deflate_end:
|
||||||
|
deflateEnd(&strm);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
stream_end(struct stream_ctx *st, int failed)
|
stream_end(struct stream_ctx *st, int failed)
|
||||||
{
|
{
|
||||||
@ -697,132 +759,43 @@ httpd_stream_file(struct evhttp_request *req, int id)
|
|||||||
void
|
void
|
||||||
httpd_send_reply(struct evhttp_request *req, int code, const char *reason, struct evbuffer *evbuf)
|
httpd_send_reply(struct evhttp_request *req, int code, const char *reason, struct evbuffer *evbuf)
|
||||||
{
|
{
|
||||||
unsigned char outbuf[128 * 1024];
|
|
||||||
z_stream strm;
|
|
||||||
struct evbuffer *gzbuf;
|
struct evbuffer *gzbuf;
|
||||||
struct evkeyvalq *headers;
|
struct evkeyvalq *input_headers;
|
||||||
|
struct evkeyvalq *output_headers;
|
||||||
const char *param;
|
const char *param;
|
||||||
int flush;
|
int do_gzip;
|
||||||
int zret;
|
|
||||||
int ret;
|
|
||||||
char *origin;
|
char *origin;
|
||||||
|
|
||||||
if (!req)
|
if (!req)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!evbuf || (evbuffer_get_length(evbuf) == 0))
|
input_headers = evhttp_request_get_input_headers(req);
|
||||||
{
|
output_headers = evhttp_request_get_output_headers(req);
|
||||||
DPRINTF(E_DBG, L_HTTPD, "Not gzipping body-less reply\n");
|
|
||||||
|
|
||||||
goto no_gzip;
|
do_gzip = ( evbuf && (evbuffer_get_length(evbuf) > 0) &&
|
||||||
}
|
(param = evhttp_find_header(input_headers, "Accept-Encoding")) &&
|
||||||
|
(strstr(param, "gzip") || strstr(param, "*"))
|
||||||
headers = evhttp_request_get_input_headers(req);
|
);
|
||||||
|
|
||||||
param = evhttp_find_header(headers, "Accept-Encoding");
|
|
||||||
if (!param)
|
|
||||||
{
|
|
||||||
DPRINTF(E_DBG, L_HTTPD, "Not gzipping; no Accept-Encoding header\n");
|
|
||||||
|
|
||||||
goto no_gzip;
|
|
||||||
}
|
|
||||||
else if (!strstr(param, "gzip") && !strstr(param, "*"))
|
|
||||||
{
|
|
||||||
DPRINTF(E_DBG, L_HTTPD, "Not gzipping; gzip not in Accept-Encoding (%s)\n", param);
|
|
||||||
|
|
||||||
goto no_gzip;
|
|
||||||
}
|
|
||||||
|
|
||||||
gzbuf = evbuffer_new();
|
|
||||||
if (!gzbuf)
|
|
||||||
{
|
|
||||||
DPRINTF(E_LOG, L_HTTPD, "Could not allocate evbuffer for gzipped reply\n");
|
|
||||||
|
|
||||||
goto no_gzip;
|
|
||||||
}
|
|
||||||
|
|
||||||
strm.zalloc = Z_NULL;
|
|
||||||
strm.zfree = Z_NULL;
|
|
||||||
strm.opaque = Z_NULL;
|
|
||||||
|
|
||||||
/* Set up a gzip stream (the "+ 16" in 15 + 16), instead of a zlib stream (default) */
|
|
||||||
zret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY);
|
|
||||||
if (zret != Z_OK)
|
|
||||||
{
|
|
||||||
DPRINTF(E_DBG, L_HTTPD, "zlib setup failed: %s\n", zError(zret));
|
|
||||||
|
|
||||||
goto out_fail_init;
|
|
||||||
}
|
|
||||||
|
|
||||||
strm.next_in = evbuffer_pullup(evbuf, -1);
|
|
||||||
strm.avail_in = evbuffer_get_length(evbuf);
|
|
||||||
|
|
||||||
flush = Z_NO_FLUSH;
|
|
||||||
|
|
||||||
/* 2 iterations: Z_NO_FLUSH until input is consumed, then Z_FINISH */
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
do
|
|
||||||
{
|
|
||||||
strm.next_out = outbuf;
|
|
||||||
strm.avail_out = sizeof(outbuf);
|
|
||||||
|
|
||||||
zret = deflate(&strm, flush);
|
|
||||||
if (zret == Z_STREAM_ERROR)
|
|
||||||
{
|
|
||||||
DPRINTF(E_LOG, L_HTTPD, "Could not deflate data: %s\n", strm.msg);
|
|
||||||
|
|
||||||
goto out_fail_gz;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = evbuffer_add(gzbuf, outbuf, sizeof(outbuf) - strm.avail_out);
|
|
||||||
if (ret < 0)
|
|
||||||
{
|
|
||||||
DPRINTF(E_LOG, L_HTTPD, "Out of memory adding gzipped data to evbuffer\n");
|
|
||||||
|
|
||||||
goto out_fail_gz;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (strm.avail_out == 0);
|
|
||||||
|
|
||||||
if (flush == Z_FINISH)
|
|
||||||
break;
|
|
||||||
|
|
||||||
flush = Z_FINISH;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (zret != Z_STREAM_END)
|
|
||||||
{
|
|
||||||
DPRINTF(E_LOG, L_HTTPD, "Compressed data not finalized!\n");
|
|
||||||
|
|
||||||
goto out_fail_gz;
|
|
||||||
}
|
|
||||||
|
|
||||||
deflateEnd(&strm);
|
|
||||||
|
|
||||||
headers = evhttp_request_get_output_headers(req);
|
|
||||||
|
|
||||||
origin = cfg_getstr(cfg_getsec(cfg, "general"), "allow_origin");
|
origin = cfg_getstr(cfg_getsec(cfg, "general"), "allow_origin");
|
||||||
if (origin && strlen(origin))
|
if (origin && strlen(origin))
|
||||||
evhttp_add_header(headers, "Access-Control-Allow-Origin", origin);
|
evhttp_add_header(output_headers, "Access-Control-Allow-Origin", origin);
|
||||||
|
|
||||||
evhttp_add_header(headers, "Content-Encoding", "gzip");
|
if (do_gzip && (gzbuf = gzip(evbuf)))
|
||||||
|
{
|
||||||
|
evhttp_add_header(output_headers, "Content-Encoding", "gzip");
|
||||||
evhttp_send_reply(req, code, reason, gzbuf);
|
evhttp_send_reply(req, code, reason, gzbuf);
|
||||||
|
|
||||||
evbuffer_free(gzbuf);
|
evbuffer_free(gzbuf);
|
||||||
|
|
||||||
/* Drain original buffer, as would be after evhttp_send_reply() */
|
// Drain original buffer, as would be after evhttp_send_reply()
|
||||||
evbuffer_drain(evbuf, evbuffer_get_length(evbuf));
|
evbuffer_drain(evbuf, evbuffer_get_length(evbuf));
|
||||||
|
}
|
||||||
return;
|
else
|
||||||
|
{
|
||||||
out_fail_gz:
|
DPRINTF(E_DBG, L_HTTPD, "Not gzipping response\n");
|
||||||
deflateEnd(&strm);
|
|
||||||
out_fail_init:
|
|
||||||
evbuffer_free(gzbuf);
|
|
||||||
no_gzip:
|
|
||||||
evhttp_send_reply(req, code, reason, evbuf);
|
evhttp_send_reply(req, code, reason, evbuf);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Thread: httpd */
|
/* Thread: httpd */
|
||||||
static int
|
static int
|
||||||
|
Loading…
x
Reference in New Issue
Block a user