From f7185db0352c81e0680c45f08175bf6f9a56229d Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Sun, 16 Oct 2016 23:36:27 +0200 Subject: [PATCH 01/13] [httpd] Reduce memcpy when gzipping a response --- src/httpd.c | 197 +++++++++++++++++++++++----------------------------- 1 file changed, 85 insertions(+), 112 deletions(-) diff --git a/src/httpd.c b/src/httpd.c index f0dc8e88..d98d6de5 100644 --- a/src/httpd.c +++ b/src/httpd.c @@ -139,6 +139,68 @@ struct stream_ctx *g_st; #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 stream_end(struct stream_ctx *st, int failed) { @@ -697,131 +759,42 @@ httpd_stream_file(struct evhttp_request *req, int id) void 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 evkeyvalq *headers; + struct evkeyvalq *input_headers; + struct evkeyvalq *output_headers; const char *param; - int flush; - int zret; - int ret; + int do_gzip; char *origin; if (!req) return; - if (!evbuf || (evbuffer_get_length(evbuf) == 0)) - { - DPRINTF(E_DBG, L_HTTPD, "Not gzipping body-less reply\n"); + input_headers = evhttp_request_get_input_headers(req); + output_headers = evhttp_request_get_output_headers(req); - goto no_gzip; - } - - 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); + do_gzip = ( evbuf && (evbuffer_get_length(evbuf) > 0) && + (param = evhttp_find_header(input_headers, "Accept-Encoding")) && + (strstr(param, "gzip") || strstr(param, "*")) + ); origin = cfg_getstr(cfg_getsec(cfg, "general"), "allow_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"); - evhttp_send_reply(req, code, reason, gzbuf); + if (do_gzip && (gzbuf = gzip(evbuf))) + { + evhttp_add_header(output_headers, "Content-Encoding", "gzip"); + evhttp_send_reply(req, code, reason, gzbuf); + evbuffer_free(gzbuf); - evbuffer_free(gzbuf); - - /* Drain original buffer, as would be after evhttp_send_reply() */ - evbuffer_drain(evbuf, evbuffer_get_length(evbuf)); - - return; - - out_fail_gz: - deflateEnd(&strm); - out_fail_init: - evbuffer_free(gzbuf); - no_gzip: - evhttp_send_reply(req, code, reason, evbuf); + // Drain original buffer, as would be after evhttp_send_reply() + evbuffer_drain(evbuf, evbuffer_get_length(evbuf)); + } + else + { + DPRINTF(E_DBG, L_HTTPD, "Not gzipping response\n"); + evhttp_send_reply(req, code, reason, evbuf); + } } /* Thread: httpd */ From d8696e72ea74f8e1a06f24ab11a772944bc73239 Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Mon, 17 Oct 2016 14:09:16 +0200 Subject: [PATCH 02/13] [httpd] Don't gzip small messages --- src/httpd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/httpd.c b/src/httpd.c index d98d6de5..efab4c29 100644 --- a/src/httpd.c +++ b/src/httpd.c @@ -772,7 +772,7 @@ httpd_send_reply(struct evhttp_request *req, int code, const char *reason, struc input_headers = evhttp_request_get_input_headers(req); output_headers = evhttp_request_get_output_headers(req); - do_gzip = ( evbuf && (evbuffer_get_length(evbuf) > 0) && + do_gzip = ( evbuf && (evbuffer_get_length(evbuf) > 512) && (param = evhttp_find_header(input_headers, "Accept-Encoding")) && (strstr(param, "gzip") || strstr(param, "*")) ); From 249d923af2fec6553b470c410b79939b6c4c7350 Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Mon, 17 Oct 2016 14:18:13 +0200 Subject: [PATCH 03/13] [httpd] Allow caller of httpd_send_reply to avoid gzipping --- src/httpd.c | 5 +++-- src/httpd.h | 7 ++++++- src/httpd_daap.c | 24 ++++++++++++------------ src/httpd_dacp.c | 20 ++++++++++---------- src/httpd_rsp.c | 2 +- 5 files changed, 32 insertions(+), 26 deletions(-) diff --git a/src/httpd.c b/src/httpd.c index efab4c29..67b868bf 100644 --- a/src/httpd.c +++ b/src/httpd.c @@ -757,7 +757,7 @@ httpd_stream_file(struct evhttp_request *req, int id) /* Thread: httpd */ 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, enum httpd_send_flags flags) { struct evbuffer *gzbuf; struct evkeyvalq *input_headers; @@ -772,7 +772,8 @@ httpd_send_reply(struct evhttp_request *req, int code, const char *reason, struc input_headers = evhttp_request_get_input_headers(req); output_headers = evhttp_request_get_output_headers(req); - do_gzip = ( evbuf && (evbuffer_get_length(evbuf) > 512) && + do_gzip = ( (!(flags & HTTPD_SEND_NO_GZIP)) && + evbuf && (evbuffer_get_length(evbuf) > 512) && (param = evhttp_find_header(input_headers, "Accept-Encoding")) && (strstr(param, "gzip") || strstr(param, "*")) ); diff --git a/src/httpd.h b/src/httpd.h index 83998187..27eff8e8 100644 --- a/src/httpd.h +++ b/src/httpd.h @@ -5,11 +5,16 @@ #include #include +enum httpd_send_flags +{ + HTTPD_SEND_NO_GZIP = (1 << 0), +}; + void httpd_stream_file(struct evhttp_request *req, int id); 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, enum httpd_send_flags flags); char * httpd_fixup_uri(struct evhttp_request *req); diff --git a/src/httpd_daap.c b/src/httpd_daap.c index c985a586..e38404e4 100644 --- a/src/httpd_daap.c +++ b/src/httpd_daap.c @@ -355,7 +355,7 @@ update_refresh_cb(int fd, short event, void *arg) evcon = evhttp_request_get_connection(ur->req); evhttp_connection_set_closecb(evcon, NULL, NULL); - httpd_send_reply(ur->req, HTTP_OK, "OK", evbuf); + httpd_send_reply(ur->req, HTTP_OK, "OK", evbuf, 0); update_remove(ur); } @@ -882,7 +882,7 @@ daap_reply_server_info(struct evhttp_request *req, struct evbuffer *evbuf, char evbuffer_add_buffer(evbuf, content); evbuffer_free(content); - httpd_send_reply(req, HTTP_OK, "OK", evbuf); + httpd_send_reply(req, HTTP_OK, "OK", evbuf, 0); return 0; } @@ -924,7 +924,7 @@ daap_reply_content_codes(struct evhttp_request *req, struct evbuffer *evbuf, cha dmap_add_short(evbuf, "mcty", dmap_fields[i].type); /* 10 */ } - httpd_send_reply(req, HTTP_OK, "OK", evbuf); + httpd_send_reply(req, HTTP_OK, "OK", evbuf, 0); return 0; } @@ -999,7 +999,7 @@ daap_reply_login(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, dmap_add_int(evbuf, "mstt", 200); /* 12 */ dmap_add_int(evbuf, "mlid", s->id); /* 12 */ - httpd_send_reply(req, HTTP_OK, "OK", evbuf); + httpd_send_reply(req, HTTP_OK, "OK", evbuf, 0); return 0; } @@ -1015,7 +1015,7 @@ daap_reply_logout(struct evhttp_request *req, struct evbuffer *evbuf, char **uri daap_session_remove(s); - httpd_send_reply(req, 204, "Logout Successful", evbuf); + httpd_send_reply(req, 204, "Logout Successful", evbuf, 0); return 0; } @@ -1068,7 +1068,7 @@ daap_reply_update(struct evhttp_request *req, struct evbuffer *evbuf, char **uri dmap_add_int(evbuf, "mstt", 200); /* 12 */ dmap_add_int(evbuf, "musr", current_rev); /* 12 */ - httpd_send_reply(req, HTTP_OK, "OK", evbuf); + httpd_send_reply(req, HTTP_OK, "OK", evbuf, 0); return 0; } @@ -1222,7 +1222,7 @@ daap_reply_dblist(struct evhttp_request *req, struct evbuffer *evbuf, char **uri evbuffer_add_buffer(evbuf, content); evbuffer_free(content); - httpd_send_reply(req, HTTP_OK, "OK", evbuf); + httpd_send_reply(req, HTTP_OK, "OK", evbuf, 0); return 0; } @@ -1510,7 +1510,7 @@ daap_reply_songlist_generic(struct evhttp_request *req, struct evbuffer *evbuf, } } - httpd_send_reply(req, HTTP_OK, "OK", evbuf); + httpd_send_reply(req, HTTP_OK, "OK", evbuf, 0); return 0; @@ -1821,7 +1821,7 @@ daap_reply_playlists(struct evhttp_request *req, struct evbuffer *evbuf, char ** return -1; } - httpd_send_reply(req, HTTP_OK, "OK", evbuf); + httpd_send_reply(req, HTTP_OK, "OK", evbuf, 0); return 0; @@ -2132,7 +2132,7 @@ daap_reply_groups(struct evhttp_request *req, struct evbuffer *evbuf, char **uri } } - httpd_send_reply(req, HTTP_OK, "OK", evbuf); + httpd_send_reply(req, HTTP_OK, "OK", evbuf, 0); return 0; @@ -2338,7 +2338,7 @@ daap_reply_browse(struct evhttp_request *req, struct evbuffer *evbuf, char **uri } } - httpd_send_reply(req, HTTP_OK, "OK", evbuf); + httpd_send_reply(req, HTTP_OK, "OK", evbuf, 0); ret = 0; @@ -2853,7 +2853,7 @@ daap_request(struct evhttp_request *req) ret = cache_daap_get(full_uri, evbuf); if (ret == 0) { - httpd_send_reply(req, HTTP_OK, "OK", evbuf); // TODO not all want this reply + httpd_send_reply(req, HTTP_OK, "OK", evbuf, 0); // TODO not all want this reply evbuffer_free(evbuf); free(uri); diff --git a/src/httpd_dacp.c b/src/httpd_dacp.c index f0f22fef..99eb139a 100644 --- a/src/httpd_dacp.c +++ b/src/httpd_dacp.c @@ -368,10 +368,10 @@ playstatusupdate_cb(int fd, short what, void *arg) { buf = evbuffer_pullup(update, -1); evbuffer_add(evbuf, buf, len); - httpd_send_reply(ur->req, HTTP_OK, "OK", evbuf); + httpd_send_reply(ur->req, HTTP_OK, "OK", evbuf, 0); } else - httpd_send_reply(ur->req, HTTP_OK, "OK", update); + httpd_send_reply(ur->req, HTTP_OK, "OK", update, 0); free(ur); } @@ -774,7 +774,7 @@ dacp_reply_ctrlint(struct evhttp_request *req, struct evbuffer *evbuf, char **ur dmap_add_char(evbuf, "cmrl", 1); /* 9, unknown */ dmap_add_long(evbuf, "ceSX", (1 << 1 | 1)); /* 16, unknown dacp - lowest bit announces support for playqueue-contents/-edit */ - httpd_send_reply(req, HTTP_OK, "OK", evbuf); + httpd_send_reply(req, HTTP_OK, "OK", evbuf, 0); } static int @@ -1103,7 +1103,7 @@ dacp_reply_cue_play(struct evhttp_request *req, struct evbuffer *evbuf, char **u dmap_add_int(evbuf, "mstt", 200); /* 12 */ dmap_add_int(evbuf, "miid", id); /* 12 */ - httpd_send_reply(req, HTTP_OK, "OK", evbuf); + httpd_send_reply(req, HTTP_OK, "OK", evbuf, 0); } static void @@ -1119,7 +1119,7 @@ dacp_reply_cue_clear(struct evhttp_request *req, struct evbuffer *evbuf, char ** dmap_add_int(evbuf, "mstt", 200); /* 12 */ dmap_add_int(evbuf, "miid", 0); /* 12 */ - httpd_send_reply(req, HTTP_OK, "OK", evbuf); + httpd_send_reply(req, HTTP_OK, "OK", evbuf, 0); } static void @@ -1673,7 +1673,7 @@ dacp_reply_playqueuecontents(struct evhttp_request *req, struct evbuffer *evbuf, dmap_add_char(evbuf, "apsm", status.shuffle); /* 9, daap.playlistshufflemode - not part of mlcl container */ dmap_add_char(evbuf, "aprm", status.repeat); /* 9, daap.playlistrepeatmode - not part of mlcl container */ - httpd_send_reply(req, HTTP_OK, "OK", evbuf); + httpd_send_reply(req, HTTP_OK, "OK", evbuf, 0); } static void @@ -1697,7 +1697,7 @@ dacp_reply_playqueueedit_clear(struct evhttp_request *req, struct evbuffer *evbu dmap_add_int(evbuf, "mstt", 200); /* 12 */ dmap_add_int(evbuf, "miid", 0); /* 12 */ - httpd_send_reply(req, HTTP_OK, "OK", evbuf); + httpd_send_reply(req, HTTP_OK, "OK", evbuf, 0); } static void @@ -2025,7 +2025,7 @@ dacp_reply_playstatusupdate(struct evhttp_request *req, struct evbuffer *evbuf, if (ret < 0) evhttp_send_error(req, 500, "Internal Server Error"); else - httpd_send_reply(req, HTTP_OK, "OK", evbuf); + httpd_send_reply(req, HTTP_OK, "OK", evbuf, 0); return; } @@ -2243,7 +2243,7 @@ dacp_reply_getproperty(struct evhttp_request *req, struct evbuffer *evbuf, char return; } - httpd_send_reply(req, HTTP_OK, "OK", evbuf); + httpd_send_reply(req, HTTP_OK, "OK", evbuf, 0); return; @@ -2354,7 +2354,7 @@ dacp_reply_getspeakers(struct evhttp_request *req, struct evbuffer *evbuf, char evbuffer_free(spklist); - httpd_send_reply(req, HTTP_OK, "OK", evbuf); + httpd_send_reply(req, HTTP_OK, "OK", evbuf, 0); } static void diff --git a/src/httpd_rsp.c b/src/httpd_rsp.c index 8e393749..4f35592a 100644 --- a/src/httpd_rsp.c +++ b/src/httpd_rsp.c @@ -283,7 +283,7 @@ rsp_send_reply(struct evhttp_request *req, mxml_node_t *reply) headers = evhttp_request_get_output_headers(req); evhttp_add_header(headers, "Content-Type", "text/xml; charset=utf-8"); evhttp_add_header(headers, "Connection", "close"); - httpd_send_reply(req, HTTP_OK, "OK", evbuf); + httpd_send_reply(req, HTTP_OK, "OK", evbuf, 0); evbuffer_free(evbuf); } From 7dc2a46261993ff90fc6eb7ed5826f0c1031d56a Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Mon, 17 Oct 2016 19:35:37 +0200 Subject: [PATCH 04/13] [cache] Save daap replies gzipped - should improve performance --- src/cache.c | 22 ++++++-- src/httpd.c | 130 ++++++++++++++++++++++++----------------------- src/httpd.h | 4 ++ src/httpd_daap.c | 4 +- 4 files changed, 92 insertions(+), 68 deletions(-) diff --git a/src/cache.c b/src/cache.c index 845d967f..a42ce018 100644 --- a/src/cache.c +++ b/src/cache.c @@ -38,6 +38,7 @@ #include "conffile.h" #include "logger.h" +#include "httpd.h" #include "httpd_daap.h" #include "db.h" #include "cache.h" @@ -680,7 +681,8 @@ cache_daap_query_add(void *arg, int *retval) #undef Q_TMPL } -/* Gets a reply from the cache */ +// Gets a reply from the cache. +// cmdarg->evbuf will be filled with the reply (gzipped) static enum command_state cache_daap_query_get(void *arg, int *retval) { @@ -783,6 +785,7 @@ cache_daap_update_cb(int fd, short what, void *arg) { sqlite3_stmt *stmt; struct evbuffer *evbuf; + struct evbuffer *gzbuf; char *errmsg; char *query; int ret; @@ -818,10 +821,23 @@ cache_daap_update_cb(int fd, short what, void *arg) continue; } - cache_daap_reply_add(query, evbuf); + gzbuf = httpd_gzip_deflate(evbuf); + if (!gzbuf) + { + DPRINTF(E_LOG, L_CACHE, "Error gzipping DAAP reply for query: %s\n", query); + cache_daap_query_delete(sqlite3_column_int(stmt, 0)); + free(query); + evbuffer_free(evbuf); + + continue; + } + + evbuffer_free(evbuf); + + cache_daap_reply_add(query, gzbuf); free(query); - evbuffer_free(evbuf); + evbuffer_free(gzbuf); } if (ret != SQLITE_DONE) diff --git a/src/httpd.c b/src/httpd.c index 67b868bf..d1136d02 100644 --- a/src/httpd.c +++ b/src/httpd.c @@ -139,68 +139,6 @@ struct stream_ctx *g_st; #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 stream_end(struct stream_ctx *st, int failed) { @@ -755,6 +693,69 @@ httpd_stream_file(struct evhttp_request *req, int id) free_mfi(mfi, 0); } +struct evbuffer * +httpd_gzip_deflate(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; + } + + // We use this to avoid a memcpy. The 512 is an arbitrary padding to make sure + // there is enough space, even if the compressed output should be slightly + // larger than input (could happen with small inputs). + ret = evbuffer_reserve_space(out, strm.avail_in + 512, 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; +} + /* Thread: httpd */ void httpd_send_reply(struct evhttp_request *req, int code, const char *reason, struct evbuffer *evbuf, enum httpd_send_flags flags) @@ -782,8 +783,10 @@ httpd_send_reply(struct evhttp_request *req, int code, const char *reason, struc if (origin && strlen(origin)) evhttp_add_header(output_headers, "Access-Control-Allow-Origin", origin); - if (do_gzip && (gzbuf = gzip(evbuf))) + if (do_gzip && (gzbuf = httpd_gzip_deflate(evbuf))) { + DPRINTF(E_DBG, L_HTTPD, "Gzipping response\n"); + evhttp_add_header(output_headers, "Content-Encoding", "gzip"); evhttp_send_reply(req, code, reason, gzbuf); evbuffer_free(gzbuf); @@ -793,7 +796,6 @@ httpd_send_reply(struct evhttp_request *req, int code, const char *reason, struc } else { - DPRINTF(E_DBG, L_HTTPD, "Not gzipping response\n"); evhttp_send_reply(req, code, reason, evbuf); } } diff --git a/src/httpd.h b/src/httpd.h index 27eff8e8..3cb5c490 100644 --- a/src/httpd.h +++ b/src/httpd.h @@ -13,6 +13,10 @@ enum httpd_send_flags void httpd_stream_file(struct evhttp_request *req, int id); +// Returns a pointer to an evbuffer with gzipped. Must be freed by caller. +struct evbuffer * +httpd_gzip_deflate(struct evbuffer *in); + void httpd_send_reply(struct evhttp_request *req, int code, const char *reason, struct evbuffer *evbuf, enum httpd_send_flags flags); diff --git a/src/httpd_daap.c b/src/httpd_daap.c index e38404e4..ec99d860 100644 --- a/src/httpd_daap.c +++ b/src/httpd_daap.c @@ -2853,7 +2853,9 @@ daap_request(struct evhttp_request *req) ret = cache_daap_get(full_uri, evbuf); if (ret == 0) { - httpd_send_reply(req, HTTP_OK, "OK", evbuf, 0); // TODO not all want this reply + // The cache will return the data gzipped, so httpd_send_reply won't need to do it + evhttp_add_header(headers, "Content-Encoding", "gzip"); + httpd_send_reply(req, HTTP_OK, "OK", evbuf, HTTPD_SEND_NO_GZIP); // TODO not all want this reply evbuffer_free(evbuf); free(uri); From 0b29b4368de1b7824fa5cb3ee96ab4715084090a Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Mon, 17 Oct 2016 22:03:32 +0200 Subject: [PATCH 05/13] [httpd] Enable CORS headers by default (Access-Control-Allow-Origin: *) --- src/conffile.c | 2 +- src/httpd.c | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/conffile.c b/src/conffile.c index dedee64e..df948ba6 100644 --- a/src/conffile.c +++ b/src/conffile.c @@ -56,7 +56,7 @@ static cfg_opt_t sec_general[] = CFG_STR("cache_path", STATEDIR "/cache/" PACKAGE "/cache.db", CFGF_NONE), CFG_INT("cache_daap_threshold", 1000, CFGF_NONE), CFG_BOOL("speaker_autoselect", cfg_true, CFGF_NONE), - CFG_STR("allow_origin", NULL, CFGF_NONE), + CFG_STR("allow_origin", "*", CFGF_NONE), CFG_END() }; diff --git a/src/httpd.c b/src/httpd.c index d1136d02..218c6389 100644 --- a/src/httpd.c +++ b/src/httpd.c @@ -134,6 +134,8 @@ static struct event *exitev; static struct evhttp *evhttpd; static pthread_t tid_httpd; +static char *allow_origin; + #ifdef HAVE_LIBEVENT2_OLD struct stream_ctx *g_st; #endif @@ -765,7 +767,6 @@ httpd_send_reply(struct evhttp_request *req, int code, const char *reason, struc struct evkeyvalq *output_headers; const char *param; int do_gzip; - char *origin; if (!req) return; @@ -779,9 +780,8 @@ httpd_send_reply(struct evhttp_request *req, int code, const char *reason, struc (strstr(param, "gzip") || strstr(param, "*")) ); - origin = cfg_getstr(cfg_getsec(cfg, "general"), "allow_origin"); - if (origin && strlen(origin)) - evhttp_add_header(output_headers, "Access-Control-Allow-Origin", origin); + if (allow_origin) + evhttp_add_header(output_headers, "Access-Control-Allow-Origin", allow_origin); if (do_gzip && (gzbuf = httpd_gzip_deflate(evbuf))) { @@ -1379,6 +1379,11 @@ httpd_init(void) v6enabled = cfg_getbool(cfg_getsec(cfg, "general"), "ipv6"); port = cfg_getint(cfg_getsec(cfg, "library"), "port"); + // For CORS headers + allow_origin = cfg_getstr(cfg_getsec(cfg, "general"), "allow_origin"); + if (allow_origin && (strlen(allow_origin) == 0)) + allow_origin = NULL; + if (v6enabled) { ret = evhttp_bind_socket(evhttpd, "::", port); From e1cfccbce7bc1c2e1e4083dde52fec26d03c80ae Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Mon, 17 Oct 2016 22:14:10 +0200 Subject: [PATCH 06/13] [httpd] Exchange evhttp_send_reply with httpd_send_reply in httpd.c --- src/httpd.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/httpd.c b/src/httpd.c index 218c6389..8a7573fa 100644 --- a/src/httpd.c +++ b/src/httpd.c @@ -828,9 +828,9 @@ redirect_to_index(struct evhttp_request *req, char *uri) } headers = evhttp_request_get_output_headers(req); - evhttp_add_header(headers, "Location", buf); - evhttp_send_reply(req, HTTP_MOVETEMP, "Moved", NULL); + + httpd_send_reply(req, HTTP_MOVETEMP, "Moved", NULL, HTTPD_SEND_NO_GZIP); } /* Thread: httpd */ @@ -997,9 +997,9 @@ serve_file(struct evhttp_request *req, char *uri) } headers = evhttp_request_get_output_headers(req); - evhttp_add_header(headers, "Content-Type", ctype); - evhttp_send_reply(req, HTTP_OK, "OK", evbuf); + + httpd_send_reply(req, HTTP_OK, "OK", evbuf, HTTPD_SEND_NO_GZIP); evbuffer_free(evbuf); } @@ -1282,8 +1282,10 @@ httpd_basic_auth(struct evhttp_request *req, char *user, char *passwd, char *rea headers = evhttp_request_get_output_headers(req); evhttp_add_header(headers, "WWW-Authenticate", header); + evbuffer_add(evbuf, http_reply_401, strlen(http_reply_401)); - evhttp_send_reply(req, 401, "Unauthorized", evbuf); + + httpd_send_reply(req, 401, "Unauthorized", evbuf, HTTPD_SEND_NO_GZIP); free(header); evbuffer_free(evbuf); From 40c49f1fde225a7c38b11edca4ae08110486ca46 Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Mon, 17 Oct 2016 22:25:31 +0200 Subject: [PATCH 07/13] [httpd] Use httpd_send_reply() wrapper in httpd_*, dmap_common and mpd --- src/dmap_common.c | 3 ++- src/httpd_daap.c | 9 ++++----- src/httpd_dacp.c | 29 ++++++++++++++--------------- src/httpd_rsp.c | 4 +++- src/mpd.c | 4 +++- 5 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/dmap_common.c b/src/dmap_common.c index 04dbcffa..c0e2952c 100644 --- a/src/dmap_common.c +++ b/src/dmap_common.c @@ -25,6 +25,7 @@ #include "db.h" #include "misc.h" +#include "httpd.h" #include "logger.h" #include "dmap_common.h" @@ -376,7 +377,7 @@ dmap_send_error(struct evhttp_request *req, const char *container, const char *e dmap_add_int(evbuf, "mstt", 500); dmap_add_string(evbuf, "msts", errmsg); - evhttp_send_reply(req, HTTP_OK, "OK", evbuf); + httpd_send_reply(req, HTTP_OK, "OK", evbuf, HTTPD_SEND_NO_GZIP); evbuffer_free(evbuf); } diff --git a/src/httpd_daap.c b/src/httpd_daap.c index ec99d860..ed7c3745 100644 --- a/src/httpd_daap.c +++ b/src/httpd_daap.c @@ -1122,7 +1122,7 @@ static int daap_reply_activity(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query, const char *ua) { /* That's so nice, thanks for letting us know */ - evhttp_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf); + httpd_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf, HTTPD_SEND_NO_GZIP); return 0; } @@ -2442,12 +2442,11 @@ daap_reply_extra_data(struct evhttp_request *req, struct evbuffer *evbuf, char * snprintf(clen, sizeof(clen), "%ld", (long)len); evhttp_add_header(headers, "Content-Length", clen); - /* No gzip compression for artwork */ - evhttp_send_reply(req, HTTP_OK, "OK", evbuf); + httpd_send_reply(req, HTTP_OK, "OK", evbuf, HTTPD_SEND_NO_GZIP); return 0; no_artwork: - evhttp_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf); + httpd_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf, HTTPD_SEND_NO_GZIP); return -1; } @@ -2602,7 +2601,7 @@ daap_reply_dmap_test(struct evhttp_request *req, struct evbuffer *evbuf, char ** return -1; } - evhttp_send_reply(req, HTTP_OK, "OK", evbuf); + httpd_send_reply(req, HTTP_OK, "OK", evbuf, HTTPD_SEND_NO_GZIP); return 0; } diff --git a/src/httpd_dacp.c b/src/httpd_dacp.c index 99eb139a..9b81898d 100644 --- a/src/httpd_dacp.c +++ b/src/httpd_dacp.c @@ -1282,7 +1282,7 @@ dacp_reply_playspec(struct evhttp_request *req, struct evbuffer *evbuf, char **u } /* 204 No Content is the canonical reply */ - evhttp_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf); + httpd_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf, HTTPD_SEND_NO_GZIP); return; out_fail: @@ -1301,7 +1301,7 @@ dacp_reply_pause(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, player_playback_pause(); /* 204 No Content is the canonical reply */ - evhttp_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf); + httpd_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf, HTTPD_SEND_NO_GZIP); } static void @@ -1334,7 +1334,7 @@ dacp_reply_playpause(struct evhttp_request *req, struct evbuffer *evbuf, char ** } /* 204 No Content is the canonical reply */ - evhttp_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf); + httpd_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf, HTTPD_SEND_NO_GZIP); } static void @@ -1366,7 +1366,7 @@ dacp_reply_nextitem(struct evhttp_request *req, struct evbuffer *evbuf, char **u } /* 204 No Content is the canonical reply */ - evhttp_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf); + httpd_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf, HTTPD_SEND_NO_GZIP); } static void @@ -1398,7 +1398,7 @@ dacp_reply_previtem(struct evhttp_request *req, struct evbuffer *evbuf, char **u } /* 204 No Content is the canonical reply */ - evhttp_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf); + httpd_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf, HTTPD_SEND_NO_GZIP); } static void @@ -1413,7 +1413,7 @@ dacp_reply_beginff(struct evhttp_request *req, struct evbuffer *evbuf, char **ur /* TODO */ /* 204 No Content is the canonical reply */ - evhttp_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf); + httpd_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf, HTTPD_SEND_NO_GZIP); } static void @@ -1428,7 +1428,7 @@ dacp_reply_beginrew(struct evhttp_request *req, struct evbuffer *evbuf, char **u /* TODO */ /* 204 No Content is the canonical reply */ - evhttp_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf); + httpd_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf, HTTPD_SEND_NO_GZIP); } static void @@ -1443,7 +1443,7 @@ dacp_reply_playresume(struct evhttp_request *req, struct evbuffer *evbuf, char * /* TODO */ /* 204 No Content is the canonical reply */ - evhttp_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf); + httpd_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf, HTTPD_SEND_NO_GZIP); } static int @@ -1826,7 +1826,7 @@ dacp_reply_playqueueedit_add(struct evhttp_request *req, struct evbuffer *evbuf, } /* 204 No Content is the canonical reply */ - evhttp_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf); + httpd_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf, HTTPD_SEND_NO_GZIP); } static void @@ -1871,7 +1871,7 @@ dacp_reply_playqueueedit_move(struct evhttp_request *req, struct evbuffer *evbuf } /* 204 No Content is the canonical reply */ - evhttp_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf); + httpd_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf, HTTPD_SEND_NO_GZIP); } static void @@ -1903,7 +1903,7 @@ dacp_reply_playqueueedit_remove(struct evhttp_request *req, struct evbuffer *evb } /* 204 No Content is the canonical reply */ - evhttp_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf); + httpd_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf, HTTPD_SEND_NO_GZIP); } static void @@ -2137,8 +2137,7 @@ dacp_reply_nowplayingartwork(struct evhttp_request *req, struct evbuffer *evbuf, snprintf(clen, sizeof(clen), "%ld", (long)len); evhttp_add_header(headers, "Content-Length", clen); - /* No gzip compression for artwork */ - evhttp_send_reply(req, HTTP_OK, "OK", evbuf); + httpd_send_reply(req, HTTP_OK, "OK", evbuf, HTTPD_SEND_NO_GZIP); return; no_artwork: @@ -2291,7 +2290,7 @@ dacp_reply_setproperty(struct evhttp_request *req, struct evbuffer *evbuf, char } /* 204 No Content is the canonical reply */ - evhttp_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf); + httpd_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf, HTTPD_SEND_NO_GZIP); } static void @@ -2445,7 +2444,7 @@ dacp_reply_setspeakers(struct evhttp_request *req, struct evbuffer *evbuf, char } /* 204 No Content is the canonical reply */ - evhttp_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf); + httpd_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf, HTTPD_SEND_NO_GZIP); } diff --git a/src/httpd_rsp.c b/src/httpd_rsp.c index 4f35592a..fd65a7f1 100644 --- a/src/httpd_rsp.c +++ b/src/httpd_rsp.c @@ -259,7 +259,8 @@ rsp_send_error(struct evhttp_request *req, char *errmsg) headers = evhttp_request_get_output_headers(req); evhttp_add_header(headers, "Content-Type", "text/xml; charset=utf-8"); evhttp_add_header(headers, "Connection", "close"); - evhttp_send_reply(req, HTTP_OK, "OK", evbuf); + + httpd_send_reply(req, HTTP_OK, "OK", evbuf, HTTPD_SEND_NO_GZIP); evbuffer_free(evbuf); } @@ -283,6 +284,7 @@ rsp_send_reply(struct evhttp_request *req, mxml_node_t *reply) headers = evhttp_request_get_output_headers(req); evhttp_add_header(headers, "Content-Type", "text/xml; charset=utf-8"); evhttp_add_header(headers, "Connection", "close"); + httpd_send_reply(req, HTTP_OK, "OK", evbuf, 0); evbuffer_free(evbuf); diff --git a/src/mpd.c b/src/mpd.c index a194d2b1..41346d7c 100644 --- a/src/mpd.c +++ b/src/mpd.c @@ -55,6 +55,7 @@ #include "logger.h" #include "db.h" #include "conffile.h" +#include "httpd.h" #include "misc.h" #include "listener.h" #include "artwork.h" @@ -4610,7 +4611,8 @@ artwork_cb(struct evhttp_request *req, void *arg) evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Type", "image/jpeg"); break; } - evhttp_send_reply(req, HTTP_OK, "OK", evbuffer); + + httpd_send_reply(req, HTTP_OK, "OK", evbuffer, HTTPD_SEND_NO_GZIP); } evbuffer_free(evbuffer); From fe7373e442239a540fea0965c2dade50c9155cce Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Mon, 17 Oct 2016 23:08:02 +0200 Subject: [PATCH 08/13] [httpd] Implement httpd_send_error, a modified evhttp_send_error, which can include CORS headers (credit @bjoernricks and libevent) --- src/httpd.c | 39 ++++++++++++++++++++++++++++++++++++++- src/httpd.h | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/src/httpd.c b/src/httpd.c index 8a7573fa..899dce1c 100644 --- a/src/httpd.c +++ b/src/httpd.c @@ -86,6 +86,11 @@ #define STREAM_CHUNK_SIZE (64 * 1024) #define WEBFACE_ROOT DATADIR "/webface/" +#define ERR_PAGE "\n\n" \ + "%d %s\n" \ + "\n\n" \ + "

