[httpd] Remove all traces of evhttp from httpd modules

This commit is contained in:
ejurgensen 2022-12-21 22:56:34 +01:00
parent 2b4ff98652
commit db25a714bd
10 changed files with 563 additions and 363 deletions

View File

@ -93,6 +93,7 @@ owntone_SOURCES = main.c \
$(MDNS_SRC) mdns.h \ $(MDNS_SRC) mdns.h \
remote_pairing.c remote_pairing.h \ remote_pairing.c remote_pairing.h \
httpd.c httpd.h httpd_internal.h \ httpd.c httpd.h httpd_internal.h \
httpd_libevhttp.c \
httpd_rsp.c \ httpd_rsp.c \
httpd_daap.c httpd_daap.h \ httpd_daap.c httpd_daap.h \
httpd_dacp.c \ httpd_dacp.c \

View File

@ -296,7 +296,6 @@ request_set(struct httpd_request *hreq, struct evhttp_request *req, struct httpd
{ {
; ;
struct evhttp_connection *evcon; struct evhttp_connection *evcon;
struct evkeyvalq *headers;
struct httpd_uri_map *uri; struct httpd_uri_map *uri;
int req_method; int req_method;
int ret; int ret;
@ -312,8 +311,9 @@ request_set(struct httpd_request *hreq, struct evhttp_request *req, struct httpd
if (req) if (req)
{ {
headers = evhttp_request_get_input_headers(req); hreq->in_body = evhttp_request_get_input_buffer(req);
hreq->user_agent = evhttp_find_header(headers, "User-Agent"); hreq->in_headers = evhttp_request_get_input_headers(req);
hreq->user_agent = evhttp_find_header(hreq->in_headers, "User-Agent");
evcon = evhttp_request_get_connection(req); evcon = evhttp_request_get_connection(req);
if (evcon) if (evcon)

View File

@ -40,20 +40,20 @@ request_process(struct httpd_request *hreq, uint32_t *max_w, uint32_t *max_h)
*max_w = 0; *max_w = 0;
*max_h = 0; *max_h = 0;
param = evhttp_find_header(hreq->query, "maxwidth"); param = httpd_query_value_find(hreq->query, "maxwidth");
if (param) if (param)
{ {
ret = safe_atou32(param, max_w); ret = safe_atou32(param, max_w);
if (ret < 0) if (ret < 0)
DPRINTF(E_LOG, L_WEB, "Invalid width in request: '%s'\n", hreq->uri_parsed->uri); DPRINTF(E_LOG, L_WEB, "Invalid width in request: '%s'\n", hreq->uri);
} }
param = evhttp_find_header(hreq->query, "maxheight"); param = httpd_query_value_find(hreq->query, "maxheight");
if (param) if (param)
{ {
ret = safe_atou32(param, max_h); ret = safe_atou32(param, max_h);
if (ret < 0) if (ret < 0)
DPRINTF(E_LOG, L_WEB, "Invalid height in request: '%s'\n", hreq->uri_parsed->uri); DPRINTF(E_LOG, L_WEB, "Invalid height in request: '%s'\n", hreq->uri);
} }
return 0; return 0;
@ -62,14 +62,14 @@ request_process(struct httpd_request *hreq, uint32_t *max_w, uint32_t *max_h)
static int static int
response_process(struct httpd_request *hreq, int format) response_process(struct httpd_request *hreq, int format)
{ {
struct evkeyvalq *headers; httpd_headers *headers;
headers = evhttp_request_get_output_headers(hreq->req); headers = httpd_request_output_headers_get(hreq);
if (format == ART_FMT_PNG) if (format == ART_FMT_PNG)
evhttp_add_header(headers, "Content-Type", "image/png"); httpd_header_add(headers, "Content-Type", "image/png");
else if (format == ART_FMT_JPEG) else if (format == ART_FMT_JPEG)
evhttp_add_header(headers, "Content-Type", "image/jpeg"); httpd_header_add(headers, "Content-Type", "image/jpeg");
else else
return HTTP_NOCONTENT; return HTTP_NOCONTENT;
@ -141,9 +141,9 @@ artworkapi_reply_group(struct httpd_request *hreq)
static struct httpd_uri_map artworkapi_handlers[] = static struct httpd_uri_map artworkapi_handlers[] =
{ {
{ EVHTTP_REQ_GET, "^/artwork/nowplaying$", artworkapi_reply_nowplaying }, { HTTPD_METHOD_GET, "^/artwork/nowplaying$", artworkapi_reply_nowplaying },
{ EVHTTP_REQ_GET, "^/artwork/item/[[:digit:]]+$", artworkapi_reply_item }, { HTTPD_METHOD_GET, "^/artwork/item/[[:digit:]]+$", artworkapi_reply_item },
{ EVHTTP_REQ_GET, "^/artwork/group/[[:digit:]]+$", artworkapi_reply_group }, { HTTPD_METHOD_GET, "^/artwork/group/[[:digit:]]+$", artworkapi_reply_group },
{ 0, NULL, NULL } { 0, NULL, NULL }
}; };

View File

@ -286,7 +286,6 @@ static void
update_refresh_cb(int fd, short event, void *arg) update_refresh_cb(int fd, short event, void *arg)
{ {
struct daap_update_request *ur; struct daap_update_request *ur;
struct evhttp_connection *evcon;
struct evbuffer *reply; struct evbuffer *reply;
ur = (struct daap_update_request *)arg; ur = (struct daap_update_request *)arg;
@ -301,8 +300,7 @@ update_refresh_cb(int fd, short event, void *arg)
dmap_add_int(reply, "mstt", 200); /* 12 */ dmap_add_int(reply, "mstt", 200); /* 12 */
dmap_add_int(reply, "musr", current_rev); /* 12 */ dmap_add_int(reply, "musr", current_rev); /* 12 */
evcon = evhttp_request_get_connection(ur->hreq->req); httpd_request_closecb_set(ur->hreq, NULL, NULL);
evhttp_connection_set_closecb(evcon, NULL, NULL);
httpd_send_reply(ur->hreq, HTTP_OK, "OK", reply, 0); httpd_send_reply(ur->hreq, HTTP_OK, "OK", reply, 0);
@ -310,20 +308,17 @@ update_refresh_cb(int fd, short event, void *arg)
} }
static void static void
update_fail_cb(struct evhttp_connection *evcon, void *arg) update_fail_cb(httpd_connection *conn, void *arg)
{ {
struct evhttp_connection *evc;
struct daap_update_request *ur; struct daap_update_request *ur;
ur = (struct daap_update_request *)arg; ur = (struct daap_update_request *)arg;
DPRINTF(E_DBG, L_DAAP, "Update request: client closed connection\n"); DPRINTF(E_DBG, L_DAAP, "Update request: client closed connection\n");
evc = evhttp_request_get_connection(ur->hreq->req); httpd_request_closecb_set(ur->hreq, NULL, NULL);
if (evc)
evhttp_connection_set_closecb(evc, NULL, NULL);
evhttp_request_free(ur->hreq->req); httpd_request_backend_free(ur->hreq); // TODO check if still necessary
update_remove(ur); update_remove(ur);
} }
@ -513,7 +508,7 @@ query_params_set(struct query_params *qp, int *sort_headers, struct httpd_reques
memset(qp, 0, sizeof(struct query_params)); memset(qp, 0, sizeof(struct query_params));
param = evhttp_find_header(hreq->query, "index"); param = httpd_query_value_find(hreq->query, "index");
if (param) if (param)
{ {
if (param[0] == '-') /* -n, last n entries */ if (param[0] == '-') /* -n, last n entries */
@ -559,7 +554,7 @@ query_params_set(struct query_params *qp, int *sort_headers, struct httpd_reques
qp->idx_type = I_SUB; qp->idx_type = I_SUB;
qp->sort = S_NONE; qp->sort = S_NONE;
param = evhttp_find_header(hreq->query, "sort"); param = httpd_query_value_find(hreq->query, "sort");
if (param) if (param)
{ {
if (strcmp(param, "name") == 0) if (strcmp(param, "name") == 0)
@ -580,7 +575,7 @@ query_params_set(struct query_params *qp, int *sort_headers, struct httpd_reques
if (sort_headers) if (sort_headers)
{ {
*sort_headers = 0; *sort_headers = 0;
param = evhttp_find_header(hreq->query, "include-sort-headers"); param = httpd_query_value_find(hreq->query, "include-sort-headers");
if (param && (strcmp(param, "1") == 0)) if (param && (strcmp(param, "1") == 0))
{ {
*sort_headers = 1; *sort_headers = 1;
@ -588,9 +583,9 @@ query_params_set(struct query_params *qp, int *sort_headers, struct httpd_reques
} }
} }
param = evhttp_find_header(hreq->query, "query"); param = httpd_query_value_find(hreq->query, "query");
if (!param) if (!param)
param = evhttp_find_header(hreq->query, "filter"); param = httpd_query_value_find(hreq->query, "filter");
if (param) if (param)
{ {
@ -721,7 +716,7 @@ daap_request_authorize(struct httpd_request *hreq)
if (session->is_remote && (strcmp(hreq->uri_parsed->path, "/login") == 0)) if (session->is_remote && (strcmp(hreq->uri_parsed->path, "/login") == 0))
return 0; return 0;
param = evhttp_find_header(hreq->query, "session-id"); param = httpd_query_value_find(hreq->query, "session-id");
if (param) if (param)
{ {
if (session->id == 0) if (session->id == 0)
@ -764,13 +759,12 @@ daap_request_authorize(struct httpd_request *hreq)
/* --------------------------- REPLY HANDLERS ------------------------------- */ /* --------------------------- REPLY HANDLERS ------------------------------- */
/* Note that some handlers can be called without a connection (needed for */ /* Note that some handlers can be called without a connection (needed for */
/* cache regeneration), while others cannot. Those that cannot should check */ /* cache regeneration), while others cannot. Those that cannot should check */
/* that hreq->req is not a null pointer. */ /* that httpd_request_connection_get(hreq) is not null. */
static enum daap_reply_result static enum daap_reply_result
daap_reply_server_info(struct httpd_request *hreq) daap_reply_server_info(struct httpd_request *hreq)
{ {
struct evbuffer *content; struct evbuffer *content;
struct evkeyvalq *headers;
char *name; char *name;
char *passwd; char *passwd;
const char *clientver; const char *clientver;
@ -778,7 +772,7 @@ daap_reply_server_info(struct httpd_request *hreq)
int mpro; int mpro;
int apro; int apro;
if (!hreq->req) if (!httpd_request_connection_get(hreq))
{ {
DPRINTF(E_LOG, L_DAAP, "Bug! daap_reply_server_info() cannot be called without an actual connection\n"); DPRINTF(E_LOG, L_DAAP, "Bug! daap_reply_server_info() cannot be called without an actual connection\n");
return DAAP_REPLY_NO_CONNECTION; return DAAP_REPLY_NO_CONNECTION;
@ -793,8 +787,7 @@ daap_reply_server_info(struct httpd_request *hreq)
mpro = 2 << 16 | 10; mpro = 2 << 16 | 10;
apro = 3 << 16 | 12; apro = 3 << 16 | 12;
headers = evhttp_request_get_input_headers(hreq->req); if (hreq->in_headers && (clientver = httpd_header_find(hreq->in_headers, "Client-DAAP-Version")))
if (headers && (clientver = evhttp_find_header(headers, "Client-DAAP-Version")))
{ {
if (strcmp(clientver, "1.0") == 0) if (strcmp(clientver, "1.0") == 0)
{ {
@ -917,7 +910,7 @@ daap_reply_login(struct httpd_request *hreq)
CHECK_ERR(L_DAAP, evbuffer_expand(hreq->reply, 32)); CHECK_ERR(L_DAAP, evbuffer_expand(hreq->reply, 32));
param = evhttp_find_header(hreq->query, "pairing-guid"); param = httpd_query_value_find(hreq->query, "pairing-guid");
if (param && !net_peer_address_is_trusted(hreq->peer_address)) if (param && !net_peer_address_is_trusted(hreq->peer_address))
{ {
if (strlen(param) < 3) if (strlen(param) < 3)
@ -948,7 +941,7 @@ daap_reply_login(struct httpd_request *hreq)
DPRINTF(E_INFO, L_DAAP, "Client (unknown user-agent) logging in from %s\n", hreq->peer_address); DPRINTF(E_INFO, L_DAAP, "Client (unknown user-agent) logging in from %s\n", hreq->peer_address);
} }
param = evhttp_find_header(hreq->query, "request-session-id"); param = httpd_query_value_find(hreq->query, "request-session-id");
if (param) if (param)
{ {
ret = safe_atoi32(param, &request_session_id); ret = safe_atoi32(param, &request_session_id);
@ -992,18 +985,17 @@ static enum daap_reply_result
daap_reply_update(struct httpd_request *hreq) daap_reply_update(struct httpd_request *hreq)
{ {
struct daap_update_request *ur; struct daap_update_request *ur;
struct evhttp_connection *evcon;
const char *param; const char *param;
int reqd_rev; int reqd_rev;
int ret; int ret;
if (!hreq->req) if (!httpd_request_connection_get(hreq))
{ {
DPRINTF(E_LOG, L_DAAP, "Bug! daap_reply_update() cannot be called without an actual connection\n"); DPRINTF(E_LOG, L_DAAP, "Bug! daap_reply_update() cannot be called without an actual connection\n");
return DAAP_REPLY_NO_CONNECTION; return DAAP_REPLY_NO_CONNECTION;
} }
param = evhttp_find_header(hreq->query, "revision-number"); param = httpd_query_value_find(hreq->query, "revision-number");
if (!param) if (!param)
{ {
DPRINTF(E_DBG, L_DAAP, "Missing revision-number in client update request\n"); DPRINTF(E_DBG, L_DAAP, "Missing revision-number in client update request\n");
@ -1070,9 +1062,7 @@ daap_reply_update(struct httpd_request *hreq)
/* If the connection fails before we have an update to push out /* If the connection fails before we have an update to push out
* to the client, we need to know. * to the client, we need to know.
*/ */
evcon = evhttp_request_get_connection(hreq->req); httpd_request_closecb_set(hreq, update_fail_cb, ur);
if (evcon)
evhttp_connection_set_closecb(evcon, update_fail_cb, ur);
return DAAP_REPLY_NONE; return DAAP_REPLY_NONE;
} }
@ -1165,7 +1155,6 @@ daap_reply_songlist_generic(struct httpd_request *hreq, int playlist)
struct db_media_file_info dbmfi; struct db_media_file_info dbmfi;
struct evbuffer *song; struct evbuffer *song;
struct evbuffer *songlist; struct evbuffer *songlist;
struct evkeyvalq *headers;
struct daap_session *s; struct daap_session *s;
const struct dmap_field **meta = NULL; const struct dmap_field **meta = NULL;
struct sort_ctx *sctx; struct sort_ctx *sctx;
@ -1210,7 +1199,7 @@ daap_reply_songlist_generic(struct httpd_request *hreq, int playlist)
CHECK_ERR(L_DAAP, evbuffer_expand(songlist, 4096)); CHECK_ERR(L_DAAP, evbuffer_expand(songlist, 4096));
CHECK_ERR(L_DAAP, evbuffer_expand(song, 512)); CHECK_ERR(L_DAAP, evbuffer_expand(song, 512));
param = evhttp_find_header(hreq->query, "meta"); param = httpd_query_value_find(hreq->query, "meta");
if (!param) if (!param)
{ {
DPRINTF(E_DBG, L_DAAP, "No meta parameter in query, using default\n"); DPRINTF(E_DBG, L_DAAP, "No meta parameter in query, using default\n");
@ -1239,10 +1228,9 @@ daap_reply_songlist_generic(struct httpd_request *hreq, int playlist)
} }
client_codecs = NULL; client_codecs = NULL;
if (!s->is_remote && hreq->req) if (!s->is_remote && hreq->in_headers)
{ {
headers = evhttp_request_get_input_headers(hreq->req); client_codecs = httpd_header_find(hreq->in_headers, "Accept-Codecs");
client_codecs = evhttp_find_header(headers, "Accept-Codecs");
} }
nsongs = 0; nsongs = 0;
@ -1428,7 +1416,7 @@ daap_reply_playlists(struct httpd_request *hreq)
CHECK_ERR(L_DAAP, evbuffer_expand(playlistlist, 1024)); CHECK_ERR(L_DAAP, evbuffer_expand(playlistlist, 1024));
CHECK_ERR(L_DAAP, evbuffer_expand(playlist, 128)); CHECK_ERR(L_DAAP, evbuffer_expand(playlist, 128));
param = evhttp_find_header(hreq->query, "meta"); param = httpd_query_value_find(hreq->query, "meta");
if (!param) if (!param)
{ {
DPRINTF(E_LOG, L_DAAP, "No meta parameter in query, using default\n"); DPRINTF(E_LOG, L_DAAP, "No meta parameter in query, using default\n");
@ -1626,7 +1614,7 @@ daap_reply_groups(struct httpd_request *hreq)
int i; int i;
int ret; int ret;
param = evhttp_find_header(hreq->query, "group-type"); param = httpd_query_value_find(hreq->query, "group-type");
if (param && strcmp(param, "artists") == 0) if (param && strcmp(param, "artists") == 0)
{ {
// Request from Remote may have the form: // Request from Remote may have the form:
@ -1654,7 +1642,7 @@ daap_reply_groups(struct httpd_request *hreq)
CHECK_ERR(L_DAAP, evbuffer_expand(grouplist, 1024)); CHECK_ERR(L_DAAP, evbuffer_expand(grouplist, 1024));
CHECK_ERR(L_DAAP, evbuffer_expand(group, 128)); CHECK_ERR(L_DAAP, evbuffer_expand(group, 128));
param = evhttp_find_header(hreq->query, "meta"); param = httpd_query_value_find(hreq->query, "meta");
if (!param) if (!param)
{ {
DPRINTF(E_LOG, L_DAAP, "No meta parameter in query, using default\n"); DPRINTF(E_LOG, L_DAAP, "No meta parameter in query, using default\n");
@ -1947,7 +1935,7 @@ daap_reply_browse(struct httpd_request *hreq)
static enum daap_reply_result static enum daap_reply_result
daap_reply_extra_data(struct httpd_request *hreq) daap_reply_extra_data(struct httpd_request *hreq)
{ {
struct evkeyvalq *headers; httpd_headers *headers;
char clen[32]; char clen[32];
const char *param; const char *param;
char *ctype; char *ctype;
@ -1957,7 +1945,7 @@ daap_reply_extra_data(struct httpd_request *hreq)
int max_h; int max_h;
int ret; int ret;
if (!hreq->req) if (!httpd_request_connection_get(hreq))
{ {
DPRINTF(E_LOG, L_DAAP, "Bug! daap_reply_extra_data() cannot be called without an actual connection\n"); DPRINTF(E_LOG, L_DAAP, "Bug! daap_reply_extra_data() cannot be called without an actual connection\n");
return DAAP_REPLY_NO_CONNECTION; return DAAP_REPLY_NO_CONNECTION;
@ -1970,9 +1958,9 @@ daap_reply_extra_data(struct httpd_request *hreq)
return DAAP_REPLY_BAD_REQUEST; return DAAP_REPLY_BAD_REQUEST;
} }
if (evhttp_find_header(hreq->query, "mw") && evhttp_find_header(hreq->query, "mh")) if (httpd_query_value_find(hreq->query, "mw") && httpd_query_value_find(hreq->query, "mh"))
{ {
param = evhttp_find_header(hreq->query, "mw"); param = httpd_query_value_find(hreq->query, "mw");
ret = safe_atoi32(param, &max_w); ret = safe_atoi32(param, &max_w);
if (ret < 0) if (ret < 0)
{ {
@ -1980,7 +1968,7 @@ daap_reply_extra_data(struct httpd_request *hreq)
return DAAP_REPLY_BAD_REQUEST; return DAAP_REPLY_BAD_REQUEST;
} }
param = evhttp_find_header(hreq->query, "mh"); param = httpd_query_value_find(hreq->query, "mh");
ret = safe_atoi32(param, &max_h); ret = safe_atoi32(param, &max_h);
if (ret < 0) if (ret < 0)
{ {
@ -2020,11 +2008,11 @@ daap_reply_extra_data(struct httpd_request *hreq)
goto no_artwork; goto no_artwork;
} }
headers = evhttp_request_get_output_headers(hreq->req); headers = httpd_request_output_headers_get(hreq);
evhttp_remove_header(headers, "Content-Type"); httpd_header_remove(headers, "Content-Type");
evhttp_add_header(headers, "Content-Type", ctype); httpd_header_add(headers, "Content-Type", ctype);
snprintf(clen, sizeof(clen), "%ld", (long)len); snprintf(clen, sizeof(clen), "%ld", (long)len);
evhttp_add_header(headers, "Content-Length", clen); httpd_header_add(headers, "Content-Length", clen);
return DAAP_REPLY_OK_NO_GZIP; return DAAP_REPLY_OK_NO_GZIP;
@ -2038,7 +2026,7 @@ daap_stream(struct httpd_request *hreq)
int id; int id;
int ret; int ret;
if (!hreq->req) if (!httpd_request_connection_get(hreq))
{ {
DPRINTF(E_LOG, L_DAAP, "Bug! daap_stream() cannot be called without an actual connection\n"); DPRINTF(E_LOG, L_DAAP, "Bug! daap_stream() cannot be called without an actual connection\n");
return DAAP_REPLY_NO_CONNECTION; return DAAP_REPLY_NO_CONNECTION;
@ -2228,7 +2216,7 @@ static struct httpd_uri_map daap_handlers[] =
static void static void
daap_request(struct httpd_request *hreq) daap_request(struct httpd_request *hreq)
{ {
struct evkeyvalq *headers; httpd_headers *headers;
struct timespec start; struct timespec start;
struct timespec end; struct timespec end;
struct daap_session session; struct daap_session session;
@ -2248,7 +2236,7 @@ daap_request(struct httpd_request *hreq)
} }
// Check if we have a session and point hreq->extra_data to it // Check if we have a session and point hreq->extra_data to it
param = evhttp_find_header(hreq->query, "session-id"); param = httpd_query_value_find(hreq->query, "session-id");
if (param) if (param)
{ {
ret = safe_atoi32(param, &id); ret = safe_atoi32(param, &id);
@ -2262,7 +2250,7 @@ daap_request(struct httpd_request *hreq)
if (!hreq->extra_data) if (!hreq->extra_data)
{ {
memset(&session, 0, sizeof(struct daap_session)); memset(&session, 0, sizeof(struct daap_session));
session.is_remote = (evhttp_find_header(hreq->query, "pairing-guid") != NULL); session.is_remote = (httpd_query_value_find(hreq->query, "pairing-guid") != NULL);
hreq->extra_data = &session; hreq->extra_data = &session;
} }
@ -2273,13 +2261,13 @@ daap_request(struct httpd_request *hreq)
} }
// Set reply headers // Set reply headers
headers = evhttp_request_get_output_headers(hreq->req); headers = httpd_request_output_headers_get(hreq);
evhttp_add_header(headers, "Accept-Ranges", "bytes"); httpd_header_add(headers, "Accept-Ranges", "bytes");
evhttp_add_header(headers, "DAAP-Server", PACKAGE_NAME "/" VERSION); httpd_header_add(headers, "DAAP-Server", PACKAGE_NAME "/" VERSION);
// Content-Type for all replies, even the actual audio streaming. Note that // Content-Type for all replies, even the actual audio streaming. Note that
// video streaming will override this Content-Type with a more appropriate // video streaming will override this Content-Type with a more appropriate
// video/<type> Content-Type as expected by clients like Front Row. // video/<type> Content-Type as expected by clients like Front Row.
evhttp_add_header(headers, "Content-Type", "application/x-dmap-tagged"); httpd_header_add(headers, "Content-Type", "application/x-dmap-tagged");
// Now we create the actual reply // Now we create the actual reply
CHECK_NULL(L_DAAP, hreq->reply = evbuffer_new()); CHECK_NULL(L_DAAP, hreq->reply = evbuffer_new());
@ -2289,7 +2277,7 @@ daap_request(struct httpd_request *hreq)
if (ret == 0) if (ret == 0)
{ {
// The cache will return the data gzipped, so httpd_send_reply won't need to do it // 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_header_add(headers, "Content-Encoding", "gzip");
httpd_send_reply(hreq, HTTP_OK, "OK", hreq->reply, HTTPD_SEND_NO_GZIP); // TODO not all want this reply httpd_send_reply(hreq, HTTP_OK, "OK", hreq->reply, HTTPD_SEND_NO_GZIP); // TODO not all want this reply
evbuffer_free(hreq->reply); evbuffer_free(hreq->reply);
@ -2389,7 +2377,7 @@ daap_deinit(void)
{ {
struct daap_session *s; struct daap_session *s;
struct daap_update_request *ur; struct daap_update_request *ur;
struct evhttp_connection *evcon; httpd_connection *conn;
for (s = daap_sessions; daap_sessions; s = daap_sessions) for (s = daap_sessions; daap_sessions; s = daap_sessions)
{ {
@ -2401,11 +2389,11 @@ daap_deinit(void)
{ {
update_requests = ur->next; update_requests = ur->next;
evcon = evhttp_request_get_connection(ur->hreq->req); conn = httpd_request_connection_get(ur->hreq);
if (evcon) if (conn)
{ {
evhttp_connection_set_closecb(evcon, NULL, NULL); httpd_connection_closecb_set(conn, NULL, NULL);
evhttp_connection_free(evcon); httpd_connection_free(conn);
} }
update_free(ur); update_free(ur);

View File

@ -26,7 +26,6 @@
#include <fcntl.h> #include <fcntl.h>
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <sys/queue.h>
#include <sys/types.h> #include <sys/types.h>
#include <stdint.h> #include <stdint.h>
#include <inttypes.h> #include <inttypes.h>
@ -565,15 +564,13 @@ speaker_enum_cb(struct player_speaker_info *spk, void *arg)
static int static int
speaker_get(struct player_speaker_info *speaker_info, struct httpd_request *hreq, const char *req_name) speaker_get(struct player_speaker_info *speaker_info, struct httpd_request *hreq, const char *req_name)
{ {
struct evkeyvalq *headers;
const char *remote; const char *remote;
uint32_t active_remote; uint32_t active_remote;
int ret; int ret;
headers = evhttp_request_get_input_headers(hreq->req); remote = httpd_header_find(hreq->in_headers, "Active-Remote");
remote = evhttp_find_header(headers, "Active-Remote");
if (!headers || !remote || (safe_atou32(remote, &active_remote) < 0)) if (!remote || (safe_atou32(remote, &active_remote) < 0))
{ {
DPRINTF(E_LOG, L_DACP, "'%s' request from '%s' has invalid Active-Remote: '%s'\n", req_name, hreq->peer_address, remote); DPRINTF(E_LOG, L_DACP, "'%s' request from '%s' has invalid Active-Remote: '%s'\n", req_name, hreq->peer_address, remote);
return -1; return -1;
@ -633,7 +630,7 @@ dacp_request_authorize(struct httpd_request *hreq)
if (net_peer_address_is_trusted(hreq->peer_address)) if (net_peer_address_is_trusted(hreq->peer_address))
return 0; return 0;
param = evhttp_find_header(hreq->query, "session-id"); param = httpd_query_value_find(hreq->query, "session-id");
if (!param) if (!param)
{ {
DPRINTF(E_LOG, L_DACP, "No session-id specified in request\n"); DPRINTF(E_LOG, L_DACP, "No session-id specified in request\n");
@ -737,7 +734,6 @@ playstatusupdate_cb(int fd, short what, void *arg)
struct dacp_update_request *ur; struct dacp_update_request *ur;
struct evbuffer *evbuf; struct evbuffer *evbuf;
struct evbuffer *update; struct evbuffer *update;
struct evhttp_connection *evcon;
uint8_t *buf; uint8_t *buf;
size_t len; size_t len;
int ret; int ret;
@ -776,9 +772,7 @@ playstatusupdate_cb(int fd, short what, void *arg)
{ {
update_requests = ur->next; update_requests = ur->next;
evcon = evhttp_request_get_connection(ur->hreq->req); httpd_request_closecb_set(ur->hreq, NULL, NULL);
if (evcon)
evhttp_connection_set_closecb(evcon, NULL, NULL);
// Only copy buffer if we actually need to reuse it // Only copy buffer if we actually need to reuse it
if (ur->next) if (ur->next)
@ -822,19 +816,16 @@ dacp_playstatus_update_handler(short event_mask)
} }
static void static void
update_fail_cb(struct evhttp_connection *evcon, void *arg) update_fail_cb(httpd_connection *conn, void *arg)
{ {
struct dacp_update_request *ur; struct dacp_update_request *ur;
struct dacp_update_request *p; struct dacp_update_request *p;
struct evhttp_connection *evc;
ur = (struct dacp_update_request *)arg; ur = (struct dacp_update_request *)arg;
DPRINTF(E_DBG, L_DACP, "Update request: client closed connection\n"); DPRINTF(E_DBG, L_DACP, "Update request: client closed connection\n");
evc = evhttp_request_get_connection(ur->hreq->req); httpd_request_closecb_set(ur->hreq, NULL, NULL);
if (evc)
evhttp_connection_set_closecb(evc, NULL, NULL);
if (ur == update_requests) if (ur == update_requests)
update_requests = ur->next; update_requests = ur->next;
@ -852,7 +843,7 @@ update_fail_cb(struct evhttp_connection *evcon, void *arg)
p->next = ur->next; p->next = ur->next;
} }
evhttp_request_free(ur->hreq->req); httpd_request_backend_free(ur->hreq); // TODO check if still necessary
free(ur); free(ur);
} }
@ -978,7 +969,7 @@ dacp_propset_volume(const char *value, struct httpd_request *hreq)
return; return;
} }
param = evhttp_find_header(hreq->query, "speaker-id"); param = httpd_query_value_find(hreq->query, "speaker-id");
if (param) if (param)
{ {
ret = safe_atou64(param, &id); ret = safe_atou64(param, &id);
@ -993,7 +984,7 @@ dacp_propset_volume(const char *value, struct httpd_request *hreq)
return; return;
} }
param = evhttp_find_header(hreq->query, "include-speaker-id"); param = httpd_query_value_find(hreq->query, "include-speaker-id");
if (param) if (param)
{ {
ret = safe_atou64(param, &id); ret = safe_atou64(param, &id);
@ -1125,9 +1116,9 @@ dacp_propset_userrating(const char *value, struct httpd_request *hreq)
return; return;
} }
param = evhttp_find_header(hreq->query, "item-spec"); // Remote param = httpd_query_value_find(hreq->query, "item-spec"); // Remote
if (!param) if (!param)
param = evhttp_find_header(hreq->query, "song-spec"); // Retune param = httpd_query_value_find(hreq->query, "song-spec"); // Retune
if (!param) if (!param)
{ {
@ -1238,7 +1229,7 @@ dacp_reply_cue_play(struct httpd_request *hreq)
/* /cue?command=play&query=...&sort=...&index=N */ /* /cue?command=play&query=...&sort=...&index=N */
param = evhttp_find_header(hreq->query, "clear-first"); param = httpd_query_value_find(hreq->query, "clear-first");
if (param) if (param)
{ {
ret = safe_atoi32(param, &clear); ret = safe_atoi32(param, &clear);
@ -1254,10 +1245,10 @@ dacp_reply_cue_play(struct httpd_request *hreq)
player_get_status(&status); player_get_status(&status);
cuequery = evhttp_find_header(hreq->query, "query"); cuequery = httpd_query_value_find(hreq->query, "query");
if (cuequery) if (cuequery)
{ {
sort = evhttp_find_header(hreq->query, "sort"); sort = httpd_query_value_find(hreq->query, "sort");
ret = dacp_queueitem_add(cuequery, NULL, sort, 0, 0); ret = dacp_queueitem_add(cuequery, NULL, sort, 0, 0);
if (ret < 0) if (ret < 0)
@ -1273,12 +1264,12 @@ dacp_reply_cue_play(struct httpd_request *hreq)
player_playback_stop(); player_playback_stop();
} }
param = evhttp_find_header(hreq->query, "dacp.shufflestate"); param = httpd_query_value_find(hreq->query, "dacp.shufflestate");
if (param) if (param)
dacp_propset_shufflestate(param, NULL); dacp_propset_shufflestate(param, NULL);
pos = 0; pos = 0;
param = evhttp_find_header(hreq->query, "index"); param = httpd_query_value_find(hreq->query, "index");
if (param) if (param)
{ {
ret = safe_atou32(param, &pos); ret = safe_atou32(param, &pos);
@ -1287,10 +1278,10 @@ dacp_reply_cue_play(struct httpd_request *hreq)
} }
/* If selection was from Up Next queue or history queue (command will be playnow), then index is relative */ /* If selection was from Up Next queue or history queue (command will be playnow), then index is relative */
if ((param = evhttp_find_header(hreq->query, "command")) && (strcmp(param, "playnow") == 0)) if ((param = httpd_query_value_find(hreq->query, "command")) && (strcmp(param, "playnow") == 0))
{ {
/* If mode parameter is -1 or 1, the index is relative to the history queue, otherwise to the Up Next queue */ /* If mode parameter is -1 or 1, the index is relative to the history queue, otherwise to the Up Next queue */
param = evhttp_find_header(hreq->query, "mode"); param = httpd_query_value_find(hreq->query, "mode");
if (param && ((strcmp(param, "-1") == 0) || (strcmp(param, "1") == 0))) if (param && ((strcmp(param, "-1") == 0) || (strcmp(param, "1") == 0)))
{ {
/* Play from history queue */ /* Play from history queue */
@ -1397,7 +1388,7 @@ dacp_reply_cue(struct httpd_request *hreq)
if (ret < 0) if (ret < 0)
return -1; return -1;
param = evhttp_find_header(hreq->query, "command"); param = httpd_query_value_find(hreq->query, "command");
if (!param) if (!param)
{ {
DPRINTF(E_LOG, L_DACP, "No command in cue request\n"); DPRINTF(E_LOG, L_DACP, "No command in cue request\n");
@ -1463,10 +1454,10 @@ dacp_reply_playspec(struct httpd_request *hreq)
return -1; return -1;
/* Check for shuffle */ /* Check for shuffle */
shuffle = evhttp_find_header(hreq->query, "dacp.shufflestate"); shuffle = httpd_query_value_find(hreq->query, "dacp.shufflestate");
/* Playlist ID */ /* Playlist ID */
param = evhttp_find_header(hreq->query, "container-spec"); param = httpd_query_value_find(hreq->query, "container-spec");
if (!param) if (!param)
{ {
DPRINTF(E_LOG, L_DACP, "No container-spec in playspec request\n"); DPRINTF(E_LOG, L_DACP, "No container-spec in playspec request\n");
@ -1495,9 +1486,9 @@ dacp_reply_playspec(struct httpd_request *hreq)
if (!shuffle) if (!shuffle)
{ {
/* Start song ID */ /* Start song ID */
if ((param = evhttp_find_header(hreq->query, "item-spec"))) if ((param = httpd_query_value_find(hreq->query, "item-spec")))
plid = 0; // This is a podcast/audiobook - just play a single item, not a playlist plid = 0; // This is a podcast/audiobook - just play a single item, not a playlist
else if (!(param = evhttp_find_header(hreq->query, "container-item-spec"))) else if (!(param = httpd_query_value_find(hreq->query, "container-item-spec")))
{ {
DPRINTF(E_LOG, L_DACP, "No container-item-spec/item-spec in playspec request\n"); DPRINTF(E_LOG, L_DACP, "No container-item-spec/item-spec in playspec request\n");
goto out_fail; goto out_fail;
@ -1787,7 +1778,7 @@ dacp_reply_playqueuecontents(struct httpd_request *hreq)
DPRINTF(E_DBG, L_DACP, "Fetching playqueue contents\n"); DPRINTF(E_DBG, L_DACP, "Fetching playqueue contents\n");
span = 50; /* Default */ span = 50; /* Default */
param = evhttp_find_header(hreq->query, "span"); param = httpd_query_value_find(hreq->query, "span");
if (param) if (param)
{ {
ret = safe_atoi32(param, &span); ret = safe_atoi32(param, &span);
@ -1927,7 +1918,7 @@ dacp_reply_playqueueedit_clear(struct httpd_request *hreq)
const char *param; const char *param;
struct player_status status; struct player_status status;
param = evhttp_find_header(hreq->query, "mode"); param = httpd_query_value_find(hreq->query, "mode");
/* /*
* The mode parameter contains the playlist to be cleared. * The mode parameter contains the playlist to be cleared.
@ -1978,7 +1969,7 @@ dacp_reply_playqueueedit_add(struct httpd_request *hreq)
mode = 1; mode = 1;
param = evhttp_find_header(hreq->query, "mode"); param = httpd_query_value_find(hreq->query, "mode");
if (param) if (param)
{ {
ret = safe_atoi32(param, &mode); ret = safe_atoi32(param, &mode);
@ -2000,7 +1991,7 @@ dacp_reply_playqueueedit_add(struct httpd_request *hreq)
if (mode == 2) if (mode == 2)
player_shuffle_set(1); player_shuffle_set(1);
editquery = evhttp_find_header(hreq->query, "query"); editquery = httpd_query_value_find(hreq->query, "query");
if (!editquery) if (!editquery)
{ {
DPRINTF(E_LOG, L_DACP, "Could not add song queue, DACP query missing\n"); DPRINTF(E_LOG, L_DACP, "Could not add song queue, DACP query missing\n");
@ -2009,7 +2000,7 @@ dacp_reply_playqueueedit_add(struct httpd_request *hreq)
return -1; return -1;
} }
sort = evhttp_find_header(hreq->query, "sort"); sort = httpd_query_value_find(hreq->query, "sort");
// if sort param is missing and an album or artist is added to the queue, set sort to "album" // if sort param is missing and an album or artist is added to the queue, set sort to "album"
if (!sort && (strstr(editquery, "daap.songalbumid:") || strstr(editquery, "daap.songartistid:"))) if (!sort && (strstr(editquery, "daap.songalbumid:") || strstr(editquery, "daap.songartistid:")))
@ -2018,9 +2009,9 @@ dacp_reply_playqueueedit_add(struct httpd_request *hreq)
} }
// only use queryfilter if mode is not equal 0 (add to up next), 3 (play next) or 5 (add to up next) // only use queryfilter if mode is not equal 0 (add to up next), 3 (play next) or 5 (add to up next)
queuefilter = (mode == 0 || mode == 3 || mode == 5) ? NULL : evhttp_find_header(hreq->query, "queuefilter"); queuefilter = (mode == 0 || mode == 3 || mode == 5) ? NULL : httpd_query_value_find(hreq->query, "queuefilter");
querymodifier = evhttp_find_header(hreq->query, "query-modifier"); querymodifier = httpd_query_value_find(hreq->query, "query-modifier");
if (!querymodifier || (strcmp(querymodifier, "containers") != 0)) if (!querymodifier || (strcmp(querymodifier, "containers") != 0))
{ {
quirkyquery = (mode == 1) && strstr(editquery, "dmap.itemid:") && ((!queuefilter) || strstr(queuefilter, "(null)")); quirkyquery = (mode == 1) && strstr(editquery, "dmap.itemid:") && ((!queuefilter) || strstr(queuefilter, "(null)"));
@ -2106,7 +2097,7 @@ dacp_reply_playqueueedit_move(struct httpd_request *hreq)
int src; int src;
int dst; int dst;
param = evhttp_find_header(hreq->query, "edit-params"); param = httpd_query_value_find(hreq->query, "edit-params");
if (param) if (param)
{ {
ret = safe_atoi32(strchr(param, ':') + 1, &src); ret = safe_atoi32(strchr(param, ':') + 1, &src);
@ -2150,7 +2141,7 @@ dacp_reply_playqueueedit_remove(struct httpd_request *hreq)
int item_index; int item_index;
int ret; int ret;
param = evhttp_find_header(hreq->query, "items"); param = httpd_query_value_find(hreq->query, "items");
if (param) if (param)
{ {
ret = safe_atoi32(param, &item_index); ret = safe_atoi32(param, &item_index);
@ -2226,7 +2217,7 @@ dacp_reply_playqueueedit(struct httpd_request *hreq)
if (ret < 0) if (ret < 0)
return -1; return -1;
param = evhttp_find_header(hreq->query, "command"); param = httpd_query_value_find(hreq->query, "command");
if (!param) if (!param)
{ {
DPRINTF(E_LOG, L_DACP, "No command in playqueue-edit request\n"); DPRINTF(E_LOG, L_DACP, "No command in playqueue-edit request\n");
@ -2258,7 +2249,6 @@ static int
dacp_reply_playstatusupdate(struct httpd_request *hreq) dacp_reply_playstatusupdate(struct httpd_request *hreq)
{ {
struct dacp_update_request *ur; struct dacp_update_request *ur;
struct evhttp_connection *evcon;
const char *param; const char *param;
int reqd_rev; int reqd_rev;
int ret; int ret;
@ -2267,7 +2257,7 @@ dacp_reply_playstatusupdate(struct httpd_request *hreq)
if (ret < 0) if (ret < 0)
return -1; return -1;
param = evhttp_find_header(hreq->query, "revision-number"); param = httpd_query_value_find(hreq->query, "revision-number");
if (!param) if (!param)
{ {
DPRINTF(E_LOG, L_DACP, "Missing revision-number in update request\n"); DPRINTF(E_LOG, L_DACP, "Missing revision-number in update request\n");
@ -2317,9 +2307,7 @@ dacp_reply_playstatusupdate(struct httpd_request *hreq)
/* If the connection fails before we have an update to push out /* If the connection fails before we have an update to push out
* to the client, we need to know. * to the client, we need to know.
*/ */
evcon = evhttp_request_get_connection(hreq->req); httpd_request_closecb_set(hreq, update_fail_cb, ur);
if (evcon)
evhttp_connection_set_closecb(evcon, update_fail_cb, ur);
return 0; return 0;
} }
@ -2328,7 +2316,7 @@ static int
dacp_reply_nowplayingartwork(struct httpd_request *hreq) dacp_reply_nowplayingartwork(struct httpd_request *hreq)
{ {
char clen[32]; char clen[32];
struct evkeyvalq *headers; httpd_headers *headers;
const char *param; const char *param;
char *ctype; char *ctype;
size_t len; size_t len;
@ -2341,7 +2329,7 @@ dacp_reply_nowplayingartwork(struct httpd_request *hreq)
if (ret < 0) if (ret < 0)
return -1; return -1;
param = evhttp_find_header(hreq->query, "mw"); param = httpd_query_value_find(hreq->query, "mw");
if (!param) if (!param)
{ {
DPRINTF(E_LOG, L_DACP, "Request for artwork without mw parameter\n"); DPRINTF(E_LOG, L_DACP, "Request for artwork without mw parameter\n");
@ -2355,7 +2343,7 @@ dacp_reply_nowplayingartwork(struct httpd_request *hreq)
goto error; goto error;
} }
param = evhttp_find_header(hreq->query, "mh"); param = httpd_query_value_find(hreq->query, "mh");
if (!param) if (!param)
{ {
DPRINTF(E_LOG, L_DACP, "Request for artwork without mh parameter\n"); DPRINTF(E_LOG, L_DACP, "Request for artwork without mh parameter\n");
@ -2393,11 +2381,11 @@ dacp_reply_nowplayingartwork(struct httpd_request *hreq)
goto no_artwork; goto no_artwork;
} }
headers = evhttp_request_get_output_headers(hreq->req); headers = httpd_request_output_headers_get(hreq);
evhttp_remove_header(headers, "Content-Type"); httpd_header_remove(headers, "Content-Type");
evhttp_add_header(headers, "Content-Type", ctype); httpd_header_add(headers, "Content-Type", ctype);
snprintf(clen, sizeof(clen), "%ld", (long)len); snprintf(clen, sizeof(clen), "%ld", (long)len);
evhttp_add_header(headers, "Content-Length", clen); httpd_header_add(headers, "Content-Length", clen);
httpd_send_reply(hreq, HTTP_OK, "OK", hreq->reply, HTTPD_SEND_NO_GZIP); httpd_send_reply(hreq, HTTP_OK, "OK", hreq->reply, HTTPD_SEND_NO_GZIP);
return 0; return 0;
@ -2429,7 +2417,7 @@ dacp_reply_getproperty(struct httpd_request *hreq)
if (ret < 0) if (ret < 0)
return -1; return -1;
param = evhttp_find_header(hreq->query, "properties"); param = httpd_query_value_find(hreq->query, "properties");
if (!param) if (!param)
{ {
DPRINTF(E_WARN, L_DACP, "Invalid DACP getproperty request, no properties\n"); DPRINTF(E_WARN, L_DACP, "Invalid DACP getproperty request, no properties\n");
@ -2513,11 +2501,27 @@ dacp_reply_getproperty(struct httpd_request *hreq)
return -1; return -1;
} }
static void
setproperty_cb(const char *key, const char *val, void *arg)
{
struct httpd_request *hreq = arg;
const struct dacp_prop_map *dpm = dacp_find_prop(key, strlen(key));
if (!dpm)
{
DPRINTF(E_SPAM, L_DACP, "Unknown DACP property %s\n", key);
return;
}
if (dpm->propset)
dpm->propset(val, hreq);
else
DPRINTF(E_WARN, L_DACP, "No setter method for DACP property %s\n", dpm->desc);
}
static int static int
dacp_reply_setproperty(struct httpd_request *hreq) dacp_reply_setproperty(struct httpd_request *hreq)
{ {
const struct dacp_prop_map *dpm;
struct evkeyval *param;
int ret; int ret;
ret = dacp_request_authorize(hreq); ret = dacp_request_authorize(hreq);
@ -2536,21 +2540,7 @@ dacp_reply_setproperty(struct httpd_request *hreq)
/* /ctrl-int/1/setproperty?dacp.shufflestate=1&session-id=100 */ /* /ctrl-int/1/setproperty?dacp.shufflestate=1&session-id=100 */
TAILQ_FOREACH(param, hreq->query, next) httpd_query_iterate(hreq->query, setproperty_cb, hreq);
{
dpm = dacp_find_prop(param->key, strlen(param->key));
if (!dpm)
{
DPRINTF(E_SPAM, L_DACP, "Unknown DACP property %s\n", param->key);
continue;
}
if (dpm->propset)
dpm->propset(param->value, hreq);
else
DPRINTF(E_WARN, L_DACP, "No setter method for DACP property %s\n", dpm->desc);
}
/* 204 No Content is the canonical reply */ /* 204 No Content is the canonical reply */
httpd_send_reply(hreq, HTTP_NOCONTENT, "No Content", hreq->reply, HTTPD_SEND_NO_GZIP); httpd_send_reply(hreq, HTTP_NOCONTENT, "No Content", hreq->reply, HTTPD_SEND_NO_GZIP);
@ -2600,7 +2590,7 @@ dacp_reply_setspeakers(struct httpd_request *hreq)
if (ret < 0) if (ret < 0)
return -1; return -1;
param = evhttp_find_header(hreq->query, "speaker-id"); param = httpd_query_value_find(hreq->query, "speaker-id");
if (!param) if (!param)
{ {
DPRINTF(E_LOG, L_DACP, "Missing speaker-id parameter in DACP setspeakers request\n"); DPRINTF(E_LOG, L_DACP, "Missing speaker-id parameter in DACP setspeakers request\n");
@ -2865,7 +2855,7 @@ static struct httpd_uri_map dacp_handlers[] =
static void static void
dacp_request(struct httpd_request *hreq) dacp_request(struct httpd_request *hreq)
{ {
struct evkeyvalq *headers; httpd_headers *headers;
DPRINTF(E_DBG, L_DACP, "DACP request: '%s'\n", hreq->uri); DPRINTF(E_DBG, L_DACP, "DACP request: '%s'\n", hreq->uri);
@ -2877,10 +2867,10 @@ dacp_request(struct httpd_request *hreq)
return; return;
} }
headers = evhttp_request_get_output_headers(hreq->req); headers = httpd_request_output_headers_get(hreq);
evhttp_add_header(headers, "DAAP-Server", PACKAGE_NAME "/" VERSION); httpd_header_add(headers, "DAAP-Server", PACKAGE_NAME "/" VERSION);
/* Content-Type for all DACP replies; can be overriden as needed */ /* Content-Type for all DACP replies; can be overriden as needed */
evhttp_add_header(headers, "Content-Type", "application/x-dmap-tagged"); httpd_header_add(headers, "Content-Type", "application/x-dmap-tagged");
CHECK_NULL(L_DACP, hreq->reply = evbuffer_new()); CHECK_NULL(L_DACP, hreq->reply = evbuffer_new());
@ -2951,17 +2941,17 @@ static void
dacp_deinit(void) dacp_deinit(void)
{ {
struct dacp_update_request *ur; struct dacp_update_request *ur;
struct evhttp_connection *evcon; httpd_connection *conn;
for (ur = update_requests; update_requests; ur = update_requests) for (ur = update_requests; update_requests; ur = update_requests)
{ {
update_requests = ur->next; update_requests = ur->next;
evcon = evhttp_request_get_connection(ur->hreq->req); conn = httpd_request_connection_get(ur->hreq);
if (evcon) if (conn)
{ {
evhttp_connection_set_closecb(evcon, NULL, NULL); httpd_connection_closecb_set(conn, NULL, NULL);
evhttp_connection_free(evcon); httpd_connection_free(conn);
} }
free(ur); free(ur);

View File

@ -7,6 +7,26 @@
#include <event2/http.h> #include <event2/http.h>
#include <event2/keyvalq_struct.h> #include <event2/keyvalq_struct.h>
typedef struct evhttp_connection httpd_connection;
typedef struct evkeyvalq httpd_headers;
typedef struct evkeyvalq httpd_query;
typedef void (*httpd_connection_closecb)(httpd_connection *conn, void *arg);
typedef void (*httpd_connection_chunkcb)(httpd_connection *conn, void *arg);
typedef void (*httpd_query_iteratecb)(const char *key, const char *val, void *arg);
enum httpd_methods
{
HTTPD_METHOD_GET = 1 << 0,
HTTPD_METHOD_POST = 1 << 1,
HTTPD_METHOD_HEAD = 1 << 2,
HTTPD_METHOD_PUT = 1 << 3,
HTTPD_METHOD_DELETE = 1 << 4,
HTTPD_METHOD_OPTIONS = 1 << 5,
HTTPD_METHOD_TRACE = 1 << 6,
HTTPD_METHOD_CONNECT = 1 << 7,
HTTPD_METHOD_PATCH = 1 << 8,
};
enum httpd_send_flags enum httpd_send_flags
{ {
@ -49,7 +69,7 @@ struct httpd_request {
// The parsed request URI given to us by httpd_uri_parse // The parsed request URI given to us by httpd_uri_parse
struct httpd_uri_parsed *uri_parsed; struct httpd_uri_parsed *uri_parsed;
// Shortcut to &uri_parsed->ev_query // Shortcut to &uri_parsed->ev_query
struct evkeyvalq *query; httpd_query *query;
// http request struct (if available) // http request struct (if available)
struct evhttp_request *req; struct evhttp_request *req;
// Source IP address (ipv4 or ipv6) and port of the request (if available) // Source IP address (ipv4 or ipv6) and port of the request (if available)
@ -58,6 +78,10 @@ struct httpd_request {
// A pointer to extra data that the module handling the request might need // A pointer to extra data that the module handling the request might need
void *extra_data; void *extra_data;
// Request headers
httpd_headers *in_headers;
// Request body
struct evbuffer *in_body;
// Reply evbuffer // Reply evbuffer
struct evbuffer *reply; struct evbuffer *reply;
@ -103,7 +127,7 @@ struct httpd_module
*/ */
struct httpd_uri_map struct httpd_uri_map
{ {
int method; enum httpd_methods method;
char *regexp; char *regexp;
int (*handler)(struct httpd_request *hreq); int (*handler)(struct httpd_request *hreq);
void *preg; void *preg;
@ -179,4 +203,70 @@ httpd_admin_check_auth(struct httpd_request *hreq);
int int
httpd_basic_auth(struct httpd_request *hreq, const char *user, const char *passwd, const char *realm); httpd_basic_auth(struct httpd_request *hreq, const char *user, const char *passwd, const char *realm);
/*-------------------------- WRAPPERS FOR EVHTTP -----------------------------*/
const char *
httpd_query_value_find(httpd_query *query, const char *key);
void
httpd_query_iterate(httpd_query *query, httpd_query_iteratecb cb, void *arg);
const char *
httpd_header_find(httpd_headers *headers, const char *key);
void
httpd_header_remove(httpd_headers *headers, const char *key);
void
httpd_header_add(httpd_headers *headers, const char *key, const char *val);
void
httpd_headers_clear(httpd_headers *headers);
httpd_headers *
httpd_request_input_headers_get(struct httpd_request *hreq);
httpd_headers *
httpd_request_output_headers_get(struct httpd_request *hreq);
int
httpd_connection_closecb_set(httpd_connection *conn, httpd_connection_closecb cb, void *arg);
/*
int
httpd_connection_peer_get(char **addr, uint16_t *port, struct httpd_connection *conn);
*/
void
httpd_connection_free(httpd_connection *conn);
httpd_connection *
httpd_request_connection_get(struct httpd_request *hreq);
/*
const char *
httpd_request_uri_get(struct httpd_request *hreq);
int
httpd_request_peer_get(char **addr, uint16_t *port, struct httpd_request *hreq);
*/
int
httpd_request_method_get(enum httpd_methods *method, struct httpd_request *hreq);
void
httpd_request_backend_free(struct httpd_request *hreq);
int
httpd_request_closecb_set(struct httpd_request *hreq, httpd_connection_closecb cb, void *arg);
/*
void
httpd_reply_send(struct httpd_request *hreq, int code, const char *reason, struct evbuffer *evbuf)
*/
void
httpd_reply_start_send(struct httpd_request *hreq, int code, const char *reason);
void
httpd_reply_chunk_send(struct httpd_request *hreq, struct evbuffer *evbuf, httpd_connection_chunkcb cb, void *arg);
void
httpd_reply_end_send(struct httpd_request *hreq);
#endif /* !__HTTPD_INTERNAL_H__ */ #endif /* !__HTTPD_INTERNAL_H__ */

View File

@ -764,7 +764,7 @@ query_params_limit_set(struct query_params *query_params, struct httpd_request *
query_params->limit = -1; query_params->limit = -1;
query_params->offset = 0; query_params->offset = 0;
param = evhttp_find_header(hreq->query, "limit"); param = httpd_query_value_find(hreq->query, "limit");
if (param) if (param)
{ {
query_params->idx_type = I_SUB; query_params->idx_type = I_SUB;
@ -775,7 +775,7 @@ query_params_limit_set(struct query_params *query_params, struct httpd_request *
return -1; return -1;
} }
param = evhttp_find_header(hreq->query, "offset"); param = httpd_query_value_find(hreq->query, "offset");
if (param && safe_atoi32(param, &query_params->offset) < 0) if (param && safe_atoi32(param, &query_params->offset) < 0)
{ {
DPRINTF(E_LOG, L_WEB, "Invalid value for query parameter 'offset' (%s)\n", param); DPRINTF(E_LOG, L_WEB, "Invalid value for query parameter 'offset' (%s)\n", param);
@ -1062,7 +1062,6 @@ jsonapi_reply_settings_option_put(struct httpd_request *hreq)
const char *optionname; const char *optionname;
struct settings_category *category; struct settings_category *category;
struct settings_option *option; struct settings_option *option;
struct evbuffer *in_evbuf;
json_object* request; json_object* request;
int intval; int intval;
bool boolval; bool boolval;
@ -1088,8 +1087,7 @@ jsonapi_reply_settings_option_put(struct httpd_request *hreq)
return HTTP_NOTFOUND; return HTTP_NOTFOUND;
} }
in_evbuf = evhttp_request_get_input_buffer(hreq->req); request = jparse_obj_from_evbuffer(hreq->in_body);
request = jparse_obj_from_evbuffer(in_evbuf);
if (!request) if (!request)
{ {
DPRINTF(E_LOG, L_WEB, "Missing request body for setting option '%s' (type %d)\n", optionname, option->type); DPRINTF(E_LOG, L_WEB, "Missing request body for setting option '%s' (type %d)\n", optionname, option->type);
@ -1252,7 +1250,7 @@ jsonapi_reply_update(struct httpd_request *hreq)
{ {
const char *param; const char *param;
param = evhttp_find_header(hreq->query, "scan_kind"); param = httpd_query_value_find(hreq->query, "scan_kind");
library_rescan(db_scan_kind_enum(param)); library_rescan(db_scan_kind_enum(param));
return HTTP_NOCONTENT; return HTTP_NOCONTENT;
@ -1263,7 +1261,7 @@ jsonapi_reply_meta_rescan(struct httpd_request *hreq)
{ {
const char *param; const char *param;
param = evhttp_find_header(hreq->query, "scan_kind"); param = httpd_query_value_find(hreq->query, "scan_kind");
library_metarescan(db_scan_kind_enum(param)); library_metarescan(db_scan_kind_enum(param));
return HTTP_NOCONTENT; return HTTP_NOCONTENT;
@ -1379,7 +1377,6 @@ static int
jsonapi_reply_lastfm_login(struct httpd_request *hreq) jsonapi_reply_lastfm_login(struct httpd_request *hreq)
{ {
#ifdef LASTFM #ifdef LASTFM
struct evbuffer *in_evbuf;
json_object *request; json_object *request;
const char *user; const char *user;
const char *password; const char *password;
@ -1390,8 +1387,7 @@ jsonapi_reply_lastfm_login(struct httpd_request *hreq)
DPRINTF(E_DBG, L_WEB, "Received LastFM login request\n"); DPRINTF(E_DBG, L_WEB, "Received LastFM login request\n");
in_evbuf = evhttp_request_get_input_buffer(hreq->req); request = jparse_obj_from_evbuffer(hreq->in_body);
request = jparse_obj_from_evbuffer(in_evbuf);
if (!request) if (!request)
{ {
DPRINTF(E_LOG, L_WEB, "Failed to parse incoming request\n"); DPRINTF(E_LOG, L_WEB, "Failed to parse incoming request\n");
@ -1466,13 +1462,11 @@ jsonapi_reply_lastfm_logout(struct httpd_request *hreq)
static int static int
jsonapi_reply_pairing_pair(struct httpd_request *hreq) jsonapi_reply_pairing_pair(struct httpd_request *hreq)
{ {
struct evbuffer *evbuf;
json_object* request; json_object* request;
const char* pin; const char* pin;
int ret; int ret;
evbuf = evhttp_request_get_input_buffer(hreq->req); request = jparse_obj_from_evbuffer(hreq->in_body);
request = jparse_obj_from_evbuffer(evbuf);
if (!request) if (!request)
{ {
DPRINTF(E_LOG, L_WEB, "Failed to parse incoming request\n"); DPRINTF(E_LOG, L_WEB, "Failed to parse incoming request\n");
@ -1623,7 +1617,6 @@ static int
jsonapi_reply_outputs_put_byid(struct httpd_request *hreq) jsonapi_reply_outputs_put_byid(struct httpd_request *hreq)
{ {
uint64_t output_id; uint64_t output_id;
struct evbuffer *in_evbuf;
json_object* request; json_object* request;
bool selected; bool selected;
int volume; int volume;
@ -1638,8 +1631,7 @@ jsonapi_reply_outputs_put_byid(struct httpd_request *hreq)
return HTTP_BADREQUEST; return HTTP_BADREQUEST;
} }
in_evbuf = evhttp_request_get_input_buffer(hreq->req); request = jparse_obj_from_evbuffer(hreq->in_body);
request = jparse_obj_from_evbuffer(in_evbuf);
if (!request) if (!request)
{ {
DPRINTF(E_LOG, L_WEB, "Failed to parse incoming request\n"); DPRINTF(E_LOG, L_WEB, "Failed to parse incoming request\n");
@ -1741,12 +1733,10 @@ jsonapi_reply_outputs(struct httpd_request *hreq)
static int static int
jsonapi_reply_verification(struct httpd_request *hreq) jsonapi_reply_verification(struct httpd_request *hreq)
{ {
struct evbuffer *in_evbuf;
json_object* request; json_object* request;
const char* message; const char* message;
in_evbuf = evhttp_request_get_input_buffer(hreq->req); request = jparse_obj_from_evbuffer(hreq->in_body);
request = jparse_obj_from_evbuffer(in_evbuf);
if (!request) if (!request)
{ {
DPRINTF(E_LOG, L_WEB, "Failed to parse incoming request\n"); DPRINTF(E_LOG, L_WEB, "Failed to parse incoming request\n");
@ -1769,15 +1759,13 @@ jsonapi_reply_verification(struct httpd_request *hreq)
static int static int
jsonapi_reply_outputs_set(struct httpd_request *hreq) jsonapi_reply_outputs_set(struct httpd_request *hreq)
{ {
struct evbuffer *in_evbuf;
json_object *request; json_object *request;
json_object *outputs; json_object *outputs;
json_object *output_id; json_object *output_id;
int nspk, i, ret; int nspk, i, ret;
uint64_t *ids; uint64_t *ids;
in_evbuf = evhttp_request_get_input_buffer(hreq->req); request = jparse_obj_from_evbuffer(hreq->in_body);
request = jparse_obj_from_evbuffer(in_evbuf);
if (!request) if (!request)
{ {
DPRINTF(E_LOG, L_WEB, "Failed to parse incoming request\n"); DPRINTF(E_LOG, L_WEB, "Failed to parse incoming request\n");
@ -1902,11 +1890,11 @@ jsonapi_reply_player_play(struct httpd_request *hreq)
const char *param; const char *param;
int ret; int ret;
if ((param = evhttp_find_header(hreq->query, "item_id"))) if ((param = httpd_query_value_find(hreq->query, "item_id")))
{ {
return play_item_with_id(param); return play_item_with_id(param);
} }
else if ((param = evhttp_find_header(hreq->query, "position"))) else if ((param = httpd_query_value_find(hreq->query, "position")))
{ {
return play_item_at_position(param); return play_item_at_position(param);
} }
@ -2033,8 +2021,8 @@ jsonapi_reply_player_seek(struct httpd_request *hreq)
int seek_ms; int seek_ms;
int ret; int ret;
param_pos = evhttp_find_header(hreq->query, "position_ms"); param_pos = httpd_query_value_find(hreq->query, "position_ms");
param_seek = evhttp_find_header(hreq->query, "seek_ms"); param_seek = httpd_query_value_find(hreq->query, "seek_ms");
if (!param_pos && !param_seek) if (!param_pos && !param_seek)
return HTTP_BADREQUEST; return HTTP_BADREQUEST;
@ -2472,7 +2460,7 @@ jsonapi_reply_queue_tracks_add(struct httpd_request *hreq)
int ret = 0; int ret = 0;
param_pos = evhttp_find_header(hreq->query, "position"); param_pos = httpd_query_value_find(hreq->query, "position");
if (param_pos) if (param_pos)
{ {
if (safe_atoi32(param_pos, &pos) < 0) if (safe_atoi32(param_pos, &pos) < 0)
@ -2487,8 +2475,8 @@ jsonapi_reply_queue_tracks_add(struct httpd_request *hreq)
else else
pos = -1; pos = -1;
param_uris = evhttp_find_header(hreq->query, "uris"); param_uris = httpd_query_value_find(hreq->query, "uris");
param_expression = evhttp_find_header(hreq->query, "expression"); param_expression = httpd_query_value_find(hreq->query, "expression");
if (!param_uris && !param_expression) if (!param_uris && !param_expression)
{ {
@ -2498,7 +2486,7 @@ jsonapi_reply_queue_tracks_add(struct httpd_request *hreq)
} }
// if query parameter "clear" is "true", stop playback and clear the queue before adding new queue items // if query parameter "clear" is "true", stop playback and clear the queue before adding new queue items
param = evhttp_find_header(hreq->query, "clear"); param = httpd_query_value_find(hreq->query, "clear");
if (param && strcmp(param, "true") == 0) if (param && strcmp(param, "true") == 0)
{ {
player_playback_stop(); player_playback_stop();
@ -2506,7 +2494,7 @@ jsonapi_reply_queue_tracks_add(struct httpd_request *hreq)
} }
// if query parameter "shuffle" is present, update the shuffle state before adding new queue items // if query parameter "shuffle" is present, update the shuffle state before adding new queue items
param = evhttp_find_header(hreq->query, "shuffle"); param = httpd_query_value_find(hreq->query, "shuffle");
if (param) if (param)
{ {
shuffle = (strcmp(param, "true") == 0); shuffle = (strcmp(param, "true") == 0);
@ -2520,7 +2508,7 @@ jsonapi_reply_queue_tracks_add(struct httpd_request *hreq)
else else
{ {
// This overrides the value specified in query // This overrides the value specified in query
param = evhttp_find_header(hreq->query, "limit"); param = httpd_query_value_find(hreq->query, "limit");
if (param && safe_atoi32(param, &limit) == 0) if (param && safe_atoi32(param, &limit) == 0)
ret = queue_tracks_add_byexpression(param_expression, pos, limit, &total_count); ret = queue_tracks_add_byexpression(param_expression, pos, limit, &total_count);
else else
@ -2540,10 +2528,10 @@ jsonapi_reply_queue_tracks_add(struct httpd_request *hreq)
return HTTP_INTERNAL; return HTTP_INTERNAL;
// If query parameter "playback" is "start", start playback after successfully adding new items // If query parameter "playback" is "start", start playback after successfully adding new items
param = evhttp_find_header(hreq->query, "playback"); param = httpd_query_value_find(hreq->query, "playback");
if (param && strcmp(param, "start") == 0) if (param && strcmp(param, "start") == 0)
{ {
if ((param = evhttp_find_header(hreq->query, "playback_from_position"))) if ((param = httpd_query_value_find(hreq->query, "playback_from_position")))
ret = (play_item_at_position(param) == HTTP_NOCONTENT) ? 0 : -1; ret = (play_item_at_position(param) == HTTP_NOCONTENT) ? 0 : -1;
else else
ret = player_playback_start(); ret = player_playback_start();
@ -2618,21 +2606,21 @@ jsonapi_reply_queue_tracks_update(struct httpd_request *hreq)
ret = HTTP_OK; ret = HTTP_OK;
is_changed = false; is_changed = false;
if ((param = evhttp_find_header(hreq->query, "new_position"))) if ((param = httpd_query_value_find(hreq->query, "new_position")))
ret = update_pos(item_id, param, status.shuffle); ret = update_pos(item_id, param, status.shuffle);
if ((param = evhttp_find_header(hreq->query, "title"))) if ((param = httpd_query_value_find(hreq->query, "title")))
update_str(&is_changed, &queue_item->title, param); update_str(&is_changed, &queue_item->title, param);
if ((param = evhttp_find_header(hreq->query, "album"))) if ((param = httpd_query_value_find(hreq->query, "album")))
update_str(&is_changed, &queue_item->album, param); update_str(&is_changed, &queue_item->album, param);
if ((param = evhttp_find_header(hreq->query, "artist"))) if ((param = httpd_query_value_find(hreq->query, "artist")))
update_str(&is_changed, &queue_item->artist, param); update_str(&is_changed, &queue_item->artist, param);
if ((param = evhttp_find_header(hreq->query, "album_artist"))) if ((param = httpd_query_value_find(hreq->query, "album_artist")))
update_str(&is_changed, &queue_item->album_artist, param); update_str(&is_changed, &queue_item->album_artist, param);
if ((param = evhttp_find_header(hreq->query, "composer"))) if ((param = httpd_query_value_find(hreq->query, "composer")))
update_str(&is_changed, &queue_item->composer, param); update_str(&is_changed, &queue_item->composer, param);
if ((param = evhttp_find_header(hreq->query, "genre"))) if ((param = httpd_query_value_find(hreq->query, "genre")))
update_str(&is_changed, &queue_item->genre, param); update_str(&is_changed, &queue_item->genre, param);
if ((param = evhttp_find_header(hreq->query, "artwork_url"))) if ((param = httpd_query_value_find(hreq->query, "artwork_url")))
update_str(&is_changed, &queue_item->artwork_url, param); update_str(&is_changed, &queue_item->artwork_url, param);
if (ret != HTTP_OK) if (ret != HTTP_OK)
@ -2713,7 +2701,7 @@ jsonapi_reply_queue(struct httpd_request *hreq)
if (status.shuffle) if (status.shuffle)
query_params.sort = S_SHUFFLE_POS; query_params.sort = S_SHUFFLE_POS;
param = evhttp_find_header(hreq->query, "id"); param = httpd_query_value_find(hreq->query, "id");
if (param && strcmp(param, "now_playing") == 0) if (param && strcmp(param, "now_playing") == 0)
{ {
query_params.filter = db_mprintf("id = %d", status.item_id); query_params.filter = db_mprintf("id = %d", status.item_id);
@ -2724,10 +2712,10 @@ jsonapi_reply_queue(struct httpd_request *hreq)
} }
else else
{ {
param = evhttp_find_header(hreq->query, "start"); param = httpd_query_value_find(hreq->query, "start");
if (param && safe_atoi32(param, &start_pos) == 0) if (param && safe_atoi32(param, &start_pos) == 0)
{ {
param = evhttp_find_header(hreq->query, "end"); param = httpd_query_value_find(hreq->query, "end");
if (!param || safe_atoi32(param, &end_pos) != 0) if (!param || safe_atoi32(param, &end_pos) != 0)
{ {
end_pos = start_pos + 1; end_pos = start_pos + 1;
@ -2774,7 +2762,7 @@ jsonapi_reply_player_repeat(struct httpd_request *hreq)
{ {
const char *param; const char *param;
param = evhttp_find_header(hreq->query, "state"); param = httpd_query_value_find(hreq->query, "state");
if (!param) if (!param)
return HTTP_BADREQUEST; return HTTP_BADREQUEST;
@ -2800,7 +2788,7 @@ jsonapi_reply_player_shuffle(struct httpd_request *hreq)
const char *param; const char *param;
bool shuffle; bool shuffle;
param = evhttp_find_header(hreq->query, "state"); param = httpd_query_value_find(hreq->query, "state");
if (!param) if (!param)
return HTTP_BADREQUEST; return HTTP_BADREQUEST;
@ -2816,7 +2804,7 @@ jsonapi_reply_player_consume(struct httpd_request *hreq)
const char *param; const char *param;
bool consume; bool consume;
param = evhttp_find_header(hreq->query, "state"); param = httpd_query_value_find(hreq->query, "state");
if (!param) if (!param)
return HTTP_BADREQUEST; return HTTP_BADREQUEST;
@ -2895,7 +2883,7 @@ jsonapi_reply_player_volume(struct httpd_request *hreq)
step = 0; step = 0;
// Parse and validate parameters // Parse and validate parameters
param_volume = evhttp_find_header(hreq->query, "volume"); param_volume = httpd_query_value_find(hreq->query, "volume");
if (param_volume) if (param_volume)
{ {
ret = safe_atoi32(param_volume, &volume); ret = safe_atoi32(param_volume, &volume);
@ -2903,7 +2891,7 @@ jsonapi_reply_player_volume(struct httpd_request *hreq)
return HTTP_BADREQUEST; return HTTP_BADREQUEST;
} }
param_step = evhttp_find_header(hreq->query, "step"); param_step = httpd_query_value_find(hreq->query, "step");
if (param_step) if (param_step)
{ {
ret = safe_atoi32(param_step, &step); ret = safe_atoi32(param_step, &step);
@ -2918,7 +2906,7 @@ jsonapi_reply_player_volume(struct httpd_request *hreq)
return HTTP_BADREQUEST; return HTTP_BADREQUEST;
} }
param = evhttp_find_header(hreq->query, "output_id"); param = httpd_query_value_find(hreq->query, "output_id");
if (param) if (param)
{ {
// Update volume for individual output // Update volume for individual output
@ -2957,7 +2945,7 @@ jsonapi_reply_library_artists(struct httpd_request *hreq)
return HTTP_NOTMODIFIED; return HTTP_NOTMODIFIED;
media_kind = 0; media_kind = 0;
param = evhttp_find_header(hreq->query, "media_kind"); param = httpd_query_value_find(hreq->query, "media_kind");
if (param) if (param)
{ {
media_kind = db_media_kind_enum(param); media_kind = db_media_kind_enum(param);
@ -3106,7 +3094,7 @@ jsonapi_reply_library_albums(struct httpd_request *hreq)
return HTTP_NOTMODIFIED; return HTTP_NOTMODIFIED;
media_kind = 0; media_kind = 0;
param = evhttp_find_header(hreq->query, "media_kind"); param = httpd_query_value_find(hreq->query, "media_kind");
if (param) if (param)
{ {
media_kind = db_media_kind_enum(param); media_kind = db_media_kind_enum(param);
@ -3251,7 +3239,7 @@ jsonapi_reply_library_album_tracks_put_byid(struct httpd_request *hreq)
if (ret < 0) if (ret < 0)
return HTTP_INTERNAL; return HTTP_INTERNAL;
param = evhttp_find_header(hreq->query, "play_count"); param = httpd_query_value_find(hreq->query, "play_count");
if (!param) if (!param)
return HTTP_BADREQUEST; return HTTP_BADREQUEST;
@ -3327,7 +3315,6 @@ jsonapi_reply_library_tracks_get_byid(struct httpd_request *hreq)
static int static int
jsonapi_reply_library_tracks_put(struct httpd_request *hreq) jsonapi_reply_library_tracks_put(struct httpd_request *hreq)
{ {
struct evbuffer *in_evbuf;
json_object *request = NULL; json_object *request = NULL;
json_object *tracks; json_object *tracks;
json_object *track = NULL; json_object *track = NULL;
@ -3337,8 +3324,7 @@ jsonapi_reply_library_tracks_put(struct httpd_request *hreq)
int32_t track_id; int32_t track_id;
int i; int i;
in_evbuf = evhttp_request_get_input_buffer(hreq->req); request = jparse_obj_from_evbuffer(hreq->in_body);
request = jparse_obj_from_evbuffer(in_evbuf);
if (!request) if (!request)
{ {
DPRINTF(E_LOG, L_WEB, "Failed to read json tracks request\n"); DPRINTF(E_LOG, L_WEB, "Failed to read json tracks request\n");
@ -3417,7 +3403,7 @@ jsonapi_reply_library_tracks_put_byid(struct httpd_request *hreq)
if (ret < 0) if (ret < 0)
return HTTP_INTERNAL; return HTTP_INTERNAL;
param = evhttp_find_header(hreq->query, "play_count"); param = httpd_query_value_find(hreq->query, "play_count");
if (param) if (param)
{ {
if (strcmp(param, "increment") == 0) if (strcmp(param, "increment") == 0)
@ -3435,7 +3421,7 @@ jsonapi_reply_library_tracks_put_byid(struct httpd_request *hreq)
} }
} }
param = evhttp_find_header(hreq->query, "rating"); param = httpd_query_value_find(hreq->query, "rating");
if (param) if (param)
{ {
ret = safe_atou32(param, &val); ret = safe_atou32(param, &val);
@ -3451,7 +3437,7 @@ jsonapi_reply_library_tracks_put_byid(struct httpd_request *hreq)
} }
// Retreive marked tracks via "/api/search?type=tracks&expression=usermark+=+1" // Retreive marked tracks via "/api/search?type=tracks&expression=usermark+=+1"
param = evhttp_find_header(hreq->query, "usermark"); param = httpd_query_value_find(hreq->query, "usermark");
if (param) if (param)
{ {
ret = safe_atou32(param, &val); ret = safe_atou32(param, &val);
@ -3672,7 +3658,7 @@ jsonapi_reply_library_playlist_put(struct httpd_request *hreq)
return HTTP_BADREQUEST; return HTTP_BADREQUEST;
} }
if ((param = evhttp_find_header(hreq->query, "query_limit"))) if ((param = httpd_query_value_find(hreq->query, "query_limit")))
ret = playlist_attrib_query_limit_set(playlist_id, param); ret = playlist_attrib_query_limit_set(playlist_id, param);
else else
ret = -1; ret = -1;
@ -3828,7 +3814,7 @@ jsonapi_reply_library_playlist_tracks_put_byid(struct httpd_request *hreq)
if (ret < 0) if (ret < 0)
return HTTP_INTERNAL; return HTTP_INTERNAL;
param = evhttp_find_header(hreq->query, "play_count"); param = httpd_query_value_find(hreq->query, "play_count");
if (!param) if (!param)
return HTTP_BADREQUEST; return HTTP_BADREQUEST;
@ -3857,7 +3843,7 @@ jsonapi_reply_queue_save(struct httpd_request *hreq)
char *playlist_name = NULL; char *playlist_name = NULL;
int ret = 0; int ret = 0;
if ((param = evhttp_find_header(hreq->query, "name")) == NULL) if ((param = httpd_query_value_find(hreq->query, "name")) == NULL)
{ {
DPRINTF(E_LOG, L_WEB, "Invalid argument, missing 'name'\n"); DPRINTF(E_LOG, L_WEB, "Invalid argument, missing 'name'\n");
return HTTP_BADREQUEST; return HTTP_BADREQUEST;
@ -3914,7 +3900,7 @@ jsonapi_reply_library_browse(struct httpd_request *hreq)
DPRINTF(E_DBG, L_WEB, "Browse query with type '%s'\n", browse_type); DPRINTF(E_DBG, L_WEB, "Browse query with type '%s'\n", browse_type);
media_kind = 0; media_kind = 0;
param = evhttp_find_header(hreq->query, "media_kind"); param = httpd_query_value_find(hreq->query, "media_kind");
if (param) if (param)
{ {
media_kind = db_media_kind_enum(param); media_kind = db_media_kind_enum(param);
@ -4069,7 +4055,7 @@ jsonapi_reply_library_count(struct httpd_request *hreq)
memset(&qp, 0, sizeof(struct query_params)); memset(&qp, 0, sizeof(struct query_params));
qp.type = Q_COUNT_ITEMS; qp.type = Q_COUNT_ITEMS;
param_expression = evhttp_find_header(hreq->query, "expression"); param_expression = httpd_query_value_find(hreq->query, "expression");
if (param_expression) if (param_expression)
{ {
memset(&smartpl_expression, 0, sizeof(struct smartpl)); memset(&smartpl_expression, 0, sizeof(struct smartpl));
@ -4123,7 +4109,7 @@ jsonapi_reply_library_files(struct httpd_request *hreq)
int total; int total;
int ret; int ret;
param = evhttp_find_header(hreq->query, "directory"); param = httpd_query_value_find(hreq->query, "directory");
directory_id = DIR_FILE; directory_id = DIR_FILE;
if (param) if (param)
@ -4215,7 +4201,7 @@ jsonapi_reply_library_add(struct httpd_request *hreq)
const char *url; const char *url;
int ret; int ret;
url = evhttp_find_header(hreq->query, "url"); url = httpd_query_value_find(hreq->query, "url");
if (!url) if (!url)
{ {
DPRINTF(E_LOG, L_WEB, "Missing URL parameter for library add\n"); DPRINTF(E_LOG, L_WEB, "Missing URL parameter for library add\n");
@ -4528,9 +4514,9 @@ jsonapi_reply_search(struct httpd_request *hreq)
reply = NULL; reply = NULL;
param_type = evhttp_find_header(hreq->query, "type"); param_type = httpd_query_value_find(hreq->query, "type");
param_query = evhttp_find_header(hreq->query, "query"); param_query = httpd_query_value_find(hreq->query, "query");
param_expression = evhttp_find_header(hreq->query, "expression"); param_expression = httpd_query_value_find(hreq->query, "expression");
if (!param_type || (!param_query && !param_expression)) if (!param_type || (!param_query && !param_expression))
{ {
@ -4540,7 +4526,7 @@ jsonapi_reply_search(struct httpd_request *hreq)
} }
media_kind = 0; media_kind = 0;
param_media_kind = evhttp_find_header(hreq->query, "media_kind"); param_media_kind = httpd_query_value_find(hreq->query, "media_kind");
if (param_media_kind) if (param_media_kind)
{ {
media_kind = db_media_kind_enum(param_media_kind); media_kind = db_media_kind_enum(param_media_kind);
@ -4635,80 +4621,80 @@ jsonapi_reply_library_backup(struct httpd_request *hreq)
static struct httpd_uri_map adm_handlers[] = static struct httpd_uri_map adm_handlers[] =
{ {
{ EVHTTP_REQ_GET, "^/api/config$", jsonapi_reply_config }, { HTTPD_METHOD_GET, "^/api/config$", jsonapi_reply_config },
{ EVHTTP_REQ_GET, "^/api/settings$", jsonapi_reply_settings_get }, { HTTPD_METHOD_GET, "^/api/settings$", jsonapi_reply_settings_get },
{ EVHTTP_REQ_GET, "^/api/settings/[A-Za-z0-9_]+$", jsonapi_reply_settings_category_get }, { HTTPD_METHOD_GET, "^/api/settings/[A-Za-z0-9_]+$", jsonapi_reply_settings_category_get },
{ EVHTTP_REQ_GET, "^/api/settings/[A-Za-z0-9_]+/[A-Za-z0-9_]+$", jsonapi_reply_settings_option_get }, { HTTPD_METHOD_GET, "^/api/settings/[A-Za-z0-9_]+/[A-Za-z0-9_]+$", jsonapi_reply_settings_option_get },
{ EVHTTP_REQ_PUT, "^/api/settings/[A-Za-z0-9_]+/[A-Za-z0-9_]+$", jsonapi_reply_settings_option_put }, { HTTPD_METHOD_PUT, "^/api/settings/[A-Za-z0-9_]+/[A-Za-z0-9_]+$", jsonapi_reply_settings_option_put },
{ EVHTTP_REQ_DELETE, "^/api/settings/[A-Za-z0-9_]+/[A-Za-z0-9_]+$", jsonapi_reply_settings_option_delete }, { HTTPD_METHOD_DELETE, "^/api/settings/[A-Za-z0-9_]+/[A-Za-z0-9_]+$", jsonapi_reply_settings_option_delete },
{ EVHTTP_REQ_GET, "^/api/library$", jsonapi_reply_library }, { HTTPD_METHOD_GET, "^/api/library$", jsonapi_reply_library },
{ EVHTTP_REQ_GET | { HTTPD_METHOD_GET |
EVHTTP_REQ_PUT, "^/api/update$", jsonapi_reply_update }, HTTPD_METHOD_PUT, "^/api/update$", jsonapi_reply_update },
{ EVHTTP_REQ_PUT, "^/api/rescan$", jsonapi_reply_meta_rescan }, { HTTPD_METHOD_PUT, "^/api/rescan$", jsonapi_reply_meta_rescan },
{ EVHTTP_REQ_GET, "^/api/spotify-logout$", jsonapi_reply_spotify_logout }, { HTTPD_METHOD_GET, "^/api/spotify-logout$", jsonapi_reply_spotify_logout },
{ EVHTTP_REQ_GET, "^/api/spotify$", jsonapi_reply_spotify }, { HTTPD_METHOD_GET, "^/api/spotify$", jsonapi_reply_spotify },
{ EVHTTP_REQ_GET, "^/api/pairing$", jsonapi_reply_pairing_get }, { HTTPD_METHOD_GET, "^/api/pairing$", jsonapi_reply_pairing_get },
{ EVHTTP_REQ_POST, "^/api/pairing$", jsonapi_reply_pairing_pair }, { HTTPD_METHOD_POST, "^/api/pairing$", jsonapi_reply_pairing_pair },
{ EVHTTP_REQ_POST, "^/api/lastfm-login$", jsonapi_reply_lastfm_login }, { HTTPD_METHOD_POST, "^/api/lastfm-login$", jsonapi_reply_lastfm_login },
{ EVHTTP_REQ_GET, "^/api/lastfm-logout$", jsonapi_reply_lastfm_logout }, { HTTPD_METHOD_GET, "^/api/lastfm-logout$", jsonapi_reply_lastfm_logout },
{ EVHTTP_REQ_GET, "^/api/lastfm$", jsonapi_reply_lastfm }, { HTTPD_METHOD_GET, "^/api/lastfm$", jsonapi_reply_lastfm },
{ EVHTTP_REQ_POST, "^/api/verification$", jsonapi_reply_verification }, { HTTPD_METHOD_POST, "^/api/verification$", jsonapi_reply_verification },
{ EVHTTP_REQ_GET, "^/api/outputs$", jsonapi_reply_outputs }, { HTTPD_METHOD_GET, "^/api/outputs$", jsonapi_reply_outputs },
{ EVHTTP_REQ_PUT, "^/api/outputs/set$", jsonapi_reply_outputs_set }, { HTTPD_METHOD_PUT, "^/api/outputs/set$", jsonapi_reply_outputs_set },
{ EVHTTP_REQ_POST, "^/api/select-outputs$", jsonapi_reply_outputs_set }, // deprecated: use "/api/outputs/set" { HTTPD_METHOD_POST, "^/api/select-outputs$", jsonapi_reply_outputs_set }, // deprecated: use "/api/outputs/set"
{ EVHTTP_REQ_GET, "^/api/outputs/[[:digit:]]+$", jsonapi_reply_outputs_get_byid }, { HTTPD_METHOD_GET, "^/api/outputs/[[:digit:]]+$", jsonapi_reply_outputs_get_byid },
{ EVHTTP_REQ_PUT, "^/api/outputs/[[:digit:]]+$", jsonapi_reply_outputs_put_byid }, { HTTPD_METHOD_PUT, "^/api/outputs/[[:digit:]]+$", jsonapi_reply_outputs_put_byid },
{ EVHTTP_REQ_PUT, "^/api/outputs/[[:digit:]]+/toggle$", jsonapi_reply_outputs_toggle_byid }, { HTTPD_METHOD_PUT, "^/api/outputs/[[:digit:]]+/toggle$", jsonapi_reply_outputs_toggle_byid },
{ EVHTTP_REQ_GET, "^/api/player$", jsonapi_reply_player }, { HTTPD_METHOD_GET, "^/api/player$", jsonapi_reply_player },
{ EVHTTP_REQ_PUT, "^/api/player/play$", jsonapi_reply_player_play }, { HTTPD_METHOD_PUT, "^/api/player/play$", jsonapi_reply_player_play },
{ EVHTTP_REQ_PUT, "^/api/player/pause$", jsonapi_reply_player_pause }, { HTTPD_METHOD_PUT, "^/api/player/pause$", jsonapi_reply_player_pause },
{ EVHTTP_REQ_PUT, "^/api/player/stop$", jsonapi_reply_player_stop }, { HTTPD_METHOD_PUT, "^/api/player/stop$", jsonapi_reply_player_stop },
{ EVHTTP_REQ_PUT, "^/api/player/toggle$", jsonapi_reply_player_toggle }, { HTTPD_METHOD_PUT, "^/api/player/toggle$", jsonapi_reply_player_toggle },
{ EVHTTP_REQ_PUT, "^/api/player/next$", jsonapi_reply_player_next }, { HTTPD_METHOD_PUT, "^/api/player/next$", jsonapi_reply_player_next },
{ EVHTTP_REQ_PUT, "^/api/player/previous$", jsonapi_reply_player_previous }, { HTTPD_METHOD_PUT, "^/api/player/previous$", jsonapi_reply_player_previous },
{ EVHTTP_REQ_PUT, "^/api/player/shuffle$", jsonapi_reply_player_shuffle }, { HTTPD_METHOD_PUT, "^/api/player/shuffle$", jsonapi_reply_player_shuffle },
{ EVHTTP_REQ_PUT, "^/api/player/repeat$", jsonapi_reply_player_repeat }, { HTTPD_METHOD_PUT, "^/api/player/repeat$", jsonapi_reply_player_repeat },
{ EVHTTP_REQ_PUT, "^/api/player/consume$", jsonapi_reply_player_consume }, { HTTPD_METHOD_PUT, "^/api/player/consume$", jsonapi_reply_player_consume },
{ EVHTTP_REQ_PUT, "^/api/player/volume$", jsonapi_reply_player_volume }, { HTTPD_METHOD_PUT, "^/api/player/volume$", jsonapi_reply_player_volume },
{ EVHTTP_REQ_PUT, "^/api/player/seek$", jsonapi_reply_player_seek }, { HTTPD_METHOD_PUT, "^/api/player/seek$", jsonapi_reply_player_seek },
{ EVHTTP_REQ_GET, "^/api/queue$", jsonapi_reply_queue }, { HTTPD_METHOD_GET, "^/api/queue$", jsonapi_reply_queue },
{ EVHTTP_REQ_PUT, "^/api/queue/clear$", jsonapi_reply_queue_clear }, { HTTPD_METHOD_PUT, "^/api/queue/clear$", jsonapi_reply_queue_clear },
{ EVHTTP_REQ_POST, "^/api/queue/items/add$", jsonapi_reply_queue_tracks_add }, { HTTPD_METHOD_POST, "^/api/queue/items/add$", jsonapi_reply_queue_tracks_add },
{ EVHTTP_REQ_PUT, "^/api/queue/items/[[:digit:]]+$", jsonapi_reply_queue_tracks_update }, { HTTPD_METHOD_PUT, "^/api/queue/items/[[:digit:]]+$", jsonapi_reply_queue_tracks_update },
{ EVHTTP_REQ_PUT, "^/api/queue/items/now_playing$", jsonapi_reply_queue_tracks_update }, { HTTPD_METHOD_PUT, "^/api/queue/items/now_playing$", jsonapi_reply_queue_tracks_update },
{ EVHTTP_REQ_DELETE, "^/api/queue/items/[[:digit:]]+$", jsonapi_reply_queue_tracks_delete }, { HTTPD_METHOD_DELETE, "^/api/queue/items/[[:digit:]]+$", jsonapi_reply_queue_tracks_delete },
{ EVHTTP_REQ_POST, "^/api/queue/save$", jsonapi_reply_queue_save}, { HTTPD_METHOD_POST, "^/api/queue/save$", jsonapi_reply_queue_save},
{ EVHTTP_REQ_GET, "^/api/library/playlists$", jsonapi_reply_library_playlists }, { HTTPD_METHOD_GET, "^/api/library/playlists$", jsonapi_reply_library_playlists },
{ EVHTTP_REQ_GET, "^/api/library/playlists/[[:digit:]]+$", jsonapi_reply_library_playlist_get }, { HTTPD_METHOD_GET, "^/api/library/playlists/[[:digit:]]+$", jsonapi_reply_library_playlist_get },
{ EVHTTP_REQ_PUT, "^/api/library/playlists/[[:digit:]]+$", jsonapi_reply_library_playlist_put }, { HTTPD_METHOD_PUT, "^/api/library/playlists/[[:digit:]]+$", jsonapi_reply_library_playlist_put },
{ EVHTTP_REQ_GET, "^/api/library/playlists/[[:digit:]]+/tracks$", jsonapi_reply_library_playlist_tracks }, { HTTPD_METHOD_GET, "^/api/library/playlists/[[:digit:]]+/tracks$", jsonapi_reply_library_playlist_tracks },
{ EVHTTP_REQ_PUT, "^/api/library/playlists/[[:digit:]]+/tracks", jsonapi_reply_library_playlist_tracks_put_byid}, { HTTPD_METHOD_PUT, "^/api/library/playlists/[[:digit:]]+/tracks", jsonapi_reply_library_playlist_tracks_put_byid},
// { EVHTTP_REQ_POST, "^/api/library/playlists/[[:digit:]]+/tracks$", jsonapi_reply_library_playlists_tracks }, // { HTTPD_METHOD_POST, "^/api/library/playlists/[[:digit:]]+/tracks$", jsonapi_reply_library_playlists_tracks },
{ EVHTTP_REQ_DELETE, "^/api/library/playlists/[[:digit:]]+$", jsonapi_reply_library_playlist_delete }, { HTTPD_METHOD_DELETE, "^/api/library/playlists/[[:digit:]]+$", jsonapi_reply_library_playlist_delete },
{ EVHTTP_REQ_GET, "^/api/library/playlists/[[:digit:]]+/playlists", jsonapi_reply_library_playlist_playlists }, { HTTPD_METHOD_GET, "^/api/library/playlists/[[:digit:]]+/playlists", jsonapi_reply_library_playlist_playlists },
{ EVHTTP_REQ_GET, "^/api/library/artists$", jsonapi_reply_library_artists }, { HTTPD_METHOD_GET, "^/api/library/artists$", jsonapi_reply_library_artists },
{ EVHTTP_REQ_GET, "^/api/library/artists/[[:digit:]]+$", jsonapi_reply_library_artist }, { HTTPD_METHOD_GET, "^/api/library/artists/[[:digit:]]+$", jsonapi_reply_library_artist },
{ EVHTTP_REQ_GET, "^/api/library/artists/[[:digit:]]+/albums$", jsonapi_reply_library_artist_albums }, { HTTPD_METHOD_GET, "^/api/library/artists/[[:digit:]]+/albums$", jsonapi_reply_library_artist_albums },
{ EVHTTP_REQ_GET, "^/api/library/albums$", jsonapi_reply_library_albums }, { HTTPD_METHOD_GET, "^/api/library/albums$", jsonapi_reply_library_albums },
{ EVHTTP_REQ_GET, "^/api/library/albums/[[:digit:]]+$", jsonapi_reply_library_album }, { HTTPD_METHOD_GET, "^/api/library/albums/[[:digit:]]+$", jsonapi_reply_library_album },
{ EVHTTP_REQ_GET, "^/api/library/albums/[[:digit:]]+/tracks$", jsonapi_reply_library_album_tracks }, { HTTPD_METHOD_GET, "^/api/library/albums/[[:digit:]]+/tracks$", jsonapi_reply_library_album_tracks },
{ EVHTTP_REQ_PUT, "^/api/library/albums/[[:digit:]]+/tracks$", jsonapi_reply_library_album_tracks_put_byid }, { HTTPD_METHOD_PUT, "^/api/library/albums/[[:digit:]]+/tracks$", jsonapi_reply_library_album_tracks_put_byid },
{ EVHTTP_REQ_PUT, "^/api/library/tracks$", jsonapi_reply_library_tracks_put }, { HTTPD_METHOD_PUT, "^/api/library/tracks$", jsonapi_reply_library_tracks_put },
{ EVHTTP_REQ_GET, "^/api/library/tracks/[[:digit:]]+$", jsonapi_reply_library_tracks_get_byid }, { HTTPD_METHOD_GET, "^/api/library/tracks/[[:digit:]]+$", jsonapi_reply_library_tracks_get_byid },
{ EVHTTP_REQ_PUT, "^/api/library/tracks/[[:digit:]]+$", jsonapi_reply_library_tracks_put_byid }, { HTTPD_METHOD_PUT, "^/api/library/tracks/[[:digit:]]+$", jsonapi_reply_library_tracks_put_byid },
{ EVHTTP_REQ_GET, "^/api/library/tracks/[[:digit:]]+/playlists$", jsonapi_reply_library_track_playlists }, { HTTPD_METHOD_GET, "^/api/library/tracks/[[:digit:]]+/playlists$", jsonapi_reply_library_track_playlists },
{ EVHTTP_REQ_GET, "^/api/library/(genres|composers)$", jsonapi_reply_library_browse }, { HTTPD_METHOD_GET, "^/api/library/(genres|composers)$", jsonapi_reply_library_browse },
{ EVHTTP_REQ_GET, "^/api/library/(genres|composers)/.*$", jsonapi_reply_library_browseitem }, { HTTPD_METHOD_GET, "^/api/library/(genres|composers)/.*$", jsonapi_reply_library_browseitem },
{ EVHTTP_REQ_GET, "^/api/library/count$", jsonapi_reply_library_count }, { HTTPD_METHOD_GET, "^/api/library/count$", jsonapi_reply_library_count },
{ EVHTTP_REQ_GET, "^/api/library/files$", jsonapi_reply_library_files }, { HTTPD_METHOD_GET, "^/api/library/files$", jsonapi_reply_library_files },
{ EVHTTP_REQ_POST, "^/api/library/add$", jsonapi_reply_library_add }, { HTTPD_METHOD_POST, "^/api/library/add$", jsonapi_reply_library_add },
{ EVHTTP_REQ_PUT, "^/api/library/backup$", jsonapi_reply_library_backup }, { HTTPD_METHOD_PUT, "^/api/library/backup$", jsonapi_reply_library_backup },
{ EVHTTP_REQ_GET, "^/api/search$", jsonapi_reply_search }, { HTTPD_METHOD_GET, "^/api/search$", jsonapi_reply_search },
{ 0, NULL, NULL } { 0, NULL, NULL }
}; };
@ -4720,7 +4706,7 @@ static void
jsonapi_request(struct httpd_request *hreq) jsonapi_request(struct httpd_request *hreq)
{ {
; ;
struct evkeyvalq *headers; httpd_headers *headers;
int status_code; int status_code;
DPRINTF(E_DBG, L_WEB, "JSON api request: '%s'\n", hreq->uri); DPRINTF(E_DBG, L_WEB, "JSON api request: '%s'\n", hreq->uri);
@ -4747,8 +4733,8 @@ jsonapi_request(struct httpd_request *hreq)
switch (status_code) switch (status_code)
{ {
case HTTP_OK: /* 200 OK */ case HTTP_OK: /* 200 OK */
headers = evhttp_request_get_output_headers(hreq->req); headers = httpd_request_output_headers_get(hreq);
evhttp_add_header(headers, "Content-Type", "application/json"); httpd_header_add(headers, "Content-Type", "application/json");
httpd_send_reply(hreq, status_code, "OK", hreq->reply, HTTPD_SEND_NO_GZIP); httpd_send_reply(hreq, status_code, "OK", hreq->reply, HTTPD_SEND_NO_GZIP);
break; break;
case HTTP_NOCONTENT: /* 204 No Content */ case HTTP_NOCONTENT: /* 204 No Content */

163
src/httpd_libevhttp.c Normal file
View File

@ -0,0 +1,163 @@
#include <sys/queue.h>
#include <event2/http.h>
#include <event2/keyvalq_struct.h>
#include "httpd_internal.h"
int
httpd_connection_closecb_set(httpd_connection *conn, httpd_connection_closecb cb, void *arg)
{
evhttp_connection_set_closecb(conn, cb, arg);
return 0;
}
/*
int
httpd_connection_peer_get(char **addr, uint16_t *port, httpd_connection *conn)
{
return evhttp_connection_get_peer(conn, addr, port);
}
*/
void
httpd_connection_free(httpd_connection *conn)
{
evhttp_connection_free(conn);
}
httpd_connection *
httpd_request_connection_get(struct httpd_request *hreq)
{
return evhttp_request_get_connection(hreq->req);
}
/*
const char *
httpd_request_uri_get(httpd_request *req)
{
return evhttp_request_get_uri(req);
}
int
httpd_request_peer_get(char **addr, uint16_t *port, httpd_request *req)
{
httpd_connection *conn = httpd_request_connection_get(req);
if (!conn)
return -1;
return httpd_connection_peer_get(addr, port, conn);
}
*/
int
httpd_request_method_get(enum httpd_methods *method, struct httpd_request *hreq)
{
enum evhttp_cmd_type cmd = evhttp_request_get_command(hreq->req);
switch (cmd)
{
case EVHTTP_REQ_GET: *method = HTTPD_METHOD_GET; break;
case EVHTTP_REQ_POST: *method = HTTPD_METHOD_POST; break;
case EVHTTP_REQ_HEAD: *method = HTTPD_METHOD_HEAD; break;
case EVHTTP_REQ_PUT: *method = HTTPD_METHOD_PUT; break;
case EVHTTP_REQ_DELETE: *method = HTTPD_METHOD_DELETE; break;
case EVHTTP_REQ_OPTIONS: *method = HTTPD_METHOD_OPTIONS; break;
case EVHTTP_REQ_TRACE: *method = HTTPD_METHOD_TRACE; break;
case EVHTTP_REQ_CONNECT: *method = HTTPD_METHOD_CONNECT; break;
case EVHTTP_REQ_PATCH: *method = HTTPD_METHOD_PATCH; break;
default: *method = HTTPD_METHOD_GET; return -1;
}
return 0;
}
void
httpd_request_backend_free(struct httpd_request *hreq)
{
evhttp_request_free(hreq->req);
}
int
httpd_request_closecb_set(struct httpd_request *hreq, httpd_connection_closecb cb, void *arg)
{
httpd_connection *conn = httpd_request_connection_get(hreq);
if (!conn)
return -1;
return httpd_connection_closecb_set(conn, cb, arg);
}
const char *
httpd_query_value_find(httpd_query *query, const char *key)
{
return evhttp_find_header(query, key);
}
void
httpd_query_iterate(httpd_query *query, httpd_query_iteratecb cb, void *arg)
{
struct evkeyval *param;
TAILQ_FOREACH(param, query, next)
{
cb(param->key, param->value, arg);
}
}
const char *
httpd_header_find(httpd_headers *headers, const char *key)
{
return evhttp_find_header(headers, key);
}
void
httpd_header_remove(httpd_headers *headers, const char *key)
{
evhttp_remove_header(headers, key);
}
void
httpd_header_add(httpd_headers *headers, const char *key, const char *val)
{
evhttp_add_header(headers, key, val);
}
void
httpd_headers_clear(httpd_headers *headers)
{
evhttp_clear_headers(headers);
}
httpd_headers *
httpd_request_input_headers_get(struct httpd_request *hreq)
{
return evhttp_request_get_input_headers(hreq->req);
}
httpd_headers *
httpd_request_output_headers_get(struct httpd_request *hreq)
{
return evhttp_request_get_output_headers(hreq->req);
}
/*
void
httpd_reply_send(httpd_request *req, int code, const char *reason, struct evbuffer *evbuf)
{
evhttp_send_reply(req, code, reason, evbuf);
}
*/
void
httpd_reply_start_send(struct httpd_request *hreq, int code, const char *reason)
{
evhttp_send_reply_start(hreq->req, code, reason);
}
void
httpd_reply_chunk_send(struct httpd_request *hreq, struct evbuffer *evbuf, httpd_connection_chunkcb cb, void *arg)
{
evhttp_send_reply_chunk_with_cb(hreq->req, evbuf, cb, arg);
}
void
httpd_reply_end_send(struct httpd_request *hreq)
{
evhttp_send_reply_end(hreq->req);
}

View File

@ -160,7 +160,7 @@ static void
rsp_send_error(struct httpd_request *hreq, char *errmsg) rsp_send_error(struct httpd_request *hreq, char *errmsg)
{ {
struct evbuffer *evbuf; struct evbuffer *evbuf;
struct evkeyvalq *headers; httpd_headers *headers;
mxml_node_t *reply; mxml_node_t *reply;
mxml_node_t *status; mxml_node_t *status;
mxml_node_t *node; mxml_node_t *node;
@ -196,9 +196,9 @@ rsp_send_error(struct httpd_request *hreq, char *errmsg)
return; return;
} }
headers = evhttp_request_get_output_headers(hreq->req); headers = httpd_request_output_headers_get(hreq);
evhttp_add_header(headers, "Content-Type", "text/xml; charset=utf-8"); httpd_header_add(headers, "Content-Type", "text/xml; charset=utf-8");
evhttp_add_header(headers, "Connection", "close"); httpd_header_add(headers, "Connection", "close");
httpd_send_reply(hreq, HTTP_OK, "OK", evbuf, HTTPD_SEND_NO_GZIP); httpd_send_reply(hreq, HTTP_OK, "OK", evbuf, HTTPD_SEND_NO_GZIP);
@ -214,7 +214,7 @@ query_params_set(struct query_params *qp, struct httpd_request *hreq)
int ret; int ret;
qp->offset = 0; qp->offset = 0;
param = evhttp_find_header(hreq->query, "offset"); param = httpd_query_value_find(hreq->query, "offset");
if (param) if (param)
{ {
ret = safe_atoi32(param, &qp->offset); ret = safe_atoi32(param, &qp->offset);
@ -226,7 +226,7 @@ query_params_set(struct query_params *qp, struct httpd_request *hreq)
} }
qp->limit = 0; qp->limit = 0;
param = evhttp_find_header(hreq->query, "limit"); param = httpd_query_value_find(hreq->query, "limit");
if (param) if (param)
{ {
ret = safe_atoi32(param, &qp->limit); ret = safe_atoi32(param, &qp->limit);
@ -243,7 +243,7 @@ query_params_set(struct query_params *qp, struct httpd_request *hreq)
qp->idx_type = I_NONE; qp->idx_type = I_NONE;
qp->filter = NULL; qp->filter = NULL;
param = evhttp_find_header(hreq->query, "query"); param = httpd_query_value_find(hreq->query, "query");
if (param) if (param)
{ {
ret = snprintf(query, sizeof(query), "%s", param); ret = snprintf(query, sizeof(query), "%s", param);
@ -280,7 +280,7 @@ static void
rsp_send_reply(struct httpd_request *hreq, mxml_node_t *reply) rsp_send_reply(struct httpd_request *hreq, mxml_node_t *reply)
{ {
struct evbuffer *evbuf; struct evbuffer *evbuf;
struct evkeyvalq *headers; httpd_headers *headers;
evbuf = mxml_to_evbuf(reply); evbuf = mxml_to_evbuf(reply);
mxmlDelete(reply); mxmlDelete(reply);
@ -292,9 +292,9 @@ rsp_send_reply(struct httpd_request *hreq, mxml_node_t *reply)
return; return;
} }
headers = evhttp_request_get_output_headers(hreq->req); headers = httpd_request_output_headers_get(hreq);
evhttp_add_header(headers, "Content-Type", "text/xml; charset=utf-8"); httpd_header_add(headers, "Content-Type", "text/xml; charset=utf-8");
evhttp_add_header(headers, "Connection", "close"); httpd_header_add(headers, "Connection", "close");
httpd_send_reply(hreq, HTTP_OK, "OK", evbuf, 0); httpd_send_reply(hreq, HTTP_OK, "OK", evbuf, 0);
@ -488,7 +488,6 @@ rsp_reply_playlist(struct httpd_request *hreq)
{ {
struct query_params qp; struct query_params qp;
struct db_media_file_info dbmfi; struct db_media_file_info dbmfi;
struct evkeyvalq *headers;
const char *param; const char *param;
const char *ua; const char *ua;
const char *client_codecs; const char *client_codecs;
@ -522,7 +521,7 @@ rsp_reply_playlist(struct httpd_request *hreq)
qp.sort = S_NAME; qp.sort = S_NAME;
mode = F_FULL; mode = F_FULL;
param = evhttp_find_header(hreq->query, "type"); param = httpd_query_value_find(hreq->query, "type");
if (param) if (param)
{ {
if (strcasecmp(param, "full") == 0) if (strcasecmp(param, "full") == 0)
@ -586,10 +585,8 @@ rsp_reply_playlist(struct httpd_request *hreq)
/* Items block (all items) */ /* Items block (all items) */
while ((ret = db_query_fetch_file(&dbmfi, &qp)) == 0) while ((ret = db_query_fetch_file(&dbmfi, &qp)) == 0)
{ {
headers = evhttp_request_get_input_headers(hreq->req); ua = httpd_header_find(hreq->in_headers, "User-Agent");
client_codecs = httpd_header_find(hreq->in_headers, "Accept-Codecs");
ua = evhttp_find_header(headers, "User-Agent");
client_codecs = evhttp_find_header(headers, "Accept-Codecs");
transcode = transcode_needed(ua, client_codecs, dbmfi.codectype); transcode = transcode_needed(ua, client_codecs, dbmfi.codectype);

View File

@ -58,7 +58,7 @@ extern struct event_base *evbase_httpd;
// Linked list of mp3 streaming requests // Linked list of mp3 streaming requests
struct streaming_session { struct streaming_session {
struct evhttp_request *req; struct httpd_request *hreq;
struct streaming_session *next; struct streaming_session *next;
bool require_icy; // Client requested icy meta bool require_icy; // Client requested icy meta
@ -102,18 +102,15 @@ static char streaming_icy_title[STREAMING_ICY_METATITLELEN_MAX];
static void static void
streaming_close_cb(struct evhttp_connection *evcon, void *arg) streaming_close_cb(httpd_connection *conn, void *arg)
{ {
struct streaming_session *this; struct streaming_session *this;
struct streaming_session *session; struct streaming_session *session;
struct streaming_session *prev; struct streaming_session *prev;
char *address;
ev_uint16_t port;
this = (struct streaming_session *)arg; this = (struct streaming_session *)arg;
evhttp_connection_get_peer(evcon, &address, &port); DPRINTF(E_INFO, L_STREAMING, "Stopping mp3 streaming to %s:%d\n", this->hreq->peer_address, (int)this->hreq->peer_port);
DPRINTF(E_INFO, L_STREAMING, "Stopping mp3 streaming to %s:%d\n", address, (int)port);
pthread_mutex_lock(&streaming_sessions_lck); pthread_mutex_lock(&streaming_sessions_lck);
if (!streaming_sessions) if (!streaming_sessions)
@ -127,7 +124,7 @@ streaming_close_cb(struct evhttp_connection *evcon, void *arg)
prev = NULL; prev = NULL;
for (session = streaming_sessions; session; session = session->next) for (session = streaming_sessions; session; session = session->next)
{ {
if (session->req == this->req) if (session->hreq == this->hreq)
break; break;
prev = session; prev = session;
@ -135,7 +132,7 @@ streaming_close_cb(struct evhttp_connection *evcon, void *arg)
if (!session) if (!session)
{ {
DPRINTF(E_LOG, L_STREAMING, "Bug! Got a failure callback for an unknown stream (%s:%d)\n", address, (int)port); DPRINTF(E_LOG, L_STREAMING, "Bug! Got a failure callback for an unknown stream (%s:%d)\n", this->hreq->peer_address, (int)this->hreq->peer_port);
free(this); free(this);
pthread_mutex_unlock(&streaming_sessions_lck); pthread_mutex_unlock(&streaming_sessions_lck);
return; return;
@ -151,7 +148,7 @@ streaming_close_cb(struct evhttp_connection *evcon, void *arg)
// Valgrind says libevent doesn't free the request on disconnect (even though it owns it - libevent bug?), // Valgrind says libevent doesn't free the request on disconnect (even though it owns it - libevent bug?),
// so we do it with a reply end // so we do it with a reply end
evhttp_send_reply_end(session->req); httpd_reply_end_send(session->hreq);
free(session); free(session);
if (!streaming_sessions) if (!streaming_sessions)
@ -168,21 +165,14 @@ static void
streaming_end(void) streaming_end(void)
{ {
struct streaming_session *session; struct streaming_session *session;
struct evhttp_connection *evcon;
char *address;
ev_uint16_t port;
pthread_mutex_lock(&streaming_sessions_lck); pthread_mutex_lock(&streaming_sessions_lck);
for (session = streaming_sessions; streaming_sessions; session = streaming_sessions) for (session = streaming_sessions; streaming_sessions; session = streaming_sessions)
{ {
evcon = evhttp_request_get_connection(session->req); DPRINTF(E_INFO, L_STREAMING, "Force close stream to %s:%d\n", session->hreq->peer_address, (int)session->hreq->peer_port);
if (evcon)
{ httpd_request_closecb_set(session->hreq, NULL, NULL);
evhttp_connection_set_closecb(evcon, NULL, NULL); httpd_reply_end_send(session->hreq);
evhttp_connection_get_peer(evcon, &address, &port);
DPRINTF(E_INFO, L_STREAMING, "Force close stream to %s:%d\n", address, (int)port);
}
evhttp_send_reply_end(session->req);
streaming_sessions = session->next; streaming_sessions = session->next;
free(session); free(session);
@ -450,7 +440,7 @@ streaming_send_cb(evutil_socket_t fd, short event, void *arg)
free(splice_buf); free(splice_buf);
splice_buf = NULL; splice_buf = NULL;
evhttp_send_reply_chunk(session->req, evbuf); httpd_reply_chunk_send(session->hreq, evbuf, NULL, NULL);
if (session->next == NULL) if (session->next == NULL)
{ {
@ -465,11 +455,11 @@ streaming_send_cb(evutil_socket_t fd, short event, void *arg)
{ {
buf = evbuffer_pullup(streaming_encoded_data, -1); buf = evbuffer_pullup(streaming_encoded_data, -1);
evbuffer_add(evbuf, buf, len); evbuffer_add(evbuf, buf, len);
evhttp_send_reply_chunk(session->req, evbuf); httpd_reply_chunk_send(session->hreq, evbuf, NULL, NULL);
} }
else else
{ {
evhttp_send_reply_chunk(session->req, streaming_encoded_data); httpd_reply_chunk_send(session->hreq, streaming_encoded_data, NULL, NULL);
} }
session->bytes_sent += len; session->bytes_sent += len;
} }
@ -547,12 +537,9 @@ static void
streaming_request(struct httpd_request *hreq) streaming_request(struct httpd_request *hreq)
{ {
struct streaming_session *session; struct streaming_session *session;
struct evhttp_connection *evcon; httpd_headers *output_headers;
struct evkeyvalq *output_headers;
cfg_t *lib; cfg_t *lib;
const char *name; const char *name;
char *address;
ev_uint16_t port;
const char *param; const char *param;
bool require_icy = false; bool require_icy = false;
char buf[9]; char buf[9];
@ -561,45 +548,43 @@ streaming_request(struct httpd_request *hreq)
{ {
DPRINTF(E_LOG, L_STREAMING, "Got MP3 streaming request, but cannot encode to MP3\n"); DPRINTF(E_LOG, L_STREAMING, "Got MP3 streaming request, but cannot encode to MP3\n");
evhttp_send_error(hreq->req, HTTP_NOTFOUND, "Not Found"); httpd_send_error(hreq, HTTP_NOTFOUND, "Not Found");
return; return;
} }
evcon = evhttp_request_get_connection(hreq->req); param = httpd_header_find(hreq->in_headers, "Icy-MetaData");
evhttp_connection_get_peer(evcon, &address, &port);
param = evhttp_find_header( evhttp_request_get_input_headers(hreq->req), "Icy-MetaData");
if (param && strcmp(param, "1") == 0) if (param && strcmp(param, "1") == 0)
require_icy = true; require_icy = true;
DPRINTF(E_INFO, L_STREAMING, "Beginning mp3 streaming (with icy=%d, icy_metaint=%d) to %s:%d\n", require_icy, streaming_icy_metaint, address, (int)port); DPRINTF(E_INFO, L_STREAMING, "Beginning mp3 streaming (with icy=%d, icy_metaint=%d) to %s:%d\n", require_icy, streaming_icy_metaint, hreq->peer_address, (int)hreq->peer_port);
lib = cfg_getsec(cfg, "library"); lib = cfg_getsec(cfg, "library");
name = cfg_getstr(lib, "name"); name = cfg_getstr(lib, "name");
output_headers = evhttp_request_get_output_headers(hreq->req); output_headers = httpd_request_output_headers_get(hreq);
evhttp_add_header(output_headers, "Content-Type", "audio/mpeg"); httpd_header_add(output_headers, "Content-Type", "audio/mpeg");
evhttp_add_header(output_headers, "Server", PACKAGE_NAME "/" VERSION); httpd_header_add(output_headers, "Server", PACKAGE_NAME "/" VERSION);
evhttp_add_header(output_headers, "Cache-Control", "no-cache"); httpd_header_add(output_headers, "Cache-Control", "no-cache");
evhttp_add_header(output_headers, "Pragma", "no-cache"); httpd_header_add(output_headers, "Pragma", "no-cache");
evhttp_add_header(output_headers, "Expires", "Mon, 31 Aug 2015 06:00:00 GMT"); httpd_header_add(output_headers, "Expires", "Mon, 31 Aug 2015 06:00:00 GMT");
if (require_icy) if (require_icy)
{ {
++streaming_icy_clients; ++streaming_icy_clients;
evhttp_add_header(output_headers, "icy-name", name); httpd_header_add(output_headers, "icy-name", name);
snprintf(buf, sizeof(buf)-1, "%d", streaming_icy_metaint); snprintf(buf, sizeof(buf)-1, "%d", streaming_icy_metaint);
evhttp_add_header(output_headers, "icy-metaint", buf); httpd_header_add(output_headers, "icy-metaint", buf);
} }
evhttp_add_header(output_headers, "Access-Control-Allow-Origin", "*"); httpd_header_add(output_headers, "Access-Control-Allow-Origin", "*");
evhttp_add_header(output_headers, "Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS"); httpd_header_add(output_headers, "Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
evhttp_send_reply_start(hreq->req, HTTP_OK, "OK"); httpd_reply_start_send(hreq, HTTP_OK, "OK");
session = calloc(1, sizeof(struct streaming_session)); session = calloc(1, sizeof(struct streaming_session));
if (!session) if (!session)
{ {
DPRINTF(E_LOG, L_STREAMING, "Out of memory for streaming request\n"); DPRINTF(E_LOG, L_STREAMING, "Out of memory for streaming request\n");
evhttp_send_error(hreq->req, HTTP_SERVUNAVAIL, "Internal Server Error"); httpd_send_error(hreq, HTTP_SERVUNAVAIL, "Internal Server Error");
return; return;
} }
@ -611,7 +596,7 @@ streaming_request(struct httpd_request *hreq)
event_add(metaev, NULL); event_add(metaev, NULL);
} }
session->req = hreq->req; session->hreq = hreq;
session->next = streaming_sessions; session->next = streaming_sessions;
session->require_icy = require_icy; session->require_icy = require_icy;
session->bytes_sent = 0; session->bytes_sent = 0;
@ -619,7 +604,7 @@ streaming_request(struct httpd_request *hreq)
pthread_mutex_unlock(&streaming_sessions_lck); pthread_mutex_unlock(&streaming_sessions_lck);
evhttp_connection_set_closecb(evcon, streaming_close_cb, session); httpd_request_closecb_set(hreq, streaming_close_cb, session);
} }
static int static int