%s

\n" \ + "\n\n" struct content_type_map { char *ext; @@ -758,7 +763,6 @@ httpd_gzip_deflate(struct evbuffer *in) return NULL; } -/* Thread: httpd */ void httpd_send_reply(struct evhttp_request *req, int code, const char *reason, struct evbuffer *evbuf, enum httpd_send_flags flags) { @@ -800,6 +804,39 @@ httpd_send_reply(struct evhttp_request *req, int code, const char *reason, struc } } +// This is a modified version of evhttp_send_error (credit libevent) +void +httpd_send_error(struct evhttp_request* req, int error, const char* reason) +{ + struct evkeyvalq *output_headers; + struct evbuffer *evbuf; + + if (!allow_origin) + { + evhttp_send_error(req, error, reason); + return; + } + + output_headers = evhttp_request_get_output_headers(req); + + evhttp_clear_headers(output_headers); + + evhttp_add_header(output_headers, "Access-Control-Allow-Origin", allow_origin); + evhttp_add_header(output_headers, "Content-Type", "text/html"); + evhttp_add_header(output_headers, "Connection", "close"); + + evbuf = evbuffer_new(); + if (!evbuf) + DPRINTF(E_LOG, L_HTTPD, "Could not allocate evbuffer for error page\n"); + else + evbuffer_add_printf(evbuf, ERR_PAGE, error, reason, reason); + + evhttp_send_reply(req, error, reason, evbuf); + + if (evbuf) + evbuffer_free(evbuf); +} + /* Thread: httpd */ static int path_is_legal(char *path) diff --git a/src/httpd.h b/src/httpd.h index 3cb5c490..7cd0f8fd 100644 --- a/src/httpd.h +++ b/src/httpd.h @@ -13,13 +13,45 @@ enum httpd_send_flags void httpd_stream_file(struct evhttp_request *req, int id); -// Returns a pointer to an evbuffer with gzipped. Must be freed by caller. +/* + * Gzips an evbuffer + * + * @in in Data to be compressed + * @return Compressed data - must be freed by caller + */ struct evbuffer * httpd_gzip_deflate(struct evbuffer *in); +/* + * This wrapper around evhttp_send_reply should be used whenever a request may + * come from a browser. It will automatically gzip if feasible, but the caller + * may direct it not to. It will set CORS headers as appropriate. Should be + * thread safe. + * + * @in req The evhttp request struct + * @in code HTTP code, e.g. 200 + * @in reason A brief explanation of the error - if NULL the standard meaning + of the error code will be used + * @in evbuf Data for the response body + * @in flags See flags above + */ void httpd_send_reply(struct evhttp_request *req, int code, const char *reason, struct evbuffer *evbuf, enum httpd_send_flags flags); +/* + * This is a substitute for evhttp_send_error that should be used whenever an + * error may be returned to a browser. It will set CORS headers as appropriate, + * which is not possible with evhttp_send_error, because it clears the headers. + * Should be thread safe. + * + * @in req The evhttp request struct + * @in error HTTP code, e.g. 200 + * @in reason A brief explanation of the error - if NULL the standard meaning + of the error code will be used + */ +void +httpd_send_error(struct evhttp_request *req, int error, const char *reason); + char * httpd_fixup_uri(struct evhttp_request *req); From c44f4310b7a4b476faf9670137751e47d3db5afe Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Tue, 18 Oct 2016 22:45:22 +0200 Subject: [PATCH 09/13] [httpd] Replace evhttp_send_error with the httpd_send_error wrapper --- src/dmap_common.c | 4 ++-- src/httpd.c | 28 ++++++++++++++-------------- src/httpd_daap.c | 28 ++++++++++++++-------------- src/httpd_dacp.c | 42 +++++++++++++++++++++--------------------- src/httpd_rsp.c | 4 ++-- src/mpd.c | 14 +++++++------- 6 files changed, 60 insertions(+), 60 deletions(-) diff --git a/src/dmap_common.c b/src/dmap_common.c index c0e2952c..495a7bfb 100644 --- a/src/dmap_common.c +++ b/src/dmap_common.c @@ -356,7 +356,7 @@ dmap_send_error(struct evhttp_request *req, const char *container, const char *e { DPRINTF(E_LOG, L_DMAP, "Could not allocate evbuffer for DMAP error\n"); - evhttp_send_error(req, HTTP_SERVUNAVAIL, "Internal Server Error"); + httpd_send_error(req, HTTP_SERVUNAVAIL, "Internal Server Error"); return; } @@ -367,7 +367,7 @@ dmap_send_error(struct evhttp_request *req, const char *container, const char *e { DPRINTF(E_LOG, L_DMAP, "Could not expand evbuffer for DMAP error\n"); - evhttp_send_error(req, HTTP_SERVUNAVAIL, "Internal Server Error"); + httpd_send_error(req, HTTP_SERVUNAVAIL, "Internal Server Error"); evbuffer_free(evbuf); return; diff --git a/src/httpd.c b/src/httpd.c index 899dce1c..d30fbfd8 100644 --- a/src/httpd.c +++ b/src/httpd.c @@ -860,7 +860,7 @@ redirect_to_index(struct evhttp_request *req, char *uri) { DPRINTF(E_LOG, L_HTTPD, "Redirection URL exceeds buffer length\n"); - evhttp_send_error(req, HTTP_NOTFOUND, "Not Found"); + httpd_send_error(req, HTTP_NOTFOUND, "Not Found"); return; } @@ -907,7 +907,7 @@ serve_file(struct evhttp_request *req, char *uri) { DPRINTF(E_LOG, L_HTTPD, "Remote web interface request denied; no password set\n"); - evhttp_send_error(req, 403, "Forbidden"); + httpd_send_error(req, 403, "Forbidden"); return; } } @@ -917,7 +917,7 @@ serve_file(struct evhttp_request *req, char *uri) { DPRINTF(E_LOG, L_HTTPD, "Request exceeds PATH_MAX: %s\n", uri); - evhttp_send_error(req, HTTP_NOTFOUND, "Not Found"); + httpd_send_error(req, HTTP_NOTFOUND, "Not Found"); return; } @@ -927,7 +927,7 @@ serve_file(struct evhttp_request *req, char *uri) { DPRINTF(E_LOG, L_HTTPD, "Could not lstat() %s: %s\n", path, strerror(errno)); - evhttp_send_error(req, HTTP_NOTFOUND, "Not Found"); + httpd_send_error(req, HTTP_NOTFOUND, "Not Found"); return; } @@ -945,7 +945,7 @@ serve_file(struct evhttp_request *req, char *uri) { DPRINTF(E_LOG, L_HTTPD, "Could not dereference %s: %s\n", path, strerror(errno)); - evhttp_send_error(req, HTTP_NOTFOUND, "Not Found"); + httpd_send_error(req, HTTP_NOTFOUND, "Not Found"); return; } @@ -954,7 +954,7 @@ serve_file(struct evhttp_request *req, char *uri) { DPRINTF(E_LOG, L_HTTPD, "Dereferenced path exceeds PATH_MAX: %s\n", path); - evhttp_send_error(req, HTTP_NOTFOUND, "Not Found"); + httpd_send_error(req, HTTP_NOTFOUND, "Not Found"); free(deref); return; @@ -968,7 +968,7 @@ serve_file(struct evhttp_request *req, char *uri) { DPRINTF(E_LOG, L_HTTPD, "Could not stat() %s: %s\n", path, strerror(errno)); - evhttp_send_error(req, HTTP_NOTFOUND, "Not Found"); + httpd_send_error(req, HTTP_NOTFOUND, "Not Found"); return; } @@ -983,7 +983,7 @@ serve_file(struct evhttp_request *req, char *uri) if (path_is_legal(path) != 0) { - evhttp_send_error(req, 403, "Forbidden"); + httpd_send_error(req, 403, "Forbidden"); return; } @@ -993,7 +993,7 @@ serve_file(struct evhttp_request *req, char *uri) { DPRINTF(E_LOG, L_HTTPD, "Could not create evbuffer\n"); - evhttp_send_error(req, HTTP_SERVUNAVAIL, "Internal error"); + httpd_send_error(req, HTTP_SERVUNAVAIL, "Internal error"); return; } @@ -1002,7 +1002,7 @@ serve_file(struct evhttp_request *req, char *uri) { DPRINTF(E_LOG, L_HTTPD, "Could not open %s: %s\n", path, strerror(errno)); - evhttp_send_error(req, HTTP_NOTFOUND, "Not Found"); + httpd_send_error(req, HTTP_NOTFOUND, "Not Found"); return; } @@ -1015,7 +1015,7 @@ serve_file(struct evhttp_request *req, char *uri) { DPRINTF(E_LOG, L_HTTPD, "Could not read file into evbuffer\n"); - evhttp_send_error(req, HTTP_SERVUNAVAIL, "Internal error"); + httpd_send_error(req, HTTP_SERVUNAVAIL, "Internal error"); return; } @@ -1299,21 +1299,21 @@ httpd_basic_auth(struct evhttp_request *req, char *user, char *passwd, char *rea header = (char *)malloc(len); if (!header) { - evhttp_send_error(req, HTTP_SERVUNAVAIL, "Internal Server Error"); + httpd_send_error(req, HTTP_SERVUNAVAIL, "Internal Server Error"); return -1; } ret = snprintf(header, len, "Basic realm=\"%s\"", realm); if ((ret < 0) || (ret >= len)) { - evhttp_send_error(req, HTTP_SERVUNAVAIL, "Internal Server Error"); + httpd_send_error(req, HTTP_SERVUNAVAIL, "Internal Server Error"); return -1; } evbuf = evbuffer_new(); if (!evbuf) { - evhttp_send_error(req, HTTP_SERVUNAVAIL, "Internal Server Error"); + httpd_send_error(req, HTTP_SERVUNAVAIL, "Internal Server Error"); return -1; } diff --git a/src/httpd_daap.c b/src/httpd_daap.c index ed7c3745..22d69f4d 100644 --- a/src/httpd_daap.c +++ b/src/httpd_daap.c @@ -282,7 +282,7 @@ daap_session_find(struct evhttp_request *req, struct evkeyvalq *query, struct ev return s; invalid: - evhttp_send_error(req, 403, "Forbidden"); + httpd_send_error(req, 403, "Forbidden"); return NULL; } @@ -954,7 +954,7 @@ daap_reply_login(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, { DPRINTF(E_LOG, L_DAAP, "Login attempt with U-A: Remote and no pairing-guid\n"); - evhttp_send_error(req, 403, "Forbidden"); + httpd_send_error(req, 403, "Forbidden"); return -1; } @@ -967,7 +967,7 @@ daap_reply_login(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, DPRINTF(E_LOG, L_DAAP, "Login attempt with invalid pairing-guid\n"); free_pi(&pi, 1); - evhttp_send_error(req, 403, "Forbidden"); + httpd_send_error(req, 403, "Forbidden"); return -1; } @@ -2378,7 +2378,7 @@ daap_reply_extra_data(struct evhttp_request *req, struct evbuffer *evbuf, char * ret = safe_atoi32(uri[3], &id); if (ret < 0) { - evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request"); + httpd_send_error(req, HTTP_BADREQUEST, "Bad Request"); return -1; } @@ -2390,7 +2390,7 @@ daap_reply_extra_data(struct evhttp_request *req, struct evbuffer *evbuf, char * { DPRINTF(E_LOG, L_DAAP, "Could not convert mw parameter to integer\n"); - evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request"); + httpd_send_error(req, HTTP_BADREQUEST, "Bad Request"); return -1; } @@ -2400,7 +2400,7 @@ daap_reply_extra_data(struct evhttp_request *req, struct evbuffer *evbuf, char * { DPRINTF(E_LOG, L_DAAP, "Could not convert mh parameter to integer\n"); - evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request"); + httpd_send_error(req, HTTP_BADREQUEST, "Bad Request"); return -1; } } @@ -2458,7 +2458,7 @@ daap_stream(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, stru ret = safe_atoi32(uri[3], &id); if (ret < 0) - evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request"); + httpd_send_error(req, HTTP_BADREQUEST, "Bad Request"); else httpd_stream_file(req, id); @@ -2710,7 +2710,7 @@ daap_request(struct evhttp_request *req) full_uri = httpd_fixup_uri(req); if (!full_uri) { - evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request"); + httpd_send_error(req, HTTP_BADREQUEST, "Bad Request"); return; } @@ -2718,7 +2718,7 @@ daap_request(struct evhttp_request *req) if (!ptr) { free(full_uri); - evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request"); + httpd_send_error(req, HTTP_BADREQUEST, "Bad Request"); return; } @@ -2729,7 +2729,7 @@ daap_request(struct evhttp_request *req) if (!uri) { - evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request"); + httpd_send_error(req, HTTP_BADREQUEST, "Bad Request"); return; } @@ -2740,7 +2740,7 @@ daap_request(struct evhttp_request *req) if (!uri) { free(full_uri); - evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request"); + httpd_send_error(req, HTTP_BADREQUEST, "Bad Request"); return; } @@ -2761,7 +2761,7 @@ daap_request(struct evhttp_request *req) { DPRINTF(E_LOG, L_DAAP, "Unrecognized DAAP request\n"); - evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request"); + httpd_send_error(req, HTTP_BADREQUEST, "Bad Request"); free(uri); free(full_uri); @@ -2819,7 +2819,7 @@ daap_request(struct evhttp_request *req) { DPRINTF(E_LOG, L_DAAP, "DAAP URI has too many/few components (%d)\n", (uri_parts[0]) ? i : 0); - evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request"); + httpd_send_error(req, HTTP_BADREQUEST, "Bad Request"); free(uri); free(full_uri); @@ -2841,7 +2841,7 @@ daap_request(struct evhttp_request *req) { DPRINTF(E_LOG, L_DAAP, "Could not allocate evbuffer for DAAP reply\n"); - evhttp_send_error(req, HTTP_SERVUNAVAIL, "Internal Server Error"); + httpd_send_error(req, HTTP_SERVUNAVAIL, "Internal Server Error"); free(uri); free(full_uri); diff --git a/src/httpd_dacp.c b/src/httpd_dacp.c index 9b81898d..a5b08fbb 100644 --- a/src/httpd_dacp.c +++ b/src/httpd_dacp.c @@ -1286,7 +1286,7 @@ dacp_reply_playspec(struct evhttp_request *req, struct evbuffer *evbuf, char **u return; out_fail: - evhttp_send_error(req, 500, "Internal Server Error"); + httpd_send_error(req, 500, "Internal Server Error"); } static void @@ -1328,7 +1328,7 @@ dacp_reply_playpause(struct evhttp_request *req, struct evbuffer *evbuf, char ** { DPRINTF(E_LOG, L_DACP, "Player returned an error for start after pause\n"); - evhttp_send_error(req, 500, "Internal Server Error"); + httpd_send_error(req, 500, "Internal Server Error"); return; } } @@ -1352,7 +1352,7 @@ dacp_reply_nextitem(struct evhttp_request *req, struct evbuffer *evbuf, char **u { DPRINTF(E_LOG, L_DACP, "Player returned an error for nextitem\n"); - evhttp_send_error(req, 500, "Internal Server Error"); + httpd_send_error(req, 500, "Internal Server Error"); return; } @@ -1361,7 +1361,7 @@ dacp_reply_nextitem(struct evhttp_request *req, struct evbuffer *evbuf, char **u { DPRINTF(E_LOG, L_DACP, "Player returned an error for start after nextitem\n"); - evhttp_send_error(req, 500, "Internal Server Error"); + httpd_send_error(req, 500, "Internal Server Error"); return; } @@ -1384,7 +1384,7 @@ dacp_reply_previtem(struct evhttp_request *req, struct evbuffer *evbuf, char **u { DPRINTF(E_LOG, L_DACP, "Player returned an error for previtem\n"); - evhttp_send_error(req, 500, "Internal Server Error"); + httpd_send_error(req, 500, "Internal Server Error"); return; } @@ -1393,7 +1393,7 @@ dacp_reply_previtem(struct evhttp_request *req, struct evbuffer *evbuf, char **u { DPRINTF(E_LOG, L_DACP, "Player returned an error for start after previtem\n"); - evhttp_send_error(req, 500, "Internal Server Error"); + httpd_send_error(req, 500, "Internal Server Error"); return; } @@ -2023,7 +2023,7 @@ dacp_reply_playstatusupdate(struct evhttp_request *req, struct evbuffer *evbuf, { ret = make_playstatusupdate(evbuf); if (ret < 0) - evhttp_send_error(req, 500, "Internal Server Error"); + httpd_send_error(req, 500, "Internal Server Error"); else httpd_send_reply(req, HTTP_OK, "OK", evbuf, 0); @@ -2076,7 +2076,7 @@ dacp_reply_nowplayingartwork(struct evhttp_request *req, struct evbuffer *evbuf, { DPRINTF(E_LOG, L_DACP, "Request for artwork without mw parameter\n"); - evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request"); + httpd_send_error(req, HTTP_BADREQUEST, "Bad Request"); return; } @@ -2085,7 +2085,7 @@ dacp_reply_nowplayingartwork(struct evhttp_request *req, struct evbuffer *evbuf, { DPRINTF(E_LOG, L_DACP, "Could not convert mw parameter to integer\n"); - evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request"); + httpd_send_error(req, HTTP_BADREQUEST, "Bad Request"); return; } @@ -2094,7 +2094,7 @@ dacp_reply_nowplayingartwork(struct evhttp_request *req, struct evbuffer *evbuf, { DPRINTF(E_LOG, L_DACP, "Request for artwork without mh parameter\n"); - evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request"); + httpd_send_error(req, HTTP_BADREQUEST, "Bad Request"); return; } @@ -2103,7 +2103,7 @@ dacp_reply_nowplayingartwork(struct evhttp_request *req, struct evbuffer *evbuf, { DPRINTF(E_LOG, L_DACP, "Could not convert mh parameter to integer\n"); - evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request"); + httpd_send_error(req, HTTP_BADREQUEST, "Bad Request"); return; } @@ -2141,7 +2141,7 @@ dacp_reply_nowplayingartwork(struct evhttp_request *req, struct evbuffer *evbuf, return; no_artwork: - evhttp_send_error(req, HTTP_NOTFOUND, "Not Found"); + httpd_send_error(req, HTTP_NOTFOUND, "Not Found"); } static void @@ -2376,7 +2376,7 @@ dacp_reply_setspeakers(struct evhttp_request *req, struct evbuffer *evbuf, char { DPRINTF(E_LOG, L_DACP, "Missing speaker-id parameter in DACP setspeakers request\n"); - evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request"); + httpd_send_error(req, HTTP_BADREQUEST, "Bad Request"); return; } @@ -2396,7 +2396,7 @@ dacp_reply_setspeakers(struct evhttp_request *req, struct evbuffer *evbuf, char { DPRINTF(E_LOG, L_DACP, "Out of memory for speaker ids\n"); - evhttp_send_error(req, HTTP_SERVUNAVAIL, "Internal Server Error"); + httpd_send_error(req, HTTP_SERVUNAVAIL, "Internal Server Error"); return; } @@ -2436,9 +2436,9 @@ dacp_reply_setspeakers(struct evhttp_request *req, struct evbuffer *evbuf, char /* Password problem */ if (ret == -2) - evhttp_send_error(req, 902, ""); + httpd_send_error(req, 902, ""); else - evhttp_send_error(req, 500, "Internal Server Error"); + httpd_send_error(req, 500, "Internal Server Error"); return; } @@ -2547,7 +2547,7 @@ dacp_request(struct evhttp_request *req) full_uri = httpd_fixup_uri(req); if (!full_uri) { - evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request"); + httpd_send_error(req, HTTP_BADREQUEST, "Bad Request"); return; } @@ -2559,7 +2559,7 @@ dacp_request(struct evhttp_request *req) if (!uri) { free(full_uri); - evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request"); + httpd_send_error(req, HTTP_BADREQUEST, "Bad Request"); return; } @@ -2587,7 +2587,7 @@ dacp_request(struct evhttp_request *req) { DPRINTF(E_LOG, L_DACP, "Unrecognized DACP request\n"); - evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request"); + httpd_send_error(req, HTTP_BADREQUEST, "Bad Request"); free(uri); free(full_uri); @@ -2608,7 +2608,7 @@ dacp_request(struct evhttp_request *req) { DPRINTF(E_LOG, L_DACP, "DACP URI has too many/few components (%d)\n", (uri_parts[0]) ? i : 0); - evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request"); + httpd_send_error(req, HTTP_BADREQUEST, "Bad Request"); free(uri); free(full_uri); @@ -2620,7 +2620,7 @@ dacp_request(struct evhttp_request *req) { DPRINTF(E_LOG, L_DACP, "Could not allocate evbuffer for DACP reply\n"); - evhttp_send_error(req, HTTP_SERVUNAVAIL, "Internal Server Error"); + httpd_send_error(req, HTTP_SERVUNAVAIL, "Internal Server Error"); free(uri); free(full_uri); diff --git a/src/httpd_rsp.c b/src/httpd_rsp.c index fd65a7f1..77b9b5ce 100644 --- a/src/httpd_rsp.c +++ b/src/httpd_rsp.c @@ -251,7 +251,7 @@ rsp_send_error(struct evhttp_request *req, char *errmsg) if (!evbuf) { - evhttp_send_error(req, HTTP_SERVUNAVAIL, "Internal Server Error"); + httpd_send_error(req, HTTP_SERVUNAVAIL, "Internal Server Error"); return; } @@ -747,7 +747,7 @@ rsp_stream(struct evhttp_request *req, char **uri, struct evkeyvalq *query) ret = safe_atoi32(uri[2], &id); if (ret < 0) - evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request"); + httpd_send_error(req, HTTP_BADREQUEST, "Bad Request"); else httpd_stream_file(req, id); } diff --git a/src/mpd.c b/src/mpd.c index 41346d7c..95a75f3d 100644 --- a/src/mpd.c +++ b/src/mpd.c @@ -4535,7 +4535,7 @@ artwork_cb(struct evhttp_request *req, void *arg) if (evhttp_request_get_command(req) != EVHTTP_REQ_GET) { DPRINTF(E_LOG, L_MPD, "Unsupported request type for artwork\n"); - evhttp_send_error(req, HTTP_BADMETHOD, "Method not allowed"); + httpd_send_error(req, HTTP_BADMETHOD, "Method not allowed"); return; } @@ -4546,7 +4546,7 @@ artwork_cb(struct evhttp_request *req, void *arg) if (!decoded) { DPRINTF(E_LOG, L_MPD, "Bad artwork request with uri '%s'\n", uri); - evhttp_send_error(req, HTTP_BADREQUEST, 0); + httpd_send_error(req, HTTP_BADREQUEST, 0); return; } @@ -4554,7 +4554,7 @@ artwork_cb(struct evhttp_request *req, void *arg) if (!path) { DPRINTF(E_LOG, L_MPD, "Invalid path from artwork request with uri '%s'\n", uri); - evhttp_send_error(req, HTTP_BADREQUEST, 0); + httpd_send_error(req, HTTP_BADREQUEST, 0); evhttp_uri_free(decoded); return; } @@ -4563,7 +4563,7 @@ artwork_cb(struct evhttp_request *req, void *arg) if (!decoded_path) { DPRINTF(E_LOG, L_MPD, "Error decoding path from artwork request with uri '%s'\n", uri); - evhttp_send_error(req, HTTP_BADREQUEST, 0); + httpd_send_error(req, HTTP_BADREQUEST, 0); evhttp_uri_free(decoded); return; } @@ -4578,7 +4578,7 @@ artwork_cb(struct evhttp_request *req, void *arg) if (!itemid) { DPRINTF(E_WARN, L_MPD, "No item found for path '%s' from request uri '%s'\n", decoded_path, uri); - evhttp_send_error(req, HTTP_NOTFOUND, "Document was not found"); + httpd_send_error(req, HTTP_NOTFOUND, "Document was not found"); evhttp_uri_free(decoded); free(decoded_path); return; @@ -4588,7 +4588,7 @@ artwork_cb(struct evhttp_request *req, void *arg) if (!evbuffer) { DPRINTF(E_LOG, L_MPD, "Could not allocate an evbuffer for artwork request\n"); - evhttp_send_error(req, HTTP_INTERNAL, "Document was not found"); + httpd_send_error(req, HTTP_INTERNAL, "Document was not found"); evhttp_uri_free(decoded); free(decoded_path); return; @@ -4597,7 +4597,7 @@ artwork_cb(struct evhttp_request *req, void *arg) format = artwork_get_item(evbuffer, itemid, 600, 600); if (format < 0) { - evhttp_send_error(req, HTTP_NOTFOUND, "Document was not found"); + httpd_send_error(req, HTTP_NOTFOUND, "Document was not found"); } else { From 57945a592c17d09ba3cfa16f6b990d1c00d0fc96 Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Tue, 18 Oct 2016 23:04:51 +0200 Subject: [PATCH 10/13] [httpd] Reply to CORS preflight requests --- src/httpd.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/httpd.c b/src/httpd.c index d30fbfd8..dc6d63d1 100644 --- a/src/httpd.c +++ b/src/httpd.c @@ -1045,10 +1045,29 @@ serve_file(struct evhttp_request *req, char *uri) static void httpd_gen_cb(struct evhttp_request *req, void *arg) { + struct evkeyvalq *input_headers; + struct evkeyvalq *output_headers; const char *req_uri; char *uri; char *ptr; + // Did we get a CORS preflight request? + input_headers = evhttp_request_get_input_headers(req); + if (allow_origin && (evhttp_request_get_command(req) == EVHTTP_REQ_OPTIONS) && evhttp_find_header(input_headers, "Origin")) + { + output_headers = evhttp_request_get_output_headers(req); + + evhttp_add_header(output_headers, "Access-Control-Allow-Origin", allow_origin); + + // Allow only GET method and authorization header in cross origin requests + evhttp_add_header(output_headers, "Access-Control-Allow-Method", "GET"); + evhttp_add_header(output_headers, "Access-Control-Allow-Headers", "authorization"); + + // In this case there is no reason to go through httpd_send_reply + evhttp_send_reply(req, HTTP_OK, "OK", NULL); + return; + } + req_uri = evhttp_request_get_uri(req); if (!req_uri) { From 54a09fce6330b5a3a83fe14f602bb924ebdfb850 Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Wed, 19 Oct 2016 17:29:22 +0200 Subject: [PATCH 11/13] [httpd] Add requirement for Access-Control-Request-Method for preflight CORS --- src/httpd.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/httpd.c b/src/httpd.c index dc6d63d1..1567f1e3 100644 --- a/src/httpd.c +++ b/src/httpd.c @@ -1053,7 +1053,10 @@ httpd_gen_cb(struct evhttp_request *req, void *arg) // Did we get a CORS preflight request? input_headers = evhttp_request_get_input_headers(req); - if (allow_origin && (evhttp_request_get_command(req) == EVHTTP_REQ_OPTIONS) && evhttp_find_header(input_headers, "Origin")) + if ( input_headers && allow_origin && + (evhttp_request_get_command(req) == EVHTTP_REQ_OPTIONS) && + evhttp_find_header(input_headers, "Origin") && + evhttp_find_header(input_headers, "Access-Control-Request-Method") ) { output_headers = evhttp_request_get_output_headers(req); From 2ce1c8d28ff8b00f84e82fa6a9a3a585562c8d6d Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Wed, 19 Oct 2016 21:22:14 +0200 Subject: [PATCH 12/13] [httpd] Enable OPTIONS for evhttp (if CORS headers are enabled) --- src/httpd.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/httpd.c b/src/httpd.c index 1567f1e3..642bdee6 100644 --- a/src/httpd.c +++ b/src/httpd.c @@ -1442,8 +1442,13 @@ httpd_init(void) // For CORS headers allow_origin = cfg_getstr(cfg_getsec(cfg, "general"), "allow_origin"); - if (allow_origin && (strlen(allow_origin) == 0)) - allow_origin = NULL; + if (allow_origin) + { + if (strlen(allow_origin) != 0) + evhttp_set_allowed_methods(evhttpd, EVHTTP_REQ_GET | EVHTTP_REQ_POST | EVHTTP_REQ_OPTIONS); + else + allow_origin = NULL; + } if (v6enabled) { From c0d07ae93b19024cde43ff47cccbe44287b1090f Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Wed, 19 Oct 2016 21:26:13 +0200 Subject: [PATCH 13/13] [httpd] Allow HEAD for evhttp requests just to be safe --- src/httpd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/httpd.c b/src/httpd.c index 642bdee6..800c5e15 100644 --- a/src/httpd.c +++ b/src/httpd.c @@ -816,7 +816,7 @@ httpd_send_error(struct evhttp_request* req, int error, const char* reason) evhttp_send_error(req, error, reason); return; } - + output_headers = evhttp_request_get_output_headers(req); evhttp_clear_headers(output_headers); @@ -1443,9 +1443,9 @@ httpd_init(void) // For CORS headers allow_origin = cfg_getstr(cfg_getsec(cfg, "general"), "allow_origin"); if (allow_origin) - { + { if (strlen(allow_origin) != 0) - evhttp_set_allowed_methods(evhttpd, EVHTTP_REQ_GET | EVHTTP_REQ_POST | EVHTTP_REQ_OPTIONS); + evhttp_set_allowed_methods(evhttpd, EVHTTP_REQ_GET | EVHTTP_REQ_POST | EVHTTP_REQ_HEAD | EVHTTP_REQ_OPTIONS); else allow_origin = NULL; }