[httpd] More fixing up and consolidation

This commit is contained in:
ejurgensen 2022-12-22 16:37:07 +01:00
parent 573a628b37
commit 8826ea13f2
8 changed files with 402 additions and 413 deletions

View File

@ -290,18 +290,121 @@ modules_search(const char *path)
/* --------------------------- REQUEST HELPERS ------------------------------ */ /* --------------------------- REQUEST HELPERS ------------------------------ */
static void
uri_parsed_free(struct httpd_uri_parsed *parsed)
{
int i;
if (!parsed)
return;
free(parsed->uri_decoded);
free(parsed->path);
for (i = 0; i < ARRAY_SIZE(parsed->path_parts); i++)
{
free(parsed->path_parts[i]);
}
httpd_query_clear(&(parsed->query));
if (parsed->ev_uri)
evhttp_uri_free(parsed->ev_uri);
free(parsed);
}
static struct httpd_uri_parsed *
uri_parsed_create(const char *uri)
{
struct httpd_uri_parsed *parsed;
char *path = NULL;
const char *query;
char *path_part;
char *ptr;
int i;
int ret;
CHECK_NULL(L_HTTPD, parsed = calloc(1, sizeof(struct httpd_uri_parsed)));
parsed->uri = uri;
parsed->ev_uri = evhttp_uri_parse_with_flags(parsed->uri, EVHTTP_URI_NONCONFORMANT);
if (!parsed->ev_uri)
{
DPRINTF(E_LOG, L_HTTPD, "Could not parse request: '%s'\n", parsed->uri);
goto error;
}
parsed->uri_decoded = evhttp_uridecode(parsed->uri, 0, NULL);
if (!parsed->uri_decoded)
{
DPRINTF(E_LOG, L_HTTPD, "Could not URI decode request: '%s'\n", parsed->uri);
goto error;
}
query = evhttp_uri_get_query(parsed->ev_uri);
if (query && strchr(query, '='))
{
ret = evhttp_parse_query_str(query, &(parsed->query));
if (ret < 0)
{
DPRINTF(E_LOG, L_DAAP, "Invalid query '%s' in request: '%s'\n", query, parsed->uri);
goto error;
}
}
path = strdup(evhttp_uri_get_path(parsed->ev_uri));
if (!path)
{
DPRINTF(E_WARN, L_HTTPD, "No path in request: '%s'\n", parsed->uri);
return parsed;
}
parsed->path = evhttp_uridecode(path, 0, NULL);
if (!parsed->path)
{
DPRINTF(E_LOG, L_HTTPD, "Could not URI decode path: '%s'\n", path);
goto error;
}
path_part = strtok_r(path, "/", &ptr);
for (i = 0; (i < ARRAY_SIZE(parsed->path_parts) && path_part); i++)
{
parsed->path_parts[i] = evhttp_uridecode(path_part, 0, NULL);
path_part = strtok_r(NULL, "/", &ptr);
}
if (path_part)
{
// If "path_part" is not NULL, we have path tokens that could not be parsed into the "parsed->path_parts" array
DPRINTF(E_LOG, L_HTTPD, "URI path has too many components (%d): '%s'\n", i, parsed->path);
goto error;
}
free(path);
return parsed;
error:
free(path);
uri_parsed_free(parsed);
return NULL;
}
static void static void
request_unset(struct httpd_request *hreq) request_unset(struct httpd_request *hreq)
{ {
httpd_uri_free(hreq->uri_parsed); if (hreq->out_body)
evbuffer_free(hreq->out_body);
uri_parsed_free(hreq->uri_parsed);
} }
static void static void
request_set(struct httpd_request *hreq, httpd_backend *backend, const char *uri, const char *user_agent) request_set(struct httpd_request *hreq, httpd_backend *backend, const char *uri, const char *user_agent)
{ {
struct httpd_uri_map *map;
struct httpd_uri_parsed *uri_parsed; struct httpd_uri_parsed *uri_parsed;
struct httpd_module *module; struct httpd_uri_map *map;
int ret; int ret;
memset(hreq, 0, sizeof(struct httpd_request)); memset(hreq, 0, sizeof(struct httpd_request));
@ -311,9 +414,9 @@ request_set(struct httpd_request *hreq, httpd_backend *backend, const char *uri,
if (backend) if (backend)
{ {
hreq->uri = httpd_backend_uri_get(backend); hreq->uri = httpd_backend_uri_get(backend);
hreq->in_body = httpd_backend_input_buffer_get(backend);
hreq->in_headers = httpd_backend_input_headers_get(backend); hreq->in_headers = httpd_backend_input_headers_get(backend);
hreq->out_headers = httpd_backend_output_headers_get(backend); hreq->out_headers = httpd_backend_output_headers_get(backend);
hreq->in_body = httpd_backend_input_buffer_get(backend);
httpd_backend_method_get(&hreq->method, backend); httpd_backend_method_get(&hreq->method, backend);
httpd_backend_peer_get(&hreq->peer_address, &hreq->peer_port, backend); httpd_backend_peer_get(&hreq->peer_address, &hreq->peer_port, backend);
@ -325,7 +428,11 @@ request_set(struct httpd_request *hreq, httpd_backend *backend, const char *uri,
hreq->user_agent = user_agent; hreq->user_agent = user_agent;
} }
uri_parsed = httpd_uri_parse(hreq->uri); // Don't write directly to backend's buffer. This way we are sure we own the
// buffer even if there is no backend.
CHECK_NULL(L_HTTPD, hreq->out_body = evbuffer_new());
uri_parsed = uri_parsed_create(hreq->uri);
if (!uri_parsed) if (!uri_parsed)
{ {
return; return;
@ -335,13 +442,13 @@ request_set(struct httpd_request *hreq, httpd_backend *backend, const char *uri,
hreq->query = &(hreq->uri_parsed->query); hreq->query = &(hreq->uri_parsed->query);
// Path with e.g. /api -> JSON module // Path with e.g. /api -> JSON module
module = modules_search(uri_parsed->path); hreq->module = modules_search(uri_parsed->path);
if (!module) if (!hreq->module)
{ {
return; return;
} }
for (map = module->handlers; map->handler; map++) for (map = hreq->module->handlers; map->handler; map++)
{ {
// Check if handler supports the current http request method // Check if handler supports the current http request method
if (map->method && hreq->method && !(map->method & hreq->method)) if (map->method && hreq->method && !(map->method & hreq->method))
@ -868,9 +975,9 @@ httpd_gen_cb(httpd_backend *backend, void *arg)
goto out; goto out;
} }
if ((&hreq)->handler) if ((&hreq)->module)
{ {
(&hreq)->handler(&hreq); (&hreq)->module->request(&hreq);
} }
else else
{ {
@ -886,107 +993,6 @@ httpd_gen_cb(httpd_backend *backend, void *arg)
/* ------------------------------- HTTPD API -------------------------------- */ /* ------------------------------- HTTPD API -------------------------------- */
void
httpd_uri_free(struct httpd_uri_parsed *parsed)
{
int i;
if (!parsed)
return;
free(parsed->uri_decoded);
free(parsed->path);
for (i = 0; i < ARRAY_SIZE(parsed->path_parts); i++)
{
free(parsed->path_parts[i]);
}
httpd_query_clear(&(parsed->query));
if (parsed->ev_uri)
evhttp_uri_free(parsed->ev_uri);
free(parsed);
}
struct httpd_uri_parsed *
httpd_uri_parse(const char *uri)
{
struct httpd_uri_parsed *parsed;
char *path = NULL;
const char *query;
char *path_part;
char *ptr;
int i;
int ret;
CHECK_NULL(L_HTTPD, parsed = calloc(1, sizeof(struct httpd_uri_parsed)));
parsed->uri = uri;
parsed->ev_uri = evhttp_uri_parse_with_flags(parsed->uri, EVHTTP_URI_NONCONFORMANT);
if (!parsed->ev_uri)
{
DPRINTF(E_LOG, L_HTTPD, "Could not parse request: '%s'\n", parsed->uri);
goto error;
}
parsed->uri_decoded = evhttp_uridecode(parsed->uri, 0, NULL);
if (!parsed->uri_decoded)
{
DPRINTF(E_LOG, L_HTTPD, "Could not URI decode request: '%s'\n", parsed->uri);
goto error;
}
query = evhttp_uri_get_query(parsed->ev_uri);
if (query && strchr(query, '='))
{
ret = evhttp_parse_query_str(query, &(parsed->query));
if (ret < 0)
{
DPRINTF(E_LOG, L_DAAP, "Invalid query '%s' in request: '%s'\n", query, parsed->uri);
goto error;
}
}
path = strdup(evhttp_uri_get_path(parsed->ev_uri));
if (!path)
{
DPRINTF(E_WARN, L_HTTPD, "No path in request: '%s'\n", parsed->uri);
return parsed;
}
parsed->path = evhttp_uridecode(path, 0, NULL);
if (!parsed->path)
{
DPRINTF(E_LOG, L_HTTPD, "Could not URI decode path: '%s'\n", path);
goto error;
}
path_part = strtok_r(path, "/", &ptr);
for (i = 0; (i < ARRAY_SIZE(parsed->path_parts) && path_part); i++)
{
parsed->path_parts[i] = evhttp_uridecode(path_part, 0, NULL);
path_part = strtok_r(NULL, "/", &ptr);
}
if (path_part)
{
// If "path_part" is not NULL, we have path tokens that could not be parsed into the "parsed->path_parts" array
DPRINTF(E_LOG, L_HTTPD, "URI path has too many components (%d): '%s'\n", i, parsed->path);
goto error;
}
free(path);
return parsed;
error:
free(path);
httpd_uri_free(parsed);
return NULL;
}
void void
httpd_request_unset(struct httpd_request *hreq) httpd_request_unset(struct httpd_request *hreq)
{ {

View File

@ -88,7 +88,7 @@ artworkapi_reply_nowplaying(struct httpd_request *hreq)
if (ret != 0) if (ret != 0)
return HTTP_NOTFOUND; return HTTP_NOTFOUND;
ret = artwork_get_item(hreq->reply, id, max_w, max_h, 0); ret = artwork_get_item(hreq->out_body, id, max_w, max_h, 0);
return response_process(hreq, ret); return response_process(hreq, ret);
} }
@ -109,7 +109,7 @@ artworkapi_reply_item(struct httpd_request *hreq)
if (ret != 0) if (ret != 0)
return HTTP_BADREQUEST; return HTTP_BADREQUEST;
ret = artwork_get_item(hreq->reply, id, max_w, max_h, 0); ret = artwork_get_item(hreq->out_body, id, max_w, max_h, 0);
return response_process(hreq, ret); return response_process(hreq, ret);
} }
@ -130,7 +130,7 @@ artworkapi_reply_group(struct httpd_request *hreq)
if (ret != 0) if (ret != 0)
return HTTP_BADREQUEST; return HTTP_BADREQUEST;
ret = artwork_get_group(hreq->reply, id, max_w, max_h, 0); ret = artwork_get_group(hreq->out_body, id, max_w, max_h, 0);
return response_process(hreq, ret); return response_process(hreq, ret);
} }
@ -164,17 +164,15 @@ artworkapi_request(struct httpd_request *hreq)
return; return;
} }
CHECK_NULL(L_WEB, hreq->reply = evbuffer_new());
status_code = hreq->handler(hreq); status_code = hreq->handler(hreq);
switch (status_code) switch (status_code)
{ {
case HTTP_OK: /* 200 OK */ case HTTP_OK: /* 200 OK */
httpd_send_reply(hreq, status_code, "OK", hreq->reply, HTTPD_SEND_NO_GZIP); httpd_send_reply(hreq, status_code, "OK", hreq->out_body, HTTPD_SEND_NO_GZIP);
break; break;
case HTTP_NOCONTENT: /* 204 No Content */ case HTTP_NOCONTENT: /* 204 No Content */
httpd_send_reply(hreq, status_code, "No Content", hreq->reply, HTTPD_SEND_NO_GZIP); httpd_send_reply(hreq, status_code, "No Content", hreq->out_body, HTTPD_SEND_NO_GZIP);
break; break;
case HTTP_NOTMODIFIED: /* 304 Not Modified */ case HTTP_NOTMODIFIED: /* 304 Not Modified */
httpd_send_reply(hreq, HTTP_NOTMODIFIED, NULL, NULL, HTTPD_SEND_NO_GZIP); httpd_send_reply(hreq, HTTP_NOTMODIFIED, NULL, NULL, HTTPD_SEND_NO_GZIP);
@ -189,8 +187,6 @@ artworkapi_request(struct httpd_request *hreq)
default: default:
httpd_send_error(hreq, HTTP_INTERNAL, "Internal Server Error"); httpd_send_error(hreq, HTTP_INTERNAL, "Internal Server Error");
} }
evbuffer_free(hreq->reply);
} }
struct httpd_module httpd_artworkapi = struct httpd_module httpd_artworkapi =

View File

@ -670,17 +670,17 @@ daap_reply_send(struct httpd_request *hreq, enum daap_reply_result result)
switch (result) switch (result)
{ {
case DAAP_REPLY_LOGOUT: case DAAP_REPLY_LOGOUT:
httpd_send_reply(hreq, 204, "Logout Successful", hreq->reply, 0); httpd_send_reply(hreq, 204, "Logout Successful", hreq->out_body, 0);
break; break;
case DAAP_REPLY_NO_CONTENT: case DAAP_REPLY_NO_CONTENT:
httpd_send_reply(hreq, HTTP_NOCONTENT, "No Content", hreq->reply, HTTPD_SEND_NO_GZIP); httpd_send_reply(hreq, HTTP_NOCONTENT, "No Content", hreq->out_body, HTTPD_SEND_NO_GZIP);
break; break;
case DAAP_REPLY_OK: case DAAP_REPLY_OK:
httpd_send_reply(hreq, HTTP_OK, "OK", hreq->reply, 0); httpd_send_reply(hreq, HTTP_OK, "OK", hreq->out_body, 0);
break; break;
case DAAP_REPLY_OK_NO_GZIP: case DAAP_REPLY_OK_NO_GZIP:
case DAAP_REPLY_ERROR: case DAAP_REPLY_ERROR:
httpd_send_reply(hreq, HTTP_OK, "OK", hreq->reply, HTTPD_SEND_NO_GZIP); httpd_send_reply(hreq, HTTP_OK, "OK", hreq->out_body, HTTPD_SEND_NO_GZIP);
break; break;
case DAAP_REPLY_FORBIDDEN: case DAAP_REPLY_FORBIDDEN:
httpd_send_error(hreq, 403, "Forbidden"); httpd_send_error(hreq, 403, "Forbidden");
@ -857,9 +857,9 @@ daap_reply_server_info(struct httpd_request *hreq)
// Create container // Create container
len = evbuffer_get_length(content); len = evbuffer_get_length(content);
dmap_add_container(hreq->reply, "msrv", len); dmap_add_container(hreq->out_body, "msrv", len);
CHECK_ERR(L_DAAP, evbuffer_add_buffer(hreq->reply, content)); CHECK_ERR(L_DAAP, evbuffer_add_buffer(hreq->out_body, content));
evbuffer_free(content); evbuffer_free(content);
@ -880,19 +880,19 @@ daap_reply_content_codes(struct httpd_request *hreq)
for (i = 0; i < nfields; i++) for (i = 0; i < nfields; i++)
len += 8 + 12 + 10 + 8 + strlen(dmap_fields[i].desc); len += 8 + 12 + 10 + 8 + strlen(dmap_fields[i].desc);
CHECK_ERR(L_DAAP, evbuffer_expand(hreq->reply, len + 8)); CHECK_ERR(L_DAAP, evbuffer_expand(hreq->out_body, len + 8));
dmap_add_container(hreq->reply, "mccr", len); dmap_add_container(hreq->out_body, "mccr", len);
dmap_add_int(hreq->reply, "mstt", 200); dmap_add_int(hreq->out_body, "mstt", 200);
for (i = 0; i < nfields; i++) for (i = 0; i < nfields; i++)
{ {
len = 12 + 10 + 8 + strlen(dmap_fields[i].desc); len = 12 + 10 + 8 + strlen(dmap_fields[i].desc);
dmap_add_container(hreq->reply, "mdcl", len); dmap_add_container(hreq->out_body, "mdcl", len);
dmap_add_string(hreq->reply, "mcnm", dmap_fields[i].tag); /* 12 */ dmap_add_string(hreq->out_body, "mcnm", dmap_fields[i].tag); /* 12 */
dmap_add_string(hreq->reply, "mcna", dmap_fields[i].desc); /* 8 + strlen(desc) */ dmap_add_string(hreq->out_body, "mcna", dmap_fields[i].desc); /* 8 + strlen(desc) */
dmap_add_short(hreq->reply, "mcty", dmap_fields[i].type); /* 10 */ dmap_add_short(hreq->out_body, "mcty", dmap_fields[i].type); /* 10 */
} }
return DAAP_REPLY_OK; return DAAP_REPLY_OK;
@ -908,7 +908,7 @@ daap_reply_login(struct httpd_request *hreq)
int request_session_id; int request_session_id;
int ret; int ret;
CHECK_ERR(L_DAAP, evbuffer_expand(hreq->reply, 32)); CHECK_ERR(L_DAAP, evbuffer_expand(hreq->out_body, 32));
param = httpd_query_value_find(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))
@ -957,13 +957,13 @@ daap_reply_login(struct httpd_request *hreq)
session = daap_session_add(adhoc->is_remote, request_session_id); session = daap_session_add(adhoc->is_remote, request_session_id);
if (!session) if (!session)
{ {
dmap_error_make(hreq->reply, "mlog", "Could not start session"); dmap_error_make(hreq->out_body, "mlog", "Could not start session");
return DAAP_REPLY_ERROR; return DAAP_REPLY_ERROR;
} }
dmap_add_container(hreq->reply, "mlog", 24); dmap_add_container(hreq->out_body, "mlog", 24);
dmap_add_int(hreq->reply, "mstt", 200); /* 12 */ dmap_add_int(hreq->out_body, "mstt", 200); /* 12 */
dmap_add_int(hreq->reply, "mlid", session->id); /* 12 */ dmap_add_int(hreq->out_body, "mlid", session->id); /* 12 */
return DAAP_REPLY_OK; return DAAP_REPLY_OK;
} }
@ -1009,18 +1009,18 @@ daap_reply_update(struct httpd_request *hreq)
{ {
DPRINTF(E_LOG, L_DAAP, "Parameter revision-number not an integer\n"); DPRINTF(E_LOG, L_DAAP, "Parameter revision-number not an integer\n");
dmap_error_make(hreq->reply, "mupd", "Invalid request"); dmap_error_make(hreq->out_body, "mupd", "Invalid request");
return DAAP_REPLY_ERROR; return DAAP_REPLY_ERROR;
} }
if (reqd_rev == 1) /* Or revision is not valid */ if (reqd_rev == 1) /* Or revision is not valid */
{ {
CHECK_ERR(L_DAAP, evbuffer_expand(hreq->reply, 32)); CHECK_ERR(L_DAAP, evbuffer_expand(hreq->out_body, 32));
/* Send back current revision */ /* Send back current revision */
dmap_add_container(hreq->reply, "mupd", 24); dmap_add_container(hreq->out_body, "mupd", 24);
dmap_add_int(hreq->reply, "mstt", 200); /* 12 */ dmap_add_int(hreq->out_body, "mstt", 200); /* 12 */
dmap_add_int(hreq->reply, "musr", current_rev); /* 12 */ dmap_add_int(hreq->out_body, "musr", current_rev); /* 12 */
return DAAP_REPLY_OK; return DAAP_REPLY_OK;
} }
@ -1031,7 +1031,7 @@ daap_reply_update(struct httpd_request *hreq)
{ {
DPRINTF(E_LOG, L_DAAP, "Out of memory for update request\n"); DPRINTF(E_LOG, L_DAAP, "Out of memory for update request\n");
dmap_error_make(hreq->reply, "mupd", "Out of memory"); dmap_error_make(hreq->out_body, "mupd", "Out of memory");
return DAAP_REPLY_ERROR; return DAAP_REPLY_ERROR;
} }
@ -1047,7 +1047,7 @@ daap_reply_update(struct httpd_request *hreq)
{ {
DPRINTF(E_LOG, L_DAAP, "Out of memory for update request event\n"); DPRINTF(E_LOG, L_DAAP, "Out of memory for update request event\n");
dmap_error_make(hreq->reply, "mupd", "Could not register timer"); dmap_error_make(hreq->out_body, "mupd", "Could not register timer");
update_free(ur); update_free(ur);
return DAAP_REPLY_ERROR; return DAAP_REPLY_ERROR;
} }
@ -1090,7 +1090,7 @@ daap_reply_dblist(struct httpd_request *hreq)
CHECK_NULL(L_DAAP, content = evbuffer_new()); CHECK_NULL(L_DAAP, content = evbuffer_new());
CHECK_NULL(L_DAAP, item = evbuffer_new()); CHECK_NULL(L_DAAP, item = evbuffer_new());
CHECK_ERR(L_DAAP, evbuffer_expand(item, 512)); CHECK_ERR(L_DAAP, evbuffer_expand(item, 512));
CHECK_ERR(L_DAAP, evbuffer_expand(hreq->reply, 1024)); CHECK_ERR(L_DAAP, evbuffer_expand(hreq->out_body, 1024));
// Add db entry for library with dbid = 1 // Add db entry for library with dbid = 1
dmap_add_int(item, "miid", 1); dmap_add_int(item, "miid", 1);
@ -1133,14 +1133,14 @@ daap_reply_dblist(struct httpd_request *hreq)
// Create container // Create container
len = evbuffer_get_length(content); len = evbuffer_get_length(content);
dmap_add_container(hreq->reply, "avdb", len + 53); dmap_add_container(hreq->out_body, "avdb", len + 53);
dmap_add_int(hreq->reply, "mstt", 200); /* 12 */ dmap_add_int(hreq->out_body, "mstt", 200); /* 12 */
dmap_add_char(hreq->reply, "muty", 0); /* 9 */ dmap_add_char(hreq->out_body, "muty", 0); /* 9 */
dmap_add_int(hreq->reply, "mtco", 2); /* 12 */ dmap_add_int(hreq->out_body, "mtco", 2); /* 12 */
dmap_add_int(hreq->reply, "mrco", 2); /* 12 */ dmap_add_int(hreq->out_body, "mrco", 2); /* 12 */
dmap_add_container(hreq->reply, "mlcl", len); /* 8 */ dmap_add_container(hreq->out_body, "mlcl", len); /* 8 */
CHECK_ERR(L_DAAP, evbuffer_add_buffer(hreq->reply, content)); CHECK_ERR(L_DAAP, evbuffer_add_buffer(hreq->out_body, content));
evbuffer_free(item); evbuffer_free(item);
evbuffer_free(content); evbuffer_free(content);
@ -1195,7 +1195,7 @@ daap_reply_songlist_generic(struct httpd_request *hreq, int playlist)
CHECK_NULL(L_DAAP, songlist = evbuffer_new()); CHECK_NULL(L_DAAP, songlist = evbuffer_new());
CHECK_NULL(L_DAAP, song = evbuffer_new()); CHECK_NULL(L_DAAP, song = evbuffer_new());
CHECK_NULL(L_DAAP, sctx = daap_sort_context_new()); CHECK_NULL(L_DAAP, sctx = daap_sort_context_new());
CHECK_ERR(L_DAAP, evbuffer_expand(hreq->reply, 61)); CHECK_ERR(L_DAAP, evbuffer_expand(hreq->out_body, 61));
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));
@ -1223,7 +1223,7 @@ daap_reply_songlist_generic(struct httpd_request *hreq, int playlist)
{ {
DPRINTF(E_LOG, L_DAAP, "Could not start query\n"); DPRINTF(E_LOG, L_DAAP, "Could not start query\n");
dmap_error_make(hreq->reply, tag, "Could not start query"); dmap_error_make(hreq->out_body, tag, "Could not start query");
goto error; goto error;
} }
@ -1288,13 +1288,13 @@ daap_reply_songlist_generic(struct httpd_request *hreq, int playlist)
if (ret == -100) if (ret == -100)
{ {
dmap_error_make(hreq->reply, tag, "Out of memory"); dmap_error_make(hreq->out_body, tag, "Out of memory");
goto error; goto error;
} }
else if (ret < 0) else if (ret < 0)
{ {
DPRINTF(E_LOG, L_DAAP, "Error fetching results\n"); DPRINTF(E_LOG, L_DAAP, "Error fetching results\n");
dmap_error_make(hreq->reply, tag, "Error fetching query results"); dmap_error_make(hreq->out_body, tag, "Error fetching query results");
goto error; goto error;
} }
@ -1303,25 +1303,25 @@ daap_reply_songlist_generic(struct httpd_request *hreq, int playlist)
if (sort_headers) if (sort_headers)
{ {
daap_sort_finalize(sctx); daap_sort_finalize(sctx);
dmap_add_container(hreq->reply, tag, len + evbuffer_get_length(sctx->headerlist) + 61); dmap_add_container(hreq->out_body, tag, len + evbuffer_get_length(sctx->headerlist) + 61);
} }
else else
dmap_add_container(hreq->reply, tag, len + 53); dmap_add_container(hreq->out_body, tag, len + 53);
dmap_add_int(hreq->reply, "mstt", 200); /* 12 */ dmap_add_int(hreq->out_body, "mstt", 200); /* 12 */
dmap_add_char(hreq->reply, "muty", 0); /* 9 */ dmap_add_char(hreq->out_body, "muty", 0); /* 9 */
dmap_add_int(hreq->reply, "mtco", qp.results); /* 12 */ dmap_add_int(hreq->out_body, "mtco", qp.results); /* 12 */
dmap_add_int(hreq->reply, "mrco", nsongs); /* 12 */ dmap_add_int(hreq->out_body, "mrco", nsongs); /* 12 */
dmap_add_container(hreq->reply, "mlcl", len); /* 8 */ dmap_add_container(hreq->out_body, "mlcl", len); /* 8 */
CHECK_ERR(L_DAAP, evbuffer_add_buffer(hreq->reply, songlist)); CHECK_ERR(L_DAAP, evbuffer_add_buffer(hreq->out_body, songlist));
if (sort_headers) if (sort_headers)
{ {
len = evbuffer_get_length(sctx->headerlist); len = evbuffer_get_length(sctx->headerlist);
dmap_add_container(hreq->reply, "mshl", len); /* 8 */ dmap_add_container(hreq->out_body, "mshl", len); /* 8 */
CHECK_ERR(L_DAAP, evbuffer_add_buffer(hreq->reply, sctx->headerlist)); CHECK_ERR(L_DAAP, evbuffer_add_buffer(hreq->out_body, sctx->headerlist));
} }
free(meta); free(meta);
@ -1357,7 +1357,7 @@ daap_reply_plsonglist(struct httpd_request *hreq)
ret = safe_atoi32(hreq->uri_parsed->path_parts[3], &playlist); ret = safe_atoi32(hreq->uri_parsed->path_parts[3], &playlist);
if (ret < 0) if (ret < 0)
{ {
dmap_error_make(hreq->reply, "apso", "Invalid playlist ID"); dmap_error_make(hreq->out_body, "apso", "Invalid playlist ID");
return DAAP_REPLY_ERROR; return DAAP_REPLY_ERROR;
} }
@ -1403,7 +1403,7 @@ daap_reply_playlists(struct httpd_request *hreq)
ret = safe_atoi32(hreq->uri_parsed->path_parts[1], &database); ret = safe_atoi32(hreq->uri_parsed->path_parts[1], &database);
if (ret < 0) if (ret < 0)
{ {
dmap_error_make(hreq->reply, "aply", "Invalid database ID"); dmap_error_make(hreq->out_body, "aply", "Invalid database ID");
return DAAP_REPLY_ERROR; return DAAP_REPLY_ERROR;
} }
@ -1412,7 +1412,7 @@ daap_reply_playlists(struct httpd_request *hreq)
CHECK_NULL(L_DAAP, playlistlist = evbuffer_new()); CHECK_NULL(L_DAAP, playlistlist = evbuffer_new());
CHECK_NULL(L_DAAP, playlist = evbuffer_new()); CHECK_NULL(L_DAAP, playlist = evbuffer_new());
CHECK_ERR(L_DAAP, evbuffer_expand(hreq->reply, 61)); CHECK_ERR(L_DAAP, evbuffer_expand(hreq->out_body, 61));
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));
@ -1429,7 +1429,7 @@ daap_reply_playlists(struct httpd_request *hreq)
{ {
DPRINTF(E_LOG, L_DAAP, "Failed to parse meta parameter in DAAP query\n"); DPRINTF(E_LOG, L_DAAP, "Failed to parse meta parameter in DAAP query\n");
dmap_error_make(hreq->reply, "aply", "Failed to parse query"); dmap_error_make(hreq->out_body, "aply", "Failed to parse query");
goto error; goto error;
} }
@ -1438,7 +1438,7 @@ daap_reply_playlists(struct httpd_request *hreq)
{ {
DPRINTF(E_LOG, L_DAAP, "Could not start query\n"); DPRINTF(E_LOG, L_DAAP, "Could not start query\n");
dmap_error_make(hreq->reply, "aply", "Could not start query"); dmap_error_make(hreq->out_body, "aply", "Could not start query");
goto error; goto error;
} }
@ -1554,26 +1554,26 @@ daap_reply_playlists(struct httpd_request *hreq)
if (ret == -100) if (ret == -100)
{ {
dmap_error_make(hreq->reply, "aply", "Out of memory"); dmap_error_make(hreq->out_body, "aply", "Out of memory");
goto error; goto error;
} }
else if (ret < 0) else if (ret < 0)
{ {
DPRINTF(E_LOG, L_DAAP, "Error fetching results\n"); DPRINTF(E_LOG, L_DAAP, "Error fetching results\n");
dmap_error_make(hreq->reply, "aply", "Error fetching query results"); dmap_error_make(hreq->out_body, "aply", "Error fetching query results");
goto error; goto error;
} }
/* Add header to evbuf, add playlistlist to evbuf */ /* Add header to evbuf, add playlistlist to evbuf */
len = evbuffer_get_length(playlistlist); len = evbuffer_get_length(playlistlist);
dmap_add_container(hreq->reply, "aply", len + 53); dmap_add_container(hreq->out_body, "aply", len + 53);
dmap_add_int(hreq->reply, "mstt", 200); /* 12 */ dmap_add_int(hreq->out_body, "mstt", 200); /* 12 */
dmap_add_char(hreq->reply, "muty", 0); /* 9 */ dmap_add_char(hreq->out_body, "muty", 0); /* 9 */
dmap_add_int(hreq->reply, "mtco", qp.results); /* 12 */ dmap_add_int(hreq->out_body, "mtco", qp.results); /* 12 */
dmap_add_int(hreq->reply,"mrco", npls); /* 12 */ dmap_add_int(hreq->out_body,"mrco", npls); /* 12 */
dmap_add_container(hreq->reply, "mlcl", len); dmap_add_container(hreq->out_body, "mlcl", len);
CHECK_ERR(L_DAAP, evbuffer_add_buffer(hreq->reply, playlistlist)); CHECK_ERR(L_DAAP, evbuffer_add_buffer(hreq->out_body, playlistlist));
free(meta); free(meta);
evbuffer_free(playlist); evbuffer_free(playlist);
@ -1638,7 +1638,7 @@ daap_reply_groups(struct httpd_request *hreq)
CHECK_NULL(L_DAAP, grouplist = evbuffer_new()); CHECK_NULL(L_DAAP, grouplist = evbuffer_new());
CHECK_NULL(L_DAAP, group = evbuffer_new()); CHECK_NULL(L_DAAP, group = evbuffer_new());
CHECK_NULL(L_DAAP, sctx = daap_sort_context_new()); CHECK_NULL(L_DAAP, sctx = daap_sort_context_new());
CHECK_ERR(L_DAAP, evbuffer_expand(hreq->reply, 61)); CHECK_ERR(L_DAAP, evbuffer_expand(hreq->out_body, 61));
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));
@ -1655,7 +1655,7 @@ daap_reply_groups(struct httpd_request *hreq)
{ {
DPRINTF(E_LOG, L_DAAP, "Failed to parse meta parameter in DAAP query\n"); DPRINTF(E_LOG, L_DAAP, "Failed to parse meta parameter in DAAP query\n");
dmap_error_make(hreq->reply, tag, "Failed to parse query"); dmap_error_make(hreq->out_body, tag, "Failed to parse query");
goto error; goto error;
} }
@ -1664,7 +1664,7 @@ daap_reply_groups(struct httpd_request *hreq)
{ {
DPRINTF(E_LOG, L_DAAP, "Could not start query\n"); DPRINTF(E_LOG, L_DAAP, "Could not start query\n");
dmap_error_make(hreq->reply, tag, "Could not start query"); dmap_error_make(hreq->out_body, tag, "Could not start query");
goto error; goto error;
} }
@ -1756,13 +1756,13 @@ daap_reply_groups(struct httpd_request *hreq)
if (ret == -100) if (ret == -100)
{ {
dmap_error_make(hreq->reply, tag, "Out of memory"); dmap_error_make(hreq->out_body, tag, "Out of memory");
goto error; goto error;
} }
else if (ret < 0) else if (ret < 0)
{ {
DPRINTF(E_LOG, L_DAAP, "Error fetching results\n"); DPRINTF(E_LOG, L_DAAP, "Error fetching results\n");
dmap_error_make(hreq->reply, tag, "Error fetching query results"); dmap_error_make(hreq->out_body, tag, "Error fetching query results");
goto error; goto error;
} }
@ -1771,25 +1771,25 @@ daap_reply_groups(struct httpd_request *hreq)
if (sort_headers) if (sort_headers)
{ {
daap_sort_finalize(sctx); daap_sort_finalize(sctx);
dmap_add_container(hreq->reply, tag, len + evbuffer_get_length(sctx->headerlist) + 61); dmap_add_container(hreq->out_body, tag, len + evbuffer_get_length(sctx->headerlist) + 61);
} }
else else
dmap_add_container(hreq->reply, tag, len + 53); dmap_add_container(hreq->out_body, tag, len + 53);
dmap_add_int(hreq->reply, "mstt", 200); /* 12 */ dmap_add_int(hreq->out_body, "mstt", 200); /* 12 */
dmap_add_char(hreq->reply, "muty", 0); /* 9 */ dmap_add_char(hreq->out_body, "muty", 0); /* 9 */
dmap_add_int(hreq->reply, "mtco", qp.results); /* 12 */ dmap_add_int(hreq->out_body, "mtco", qp.results); /* 12 */
dmap_add_int(hreq->reply,"mrco", ngrp); /* 12 */ dmap_add_int(hreq->out_body,"mrco", ngrp); /* 12 */
dmap_add_container(hreq->reply, "mlcl", len); /* 8 */ dmap_add_container(hreq->out_body, "mlcl", len); /* 8 */
CHECK_ERR(L_DAAP, evbuffer_add_buffer(hreq->reply, grouplist)); CHECK_ERR(L_DAAP, evbuffer_add_buffer(hreq->out_body, grouplist));
if (sort_headers) if (sort_headers)
{ {
len = evbuffer_get_length(sctx->headerlist); len = evbuffer_get_length(sctx->headerlist);
dmap_add_container(hreq->reply, "mshl", len); /* 8 */ dmap_add_container(hreq->out_body, "mshl", len); /* 8 */
CHECK_ERR(L_DAAP, evbuffer_add_buffer(hreq->reply, sctx->headerlist)); CHECK_ERR(L_DAAP, evbuffer_add_buffer(hreq->out_body, sctx->headerlist));
} }
free(meta); free(meta);
@ -1847,13 +1847,13 @@ daap_reply_browse(struct httpd_request *hreq)
else else
{ {
DPRINTF(E_LOG, L_DAAP, "Invalid DAAP browse request type '%s'\n", hreq->uri_parsed->path_parts[3]); DPRINTF(E_LOG, L_DAAP, "Invalid DAAP browse request type '%s'\n", hreq->uri_parsed->path_parts[3]);
dmap_error_make(hreq->reply, "abro", "Invalid browse type"); dmap_error_make(hreq->out_body, "abro", "Invalid browse type");
return DAAP_REPLY_ERROR; return DAAP_REPLY_ERROR;
} }
CHECK_NULL(L_DAAP, itemlist = evbuffer_new()); CHECK_NULL(L_DAAP, itemlist = evbuffer_new());
CHECK_NULL(L_DAAP, sctx = daap_sort_context_new()); CHECK_NULL(L_DAAP, sctx = daap_sort_context_new());
CHECK_ERR(L_DAAP, evbuffer_expand(hreq->reply, 52)); CHECK_ERR(L_DAAP, evbuffer_expand(hreq->out_body, 52));
CHECK_ERR(L_DAAP, evbuffer_expand(itemlist, 1024)); // Just a starting alloc, it'll expand as needed CHECK_ERR(L_DAAP, evbuffer_expand(itemlist, 1024)); // Just a starting alloc, it'll expand as needed
ret = db_query_start(&qp); ret = db_query_start(&qp);
@ -1861,7 +1861,7 @@ daap_reply_browse(struct httpd_request *hreq)
{ {
DPRINTF(E_LOG, L_DAAP, "Could not start query\n"); DPRINTF(E_LOG, L_DAAP, "Could not start query\n");
dmap_error_make(hreq->reply, "abro", "Could not start query"); dmap_error_make(hreq->out_body, "abro", "Could not start query");
goto error; goto error;
} }
@ -1889,7 +1889,7 @@ daap_reply_browse(struct httpd_request *hreq)
{ {
DPRINTF(E_LOG, L_DAAP, "Error fetching/building results\n"); DPRINTF(E_LOG, L_DAAP, "Error fetching/building results\n");
dmap_error_make(hreq->reply, "abro", "Error fetching/building query results"); dmap_error_make(hreq->out_body, "abro", "Error fetching/building query results");
goto error; goto error;
} }
@ -1897,24 +1897,24 @@ daap_reply_browse(struct httpd_request *hreq)
if (sort_headers) if (sort_headers)
{ {
daap_sort_finalize(sctx); daap_sort_finalize(sctx);
dmap_add_container(hreq->reply, "abro", len + evbuffer_get_length(sctx->headerlist) + 52); dmap_add_container(hreq->out_body, "abro", len + evbuffer_get_length(sctx->headerlist) + 52);
} }
else else
dmap_add_container(hreq->reply, "abro", len + 44); dmap_add_container(hreq->out_body, "abro", len + 44);
dmap_add_int(hreq->reply, "mstt", 200); /* 12 */ dmap_add_int(hreq->out_body, "mstt", 200); /* 12 */
dmap_add_int(hreq->reply, "mtco", qp.results); /* 12 */ dmap_add_int(hreq->out_body, "mtco", qp.results); /* 12 */
dmap_add_int(hreq->reply, "mrco", nitems); /* 12 */ dmap_add_int(hreq->out_body, "mrco", nitems); /* 12 */
dmap_add_container(hreq->reply, tag, len); /* 8 */ dmap_add_container(hreq->out_body, tag, len); /* 8 */
CHECK_ERR(L_DAAP, evbuffer_add_buffer(hreq->reply, itemlist)); CHECK_ERR(L_DAAP, evbuffer_add_buffer(hreq->out_body, itemlist));
if (sort_headers) if (sort_headers)
{ {
len = evbuffer_get_length(sctx->headerlist); len = evbuffer_get_length(sctx->headerlist);
dmap_add_container(hreq->reply, "mshl", len); /* 8 */ dmap_add_container(hreq->out_body, "mshl", len); /* 8 */
CHECK_ERR(L_DAAP, evbuffer_add_buffer(hreq->reply, sctx->headerlist)); CHECK_ERR(L_DAAP, evbuffer_add_buffer(hreq->out_body, sctx->headerlist));
} }
daap_sort_context_free(sctx); daap_sort_context_free(sctx);
@ -1984,11 +1984,11 @@ daap_reply_extra_data(struct httpd_request *hreq)
} }
if (strcmp(hreq->uri_parsed->path_parts[2], "groups") == 0) if (strcmp(hreq->uri_parsed->path_parts[2], "groups") == 0)
ret = artwork_get_group(hreq->reply, id, max_w, max_h, 0); ret = artwork_get_group(hreq->out_body, id, max_w, max_h, 0);
else if (strcmp(hreq->uri_parsed->path_parts[2], "items") == 0) else if (strcmp(hreq->uri_parsed->path_parts[2], "items") == 0)
ret = artwork_get_item(hreq->reply, id, max_w, max_h, 0); ret = artwork_get_item(hreq->out_body, id, max_w, max_h, 0);
len = evbuffer_get_length(hreq->reply); len = evbuffer_get_length(hreq->out_body);
switch (ret) switch (ret)
{ {
@ -2002,7 +2002,7 @@ daap_reply_extra_data(struct httpd_request *hreq)
default: default:
if (len > 0) if (len > 0)
evbuffer_drain(hreq->reply, len); evbuffer_drain(hreq->out_body, len);
goto no_artwork; goto no_artwork;
} }
@ -2112,16 +2112,16 @@ daap_reply_dmap_test(struct httpd_request *hreq)
dmap_add_field(test, &dmap_TST8, buf, 0); dmap_add_field(test, &dmap_TST8, buf, 0);
dmap_add_field(test, &dmap_TST9, buf, 0); dmap_add_field(test, &dmap_TST9, buf, 0);
dmap_add_container(hreq->reply, dmap_TEST.tag, evbuffer_get_length(test)); dmap_add_container(hreq->out_body, dmap_TEST.tag, evbuffer_get_length(test));
ret = evbuffer_add_buffer(hreq->reply, test); ret = evbuffer_add_buffer(hreq->out_body, test);
evbuffer_free(test); evbuffer_free(test);
if (ret < 0) if (ret < 0)
{ {
DPRINTF(E_LOG, L_DAAP, "Could not add test results to DMAP test reply\n"); DPRINTF(E_LOG, L_DAAP, "Could not add test results to DMAP test reply\n");
dmap_error_make(hreq->reply, dmap_TEST.tag, "Out of memory"); dmap_error_make(hreq->out_body, dmap_TEST.tag, "Out of memory");
return DAAP_REPLY_ERROR; return DAAP_REPLY_ERROR;
} }
@ -2265,18 +2265,13 @@ daap_request(struct httpd_request *hreq)
// video/<type> Content-Type as expected by clients like Front Row. // video/<type> Content-Type as expected by clients like Front Row.
httpd_header_add(hreq->out_headers, "Content-Type", "application/x-dmap-tagged"); httpd_header_add(hreq->out_headers, "Content-Type", "application/x-dmap-tagged");
// Now we create the actual reply
CHECK_NULL(L_DAAP, hreq->reply = evbuffer_new());
// Try the cache // Try the cache
ret = cache_daap_get(hreq->reply, hreq->uri); ret = cache_daap_get(hreq->out_body, hreq->uri);
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
httpd_header_add(hreq->out_headers, "Content-Encoding", "gzip"); httpd_header_add(hreq->out_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->out_body, HTTPD_SEND_NO_GZIP); // TODO not all want this reply
evbuffer_free(hreq->reply);
return; return;
} }
@ -2294,8 +2289,6 @@ daap_request(struct httpd_request *hreq)
if (ret == DAAP_REPLY_OK && msec > cache_daap_threshold() && hreq->user_agent) if (ret == DAAP_REPLY_OK && msec > cache_daap_threshold() && hreq->user_agent)
cache_daap_add(hreq->uri, hreq->user_agent, ((struct daap_session *)hreq->extra_data)->is_remote, msec); cache_daap_add(hreq->uri, hreq->user_agent, ((struct daap_session *)hreq->extra_data)->is_remote, msec);
evbuffer_free(hreq->reply);
} }
int int
@ -2316,13 +2309,13 @@ struct evbuffer *
daap_reply_build(const char *uri, const char *user_agent, int is_remote) daap_reply_build(const char *uri, const char *user_agent, int is_remote)
{ {
struct httpd_request hreq; struct httpd_request hreq;
struct evbuffer *reply; struct evbuffer *out_body;
struct daap_session session; struct daap_session session;
int ret; int ret;
DPRINTF(E_DBG, L_DAAP, "Building reply for DAAP request: '%s'\n", uri); DPRINTF(E_DBG, L_DAAP, "Building reply for DAAP request: '%s'\n", uri);
reply = NULL; out_body = NULL;
httpd_request_set(&hreq, uri, user_agent); httpd_request_set(&hreq, uri, user_agent);
if (!(&hreq)->handler) if (!(&hreq)->handler)
@ -2336,21 +2329,20 @@ daap_reply_build(const char *uri, const char *user_agent, int is_remote)
hreq.extra_data = &session; hreq.extra_data = &session;
CHECK_NULL(L_DAAP, hreq.reply = evbuffer_new());
ret = hreq.handler(&hreq); ret = hreq.handler(&hreq);
if (ret < 0) if (ret < 0)
{ {
evbuffer_free(hreq.reply);
goto out; goto out;
} }
reply = hreq.reply; // Take ownership of the reply
out_body = hreq.out_body;
hreq.out_body = NULL;
out: out:
httpd_request_unset(&hreq); httpd_request_unset(&hreq);
return reply; return out_body;
} }
static int static int

View File

@ -1183,33 +1183,33 @@ static int
dacp_reply_ctrlint(struct httpd_request *hreq) dacp_reply_ctrlint(struct httpd_request *hreq)
{ {
/* /ctrl-int */ /* /ctrl-int */
CHECK_ERR(L_DACP, evbuffer_expand(hreq->reply, 256)); CHECK_ERR(L_DACP, evbuffer_expand(hreq->out_body, 256));
/* If tags are added or removed container sizes should be adjusted too */ /* If tags are added or removed container sizes should be adjusted too */
dmap_add_container(hreq->reply, "caci", 194); /* 8, unknown dacp container - size of content */ dmap_add_container(hreq->out_body, "caci", 194); /* 8, unknown dacp container - size of content */
dmap_add_int(hreq->reply, "mstt", 200); /* 12, dmap.status */ dmap_add_int(hreq->out_body, "mstt", 200); /* 12, dmap.status */
dmap_add_char(hreq->reply, "muty", 0); /* 9, dmap.updatetype */ dmap_add_char(hreq->out_body, "muty", 0); /* 9, dmap.updatetype */
dmap_add_int(hreq->reply, "mtco", 1); /* 12, dmap.specifiedtotalcount */ dmap_add_int(hreq->out_body, "mtco", 1); /* 12, dmap.specifiedtotalcount */
dmap_add_int(hreq->reply, "mrco", 1); /* 12, dmap.returnedcount */ dmap_add_int(hreq->out_body, "mrco", 1); /* 12, dmap.returnedcount */
dmap_add_container(hreq->reply, "mlcl", 141); /* 8, dmap.listing - size of content */ dmap_add_container(hreq->out_body, "mlcl", 141); /* 8, dmap.listing - size of content */
dmap_add_container(hreq->reply, "mlit", 133); /* 8, dmap.listingitem - size of content */ dmap_add_container(hreq->out_body, "mlit", 133); /* 8, dmap.listingitem - size of content */
dmap_add_int(hreq->reply, "miid", 1); /* 12, dmap.itemid - database ID */ dmap_add_int(hreq->out_body, "miid", 1); /* 12, dmap.itemid - database ID */
dmap_add_char(hreq->reply, "cmik", 1); /* 9, unknown */ dmap_add_char(hreq->out_body, "cmik", 1); /* 9, unknown */
dmap_add_int(hreq->reply, "cmpr", (2 << 16 | 2)); /* 12, dmcp.protocolversion */ dmap_add_int(hreq->out_body, "cmpr", (2 << 16 | 2)); /* 12, dmcp.protocolversion */
dmap_add_int(hreq->reply, "capr", (2 << 16 | 5)); /* 12, dacp.protocolversion */ dmap_add_int(hreq->out_body, "capr", (2 << 16 | 5)); /* 12, dacp.protocolversion */
dmap_add_char(hreq->reply, "cmsp", 1); /* 9, unknown */ dmap_add_char(hreq->out_body, "cmsp", 1); /* 9, unknown */
dmap_add_char(hreq->reply, "aeFR", 0x64); /* 9, unknown */ dmap_add_char(hreq->out_body, "aeFR", 0x64); /* 9, unknown */
dmap_add_char(hreq->reply, "cmsv", 1); /* 9, unknown */ dmap_add_char(hreq->out_body, "cmsv", 1); /* 9, unknown */
dmap_add_char(hreq->reply, "cass", 1); /* 9, unknown */ dmap_add_char(hreq->out_body, "cass", 1); /* 9, unknown */
dmap_add_char(hreq->reply, "caov", 1); /* 9, unknown */ dmap_add_char(hreq->out_body, "caov", 1); /* 9, unknown */
dmap_add_char(hreq->reply, "casu", 1); /* 9, unknown */ dmap_add_char(hreq->out_body, "casu", 1); /* 9, unknown */
dmap_add_char(hreq->reply, "ceSG", 1); /* 9, unknown */ dmap_add_char(hreq->out_body, "ceSG", 1); /* 9, unknown */
dmap_add_char(hreq->reply, "cmrl", 1); /* 9, unknown */ dmap_add_char(hreq->out_body, "cmrl", 1); /* 9, unknown */
dmap_add_long(hreq->reply, "ceSX", (1 << 1 | 1)); /* 16, unknown dacp - lowest bit announces support for playqueue-contents/-edit */ dmap_add_long(hreq->out_body, "ceSX", (1 << 1 | 1)); /* 16, unknown dacp - lowest bit announces support for playqueue-contents/-edit */
httpd_send_reply(hreq, HTTP_OK, "OK", hreq->reply, 0); httpd_send_reply(hreq, HTTP_OK, "OK", hreq->out_body, 0);
return 0; return 0;
} }
@ -1347,13 +1347,13 @@ dacp_reply_cue_play(struct httpd_request *hreq)
player_get_status(&status); player_get_status(&status);
CHECK_ERR(L_DACP, evbuffer_expand(hreq->reply, 64)); CHECK_ERR(L_DACP, evbuffer_expand(hreq->out_body, 64));
dmap_add_container(hreq->reply, "cacr", 24); /* 8 + len */ dmap_add_container(hreq->out_body, "cacr", 24); /* 8 + len */
dmap_add_int(hreq->reply, "mstt", 200); /* 12 */ dmap_add_int(hreq->out_body, "mstt", 200); /* 12 */
dmap_add_int(hreq->reply, "miid", status.id);/* 12 */ dmap_add_int(hreq->out_body, "miid", status.id);/* 12 */
httpd_send_reply(hreq, HTTP_OK, "OK", hreq->reply, 0); httpd_send_reply(hreq, HTTP_OK, "OK", hreq->out_body, 0);
return 0; return 0;
} }
@ -1367,13 +1367,13 @@ dacp_reply_cue_clear(struct httpd_request *hreq)
db_queue_clear(0); db_queue_clear(0);
CHECK_ERR(L_DACP, evbuffer_expand(hreq->reply, 64)); CHECK_ERR(L_DACP, evbuffer_expand(hreq->out_body, 64));
dmap_add_container(hreq->reply, "cacr", 24); /* 8 + len */ dmap_add_container(hreq->out_body, "cacr", 24); /* 8 + len */
dmap_add_int(hreq->reply, "mstt", 200); /* 12 */ dmap_add_int(hreq->out_body, "mstt", 200); /* 12 */
dmap_add_int(hreq->reply, "miid", 0); /* 12 */ dmap_add_int(hreq->out_body, "miid", 0); /* 12 */
httpd_send_reply(hreq, HTTP_OK, "OK", hreq->reply, 0); httpd_send_reply(hreq, HTTP_OK, "OK", hreq->out_body, 0);
return 0; return 0;
} }
@ -1427,7 +1427,7 @@ dacp_reply_play(struct httpd_request *hreq)
} }
/* 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->out_body, HTTPD_SEND_NO_GZIP);
return 0; return 0;
} }
@ -1559,7 +1559,7 @@ dacp_reply_playspec(struct httpd_request *hreq)
} }
/* 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->out_body, HTTPD_SEND_NO_GZIP);
return 0; return 0;
out_fail: out_fail:
@ -1580,7 +1580,7 @@ dacp_reply_stop(struct httpd_request *hreq)
player_playback_stop(); player_playback_stop();
/* 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->out_body, HTTPD_SEND_NO_GZIP);
return 0; return 0;
} }
@ -1597,7 +1597,7 @@ dacp_reply_pause(struct httpd_request *hreq)
player_playback_pause(); player_playback_pause();
/* 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->out_body, HTTPD_SEND_NO_GZIP);
return 0; return 0;
} }
@ -1630,7 +1630,7 @@ dacp_reply_playpause(struct httpd_request *hreq)
} }
/* 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->out_body, HTTPD_SEND_NO_GZIP);
return 0; return 0;
} }
@ -1663,7 +1663,7 @@ dacp_reply_nextitem(struct httpd_request *hreq)
} }
/* 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->out_body, HTTPD_SEND_NO_GZIP);
return 0; return 0;
} }
@ -1696,7 +1696,7 @@ dacp_reply_previtem(struct httpd_request *hreq)
} }
/* 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->out_body, HTTPD_SEND_NO_GZIP);
return 0; return 0;
} }
@ -1713,7 +1713,7 @@ dacp_reply_beginff(struct httpd_request *hreq)
/* TODO */ /* TODO */
/* 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->out_body, HTTPD_SEND_NO_GZIP);
return 0; return 0;
} }
@ -1730,7 +1730,7 @@ dacp_reply_beginrew(struct httpd_request *hreq)
/* TODO */ /* TODO */
/* 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->out_body, HTTPD_SEND_NO_GZIP);
return 0; return 0;
} }
@ -1747,7 +1747,7 @@ dacp_reply_playresume(struct httpd_request *hreq)
/* TODO */ /* TODO */
/* 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->out_body, HTTPD_SEND_NO_GZIP);
return 0; return 0;
} }
@ -1787,7 +1787,7 @@ dacp_reply_playqueuecontents(struct httpd_request *hreq)
} }
CHECK_NULL(L_DACP, songlist = evbuffer_new()); CHECK_NULL(L_DACP, songlist = evbuffer_new());
CHECK_ERR(L_DACP, evbuffer_expand(hreq->reply, 128)); CHECK_ERR(L_DACP, evbuffer_expand(hreq->out_body, 128));
player_get_status(&status); player_get_status(&status);
@ -1882,24 +1882,24 @@ dacp_reply_playqueuecontents(struct httpd_request *hreq)
/* Final construction of reply */ /* Final construction of reply */
playlist_length = evbuffer_get_length(playlists); playlist_length = evbuffer_get_length(playlists);
dmap_add_container(hreq->reply, "ceQR", 79 + playlist_length + songlist_length); /* size of entire container */ dmap_add_container(hreq->out_body, "ceQR", 79 + playlist_length + songlist_length); /* size of entire container */
dmap_add_int(hreq->reply, "mstt", 200); /* 12, dmap.status */ dmap_add_int(hreq->out_body, "mstt", 200); /* 12, dmap.status */
dmap_add_int(hreq->reply, "mtco", abs(span)); /* 12 */ dmap_add_int(hreq->out_body, "mtco", abs(span)); /* 12 */
dmap_add_int(hreq->reply, "mrco", count); /* 12 */ dmap_add_int(hreq->out_body, "mrco", count); /* 12 */
dmap_add_char(hreq->reply, "ceQu", 0); /* 9 */ dmap_add_char(hreq->out_body, "ceQu", 0); /* 9 */
dmap_add_container(hreq->reply, "mlcl", 8 + playlist_length + songlist_length); /* 8 */ dmap_add_container(hreq->out_body, "mlcl", 8 + playlist_length + songlist_length); /* 8 */
dmap_add_container(hreq->reply, "ceQS", playlist_length); /* 8 */ dmap_add_container(hreq->out_body, "ceQS", playlist_length); /* 8 */
CHECK_ERR(L_DACP, evbuffer_add_buffer(hreq->reply, playlists)); CHECK_ERR(L_DACP, evbuffer_add_buffer(hreq->out_body, playlists));
CHECK_ERR(L_DACP, evbuffer_add_buffer(hreq->reply, songlist)); CHECK_ERR(L_DACP, evbuffer_add_buffer(hreq->out_body, songlist));
evbuffer_free(playlists); evbuffer_free(playlists);
evbuffer_free(songlist); evbuffer_free(songlist);
dmap_add_char(hreq->reply, "apsm", status.shuffle); /* 9, daap.playlistshufflemode - not part of mlcl container */ dmap_add_char(hreq->out_body, "apsm", status.shuffle); /* 9, daap.playlistshufflemode - not part of mlcl container */
dmap_add_char(hreq->reply, "aprm", status.repeat); /* 9, daap.playlistrepeatmode - not part of mlcl container */ dmap_add_char(hreq->out_body, "aprm", status.repeat); /* 9, daap.playlistrepeatmode - not part of mlcl container */
httpd_send_reply(hreq, HTTP_OK, "OK", hreq->reply, 0); httpd_send_reply(hreq, HTTP_OK, "OK", hreq->out_body, 0);
return 0; return 0;
@ -1933,11 +1933,11 @@ dacp_reply_playqueueedit_clear(struct httpd_request *hreq)
db_queue_clear(status.item_id); db_queue_clear(status.item_id);
} }
dmap_add_container(hreq->reply, "cacr", 24); /* 8 + len */ dmap_add_container(hreq->out_body, "cacr", 24); /* 8 + len */
dmap_add_int(hreq->reply, "mstt", 200); /* 12 */ dmap_add_int(hreq->out_body, "mstt", 200); /* 12 */
dmap_add_int(hreq->reply, "miid", 0); /* 12 */ dmap_add_int(hreq->out_body, "miid", 0); /* 12 */
httpd_send_reply(hreq, HTTP_OK, "OK", hreq->reply, 0); httpd_send_reply(hreq, HTTP_OK, "OK", hreq->out_body, 0);
return 0; return 0;
} }
@ -2075,7 +2075,7 @@ dacp_reply_playqueueedit_add(struct httpd_request *hreq)
} }
/* 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->out_body, HTTPD_SEND_NO_GZIP);
return 0; return 0;
} }
@ -2123,7 +2123,7 @@ dacp_reply_playqueueedit_move(struct httpd_request *hreq)
} }
/* 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->out_body, HTTPD_SEND_NO_GZIP);
return 0; return 0;
} }
@ -2159,7 +2159,7 @@ dacp_reply_playqueueedit_remove(struct httpd_request *hreq)
} }
/* 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->out_body, HTTPD_SEND_NO_GZIP);
return 0; return 0;
} }
@ -2280,11 +2280,11 @@ dacp_reply_playstatusupdate(struct httpd_request *hreq)
// to use when he calls again. // to use when he calls again.
if (reqd_rev != current_rev) if (reqd_rev != current_rev)
{ {
ret = make_playstatusupdate(hreq->reply); ret = make_playstatusupdate(hreq->out_body);
if (ret < 0) if (ret < 0)
httpd_send_error(hreq, 500, "Internal Server Error"); httpd_send_error(hreq, 500, "Internal Server Error");
else else
httpd_send_reply(hreq, HTTP_OK, "OK", hreq->reply, 0); httpd_send_reply(hreq, HTTP_OK, "OK", hreq->out_body, 0);
return ret; return ret;
} }
@ -2360,8 +2360,8 @@ dacp_reply_nowplayingartwork(struct httpd_request *hreq)
if (ret < 0) if (ret < 0)
goto no_artwork; goto no_artwork;
ret = artwork_get_item(hreq->reply, id, max_w, max_h, 0); ret = artwork_get_item(hreq->out_body, id, max_w, max_h, 0);
len = evbuffer_get_length(hreq->reply); len = evbuffer_get_length(hreq->out_body);
switch (ret) switch (ret)
{ {
@ -2375,7 +2375,7 @@ dacp_reply_nowplayingartwork(struct httpd_request *hreq)
default: default:
if (len > 0) if (len > 0)
evbuffer_drain(hreq->reply, len); evbuffer_drain(hreq->out_body, len);
goto no_artwork; goto no_artwork;
} }
@ -2385,7 +2385,7 @@ dacp_reply_nowplayingartwork(struct httpd_request *hreq)
snprintf(clen, sizeof(clen), "%ld", (long)len); snprintf(clen, sizeof(clen), "%ld", (long)len);
httpd_header_add(hreq->out_headers, "Content-Length", clen); httpd_header_add(hreq->out_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->out_body, HTTPD_SEND_NO_GZIP);
return 0; return 0;
no_artwork: no_artwork:
@ -2479,14 +2479,14 @@ dacp_reply_getproperty(struct httpd_request *hreq)
free_queue_item(queue_item, 0); free_queue_item(queue_item, 0);
len = evbuffer_get_length(proplist); len = evbuffer_get_length(proplist);
dmap_add_container(hreq->reply, "cmgt", 12 + len); dmap_add_container(hreq->out_body, "cmgt", 12 + len);
dmap_add_int(hreq->reply, "mstt", 200); /* 12 */ dmap_add_int(hreq->out_body, "mstt", 200); /* 12 */
CHECK_ERR(L_DACP, evbuffer_add_buffer(hreq->reply, proplist)); CHECK_ERR(L_DACP, evbuffer_add_buffer(hreq->out_body, proplist));
evbuffer_free(proplist); evbuffer_free(proplist);
httpd_send_reply(hreq, HTTP_OK, "OK", hreq->reply, 0); httpd_send_reply(hreq, HTTP_OK, "OK", hreq->out_body, 0);
return 0; return 0;
@ -2541,7 +2541,7 @@ dacp_reply_setproperty(struct httpd_request *hreq)
httpd_query_iterate(hreq->query, setproperty_cb, hreq); httpd_query_iterate(hreq->query, setproperty_cb, hreq);
/* 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->out_body, HTTPD_SEND_NO_GZIP);
return 0; return 0;
} }
@ -2562,14 +2562,14 @@ dacp_reply_getspeakers(struct httpd_request *hreq)
player_speaker_enumerate(speaker_enum_cb, spklist); player_speaker_enumerate(speaker_enum_cb, spklist);
len = evbuffer_get_length(spklist); len = evbuffer_get_length(spklist);
dmap_add_container(hreq->reply, "casp", 12 + len); dmap_add_container(hreq->out_body, "casp", 12 + len);
dmap_add_int(hreq->reply, "mstt", 200); /* 12 */ dmap_add_int(hreq->out_body, "mstt", 200); /* 12 */
evbuffer_add_buffer(hreq->reply, spklist); evbuffer_add_buffer(hreq->out_body, spklist);
evbuffer_free(spklist); evbuffer_free(spklist);
httpd_send_reply(hreq, HTTP_OK, "OK", hreq->reply, 0); httpd_send_reply(hreq, HTTP_OK, "OK", hreq->out_body, 0);
return 0; return 0;
} }
@ -2659,7 +2659,7 @@ dacp_reply_setspeakers(struct httpd_request *hreq)
} }
/* 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->out_body, HTTPD_SEND_NO_GZIP);
return 0; return 0;
} }
@ -2685,7 +2685,7 @@ dacp_reply_volumeup(struct httpd_request *hreq)
} }
/* 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->out_body, HTTPD_SEND_NO_GZIP);
return 0; return 0;
} }
@ -2711,7 +2711,7 @@ dacp_reply_volumedown(struct httpd_request *hreq)
} }
/* 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->out_body, HTTPD_SEND_NO_GZIP);
return 0; return 0;
} }
@ -2738,7 +2738,7 @@ dacp_reply_mutetoggle(struct httpd_request *hreq)
} }
/* 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->out_body, HTTPD_SEND_NO_GZIP);
return 0; return 0;
} }
@ -2867,11 +2867,7 @@ dacp_request(struct httpd_request *hreq)
/* Content-Type for all DACP replies; can be overriden as needed */ /* Content-Type for all DACP replies; can be overriden as needed */
httpd_header_add(hreq->out_headers, "Content-Type", "application/x-dmap-tagged"); httpd_header_add(hreq->out_headers, "Content-Type", "application/x-dmap-tagged");
CHECK_NULL(L_DACP, hreq->reply = evbuffer_new());
hreq->handler(hreq); hreq->handler(hreq);
evbuffer_free(hreq->reply);
} }
// Forward // Forward

View File

@ -8,6 +8,8 @@
#include <event2/http.h> #include <event2/http.h>
#include <event2/keyvalq_struct.h> #include <event2/keyvalq_struct.h>
struct httpd_request;
typedef struct evhttp httpd_server; typedef struct evhttp httpd_server;
typedef struct evhttp_connection httpd_connection; typedef struct evhttp_connection httpd_connection;
typedef struct evhttp_request httpd_backend; typedef struct evhttp_request httpd_backend;
@ -32,6 +34,53 @@ enum httpd_send_flags
HTTPD_SEND_NO_GZIP = (1 << 0), HTTPD_SEND_NO_GZIP = (1 << 0),
}; };
/*---------------------------------- MODULES ---------------------------------*/
// Must be in sync with modules[] in httpd.c
enum httpd_modules
{
MODULE_DACP,
MODULE_DAAP,
MODULE_JSONAPI,
MODULE_ARTWORKAPI,
MODULE_STREAMING,
MODULE_OAUTH,
MODULE_RSP,
};
struct httpd_module
{
const char *name;
enum httpd_modules type;
char initialized;
// Null-terminated list of URL subpath that the module accepts e.g., /subpath/morepath/file.mp3
const char *subpaths[16];
// Null-terminated list of URL fullparhs that the module accepts e.g., /fullpath
const char *fullpaths[16];
// Pointer to the module's handler definitions
struct httpd_uri_map *handlers;
int (*init)(void);
void (*deinit)(void);
void (*request)(struct httpd_request *hreq);
};
/*
* Maps a regex of the request path to a handler of the request
*/
struct httpd_uri_map
{
enum httpd_methods method;
char *regexp;
int (*handler)(struct httpd_request *hreq);
void *preg;
};
/*------------------------------- HTTPD STRUCTS ------------------------------*/
/* /*
* Contains a parsed version of the URI httpd got. The URI may have been * Contains a parsed version of the URI httpd got. The URI may have been
* complete: * complete:
@ -85,69 +134,17 @@ struct httpd_request {
struct evbuffer *in_body; struct evbuffer *in_body;
// Response headers // Response headers
httpd_headers *out_headers; httpd_headers *out_headers;
// Reply evbuffer // Response body
struct evbuffer *reply; struct evbuffer *out_body;
// The module that will process this request
struct httpd_module *module;
// A pointer to the handler that will process the request // A pointer to the handler that will process the request
int (*handler)(struct httpd_request *hreq); int (*handler)(struct httpd_request *hreq);
}; };
/*---------------------------------- MODULES ---------------------------------*/ /*------------------------------ HTTPD FUNCTIONS -----------------------------*/
// Must be in sync with modules[] in httpd.c
enum httpd_modules
{
MODULE_DACP,
MODULE_DAAP,
MODULE_JSONAPI,
MODULE_ARTWORKAPI,
MODULE_STREAMING,
MODULE_OAUTH,
MODULE_RSP,
};
struct httpd_module
{
const char *name;
enum httpd_modules type;
char initialized;
// Null-terminated list of URL subpath that the module accepts e.g., /subpath/morepath/file.mp3
const char *subpaths[16];
// Null-terminated list of URL fullparhs that the module accepts e.g., /fullpath
const char *fullpaths[16];
// Pointer to the module's handler definitions
struct httpd_uri_map *handlers;
int (*init)(void);
void (*deinit)(void);
void (*request)(struct httpd_request *hreq);
};
/*
* Maps a regex of the request path to a handler of the request
*/
struct httpd_uri_map
{
enum httpd_methods method;
char *regexp;
int (*handler)(struct httpd_request *hreq);
void *preg;
};
/*
* Helper to free the parsed uri struct
*/
void
httpd_uri_free(struct httpd_uri_parsed *parsed);
/*
* Parse an URI into the struct
*/
struct httpd_uri_parsed *
httpd_uri_parse(const char *uri);
void void
httpd_stream_file(struct httpd_request *hreq, int id); httpd_stream_file(struct httpd_request *hreq, int id);

View File

@ -867,7 +867,7 @@ jsonapi_reply_config(struct httpd_request *hreq)
json_object_object_add(jreply, "allow_modifying_stored_playlists", json_object_new_boolean(allow_modifying_stored_playlists)); json_object_object_add(jreply, "allow_modifying_stored_playlists", json_object_new_boolean(allow_modifying_stored_playlists));
safe_json_add_string(jreply, "default_playlist_directory", default_playlist_directory); safe_json_add_string(jreply, "default_playlist_directory", default_playlist_directory);
CHECK_ERRNO(L_WEB, evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(jreply))); CHECK_ERRNO(L_WEB, evbuffer_add_printf(hreq->out_body, "%s", json_object_to_json_string(jreply)));
jparse_free(jreply); jparse_free(jreply);
@ -974,7 +974,7 @@ jsonapi_reply_settings_get(struct httpd_request *hreq)
json_object_object_add(jreply, "categories", json_categories); json_object_object_add(jreply, "categories", json_categories);
CHECK_ERRNO(L_WEB, evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(jreply))); CHECK_ERRNO(L_WEB, evbuffer_add_printf(hreq->out_body, "%s", json_object_to_json_string(jreply)));
jparse_free(jreply); jparse_free(jreply);
@ -1006,7 +1006,7 @@ jsonapi_reply_settings_category_get(struct httpd_request *hreq)
return HTTP_INTERNAL; return HTTP_INTERNAL;
} }
CHECK_ERRNO(L_WEB, evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(jreply))); CHECK_ERRNO(L_WEB, evbuffer_add_printf(hreq->out_body, "%s", json_object_to_json_string(jreply)));
jparse_free(jreply); jparse_free(jreply);
@ -1048,7 +1048,7 @@ jsonapi_reply_settings_option_get(struct httpd_request *hreq)
return HTTP_INTERNAL; return HTTP_INTERNAL;
} }
CHECK_ERRNO(L_WEB, evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(jreply))); CHECK_ERRNO(L_WEB, evbuffer_add_printf(hreq->out_body, "%s", json_object_to_json_string(jreply)));
jparse_free(jreply); jparse_free(jreply);
@ -1236,7 +1236,7 @@ jsonapi_reply_library(struct httpd_request *hreq)
} }
} }
CHECK_ERRNO(L_WEB, evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(jreply))); CHECK_ERRNO(L_WEB, evbuffer_add_printf(hreq->out_body, "%s", json_object_to_json_string(jreply)));
jparse_free(jreply); jparse_free(jreply);
return HTTP_OK; return HTTP_OK;
@ -1329,7 +1329,7 @@ jsonapi_reply_spotify(struct httpd_request *hreq)
json_object_object_add(jreply, "enabled", json_object_new_boolean(false)); json_object_object_add(jreply, "enabled", json_object_new_boolean(false));
#endif #endif
CHECK_ERRNO(L_WEB, evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(jreply))); CHECK_ERRNO(L_WEB, evbuffer_add_printf(hreq->out_body, "%s", json_object_to_json_string(jreply)));
jparse_free(jreply); jparse_free(jreply);
@ -1363,7 +1363,7 @@ jsonapi_reply_lastfm(struct httpd_request *hreq)
json_object_object_add(jreply, "enabled", json_object_new_boolean(enabled)); json_object_object_add(jreply, "enabled", json_object_new_boolean(enabled));
json_object_object_add(jreply, "scrobbling_enabled", json_object_new_boolean(scrobbling_enabled)); json_object_object_add(jreply, "scrobbling_enabled", json_object_new_boolean(scrobbling_enabled));
CHECK_ERRNO(L_WEB, evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(jreply))); CHECK_ERRNO(L_WEB, evbuffer_add_printf(hreq->out_body, "%s", json_object_to_json_string(jreply)));
jparse_free(jreply); jparse_free(jreply);
@ -1430,7 +1430,7 @@ jsonapi_reply_lastfm_login(struct httpd_request *hreq)
json_object_object_add(jreply, "errors", errors); json_object_object_add(jreply, "errors", errors);
} }
CHECK_ERRNO(L_WEB, evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(jreply))); CHECK_ERRNO(L_WEB, evbuffer_add_printf(hreq->out_body, "%s", json_object_to_json_string(jreply)));
jparse_free(jreply); jparse_free(jreply);
@ -1526,7 +1526,7 @@ jsonapi_reply_pairing_get(struct httpd_request *hreq)
json_object_object_add(jreply, "active", json_object_new_boolean(false)); json_object_object_add(jreply, "active", json_object_new_boolean(false));
} }
CHECK_ERRNO(L_WEB, evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(jreply))); CHECK_ERRNO(L_WEB, evbuffer_add_printf(hreq->out_body, "%s", json_object_to_json_string(jreply)));
jparse_free(jreply); jparse_free(jreply);
free(remote_name); free(remote_name);
@ -1603,7 +1603,7 @@ jsonapi_reply_outputs_get_byid(struct httpd_request *hreq)
} }
jreply = speaker_to_json(&speaker_info); jreply = speaker_to_json(&speaker_info);
CHECK_ERRNO(L_WEB, evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(jreply))); CHECK_ERRNO(L_WEB, evbuffer_add_printf(hreq->out_body, "%s", json_object_to_json_string(jreply)));
jparse_free(jreply); jparse_free(jreply);
@ -1723,7 +1723,7 @@ jsonapi_reply_outputs(struct httpd_request *hreq)
jreply = json_object_new_object(); jreply = json_object_new_object();
json_object_object_add(jreply, "outputs", outputs); json_object_object_add(jreply, "outputs", outputs);
CHECK_ERRNO(L_WEB, evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(jreply))); CHECK_ERRNO(L_WEB, evbuffer_add_printf(hreq->out_body, "%s", json_object_to_json_string(jreply)));
jparse_free(jreply); jparse_free(jreply);
@ -2132,7 +2132,7 @@ jsonapi_reply_player(struct httpd_request *hreq)
} }
} }
CHECK_ERRNO(L_WEB, evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(reply))); CHECK_ERRNO(L_WEB, evbuffer_add_printf(hreq->out_body, "%s", json_object_to_json_string(reply)));
jparse_free(reply); jparse_free(reply);
@ -2520,7 +2520,7 @@ jsonapi_reply_queue_tracks_add(struct httpd_request *hreq)
reply = json_object_new_object(); reply = json_object_new_object();
json_object_object_add(reply, "count", json_object_new_int(total_count)); json_object_object_add(reply, "count", json_object_new_int(total_count));
ret = evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(reply)); ret = evbuffer_add_printf(hreq->out_body, "%s", json_object_to_json_string(reply));
jparse_free(reply); jparse_free(reply);
} }
@ -2741,7 +2741,7 @@ jsonapi_reply_queue(struct httpd_request *hreq)
json_object_array_add(items, item); json_object_array_add(items, item);
} }
ret = evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(reply)); ret = evbuffer_add_printf(hreq->out_body, "%s", json_object_to_json_string(reply));
if (ret < 0) if (ret < 0)
DPRINTF(E_LOG, L_WEB, "outputs: Couldn't add outputs to response buffer.\n"); DPRINTF(E_LOG, L_WEB, "outputs: Couldn't add outputs to response buffer.\n");
@ -2980,7 +2980,7 @@ jsonapi_reply_library_artists(struct httpd_request *hreq)
json_object_object_add(reply, "offset", json_object_new_int(query_params.offset)); json_object_object_add(reply, "offset", json_object_new_int(query_params.offset));
json_object_object_add(reply, "limit", json_object_new_int(query_params.limit)); json_object_object_add(reply, "limit", json_object_new_int(query_params.limit));
ret = evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(reply)); ret = evbuffer_add_printf(hreq->out_body, "%s", json_object_to_json_string(reply));
if (ret < 0) if (ret < 0)
DPRINTF(E_LOG, L_WEB, "browse: Couldn't add artists to response buffer.\n"); DPRINTF(E_LOG, L_WEB, "browse: Couldn't add artists to response buffer.\n");
@ -3014,7 +3014,7 @@ jsonapi_reply_library_artist(struct httpd_request *hreq)
goto error; goto error;
} }
ret = evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(reply)); ret = evbuffer_add_printf(hreq->out_body, "%s", json_object_to_json_string(reply));
if (ret < 0) if (ret < 0)
DPRINTF(E_LOG, L_WEB, "browse: Couldn't add artists to response buffer.\n"); DPRINTF(E_LOG, L_WEB, "browse: Couldn't add artists to response buffer.\n");
@ -3066,7 +3066,7 @@ jsonapi_reply_library_artist_albums(struct httpd_request *hreq)
json_object_object_add(reply, "offset", json_object_new_int(query_params.offset)); json_object_object_add(reply, "offset", json_object_new_int(query_params.offset));
json_object_object_add(reply, "limit", json_object_new_int(query_params.limit)); json_object_object_add(reply, "limit", json_object_new_int(query_params.limit));
ret = evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(reply)); ret = evbuffer_add_printf(hreq->out_body, "%s", json_object_to_json_string(reply));
if (ret < 0) if (ret < 0)
DPRINTF(E_LOG, L_WEB, "browse: Couldn't add albums to response buffer.\n"); DPRINTF(E_LOG, L_WEB, "browse: Couldn't add albums to response buffer.\n");
@ -3129,7 +3129,7 @@ jsonapi_reply_library_albums(struct httpd_request *hreq)
json_object_object_add(reply, "offset", json_object_new_int(query_params.offset)); json_object_object_add(reply, "offset", json_object_new_int(query_params.offset));
json_object_object_add(reply, "limit", json_object_new_int(query_params.limit)); json_object_object_add(reply, "limit", json_object_new_int(query_params.limit));
ret = evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(reply)); ret = evbuffer_add_printf(hreq->out_body, "%s", json_object_to_json_string(reply));
if (ret < 0) if (ret < 0)
DPRINTF(E_LOG, L_WEB, "browse: Couldn't add albums to response buffer.\n"); DPRINTF(E_LOG, L_WEB, "browse: Couldn't add albums to response buffer.\n");
@ -3163,7 +3163,7 @@ jsonapi_reply_library_album(struct httpd_request *hreq)
goto error; goto error;
} }
ret = evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(reply)); ret = evbuffer_add_printf(hreq->out_body, "%s", json_object_to_json_string(reply));
if (ret < 0) if (ret < 0)
DPRINTF(E_LOG, L_WEB, "browse: Couldn't add artists to response buffer.\n"); DPRINTF(E_LOG, L_WEB, "browse: Couldn't add artists to response buffer.\n");
@ -3215,7 +3215,7 @@ jsonapi_reply_library_album_tracks(struct httpd_request *hreq)
json_object_object_add(reply, "offset", json_object_new_int(query_params.offset)); json_object_object_add(reply, "offset", json_object_new_int(query_params.offset));
json_object_object_add(reply, "limit", json_object_new_int(query_params.limit)); json_object_object_add(reply, "limit", json_object_new_int(query_params.limit));
ret = evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(reply)); ret = evbuffer_add_printf(hreq->out_body, "%s", json_object_to_json_string(reply));
if (ret < 0) if (ret < 0)
DPRINTF(E_LOG, L_WEB, "browse: Couldn't add tracks to response buffer.\n"); DPRINTF(E_LOG, L_WEB, "browse: Couldn't add tracks to response buffer.\n");
@ -3297,7 +3297,7 @@ jsonapi_reply_library_tracks_get_byid(struct httpd_request *hreq)
reply = track_to_json(&dbmfi); reply = track_to_json(&dbmfi);
ret = evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(reply)); ret = evbuffer_add_printf(hreq->out_body, "%s", json_object_to_json_string(reply));
if (ret < 0) if (ret < 0)
DPRINTF(E_LOG, L_WEB, "browse: Couldn't add track to response buffer.\n"); DPRINTF(E_LOG, L_WEB, "browse: Couldn't add track to response buffer.\n");
@ -3505,7 +3505,7 @@ jsonapi_reply_library_track_playlists(struct httpd_request *hreq)
json_object_object_add(reply, "offset", json_object_new_int(query_params.offset)); json_object_object_add(reply, "offset", json_object_new_int(query_params.offset));
json_object_object_add(reply, "limit", json_object_new_int(query_params.limit)); json_object_object_add(reply, "limit", json_object_new_int(query_params.limit));
ret = evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(reply)); ret = evbuffer_add_printf(hreq->out_body, "%s", json_object_to_json_string(reply));
if (ret < 0) if (ret < 0)
DPRINTF(E_LOG, L_WEB, "track playlists: Couldn't add playlists to response buffer.\n"); DPRINTF(E_LOG, L_WEB, "track playlists: Couldn't add playlists to response buffer.\n");
@ -3556,7 +3556,7 @@ jsonapi_reply_library_playlists(struct httpd_request *hreq)
json_object_object_add(reply, "offset", json_object_new_int(query_params.offset)); json_object_object_add(reply, "offset", json_object_new_int(query_params.offset));
json_object_object_add(reply, "limit", json_object_new_int(query_params.limit)); json_object_object_add(reply, "limit", json_object_new_int(query_params.limit));
ret = evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(reply)); ret = evbuffer_add_printf(hreq->out_body, "%s", json_object_to_json_string(reply));
if (ret < 0) if (ret < 0)
DPRINTF(E_LOG, L_WEB, "browse: Couldn't add playlists to response buffer.\n"); DPRINTF(E_LOG, L_WEB, "browse: Couldn't add playlists to response buffer.\n");
@ -3607,7 +3607,7 @@ jsonapi_reply_library_playlist_get(struct httpd_request *hreq)
goto error; goto error;
} }
ret = evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(reply)); ret = evbuffer_add_printf(hreq->out_body, "%s", json_object_to_json_string(reply));
if (ret < 0) if (ret < 0)
DPRINTF(E_LOG, L_WEB, "browse: Couldn't add playlist to response buffer.\n"); DPRINTF(E_LOG, L_WEB, "browse: Couldn't add playlist to response buffer.\n");
@ -3711,7 +3711,7 @@ jsonapi_reply_library_playlist_tracks(struct httpd_request *hreq)
json_object_object_add(reply, "offset", json_object_new_int(query_params.offset)); json_object_object_add(reply, "offset", json_object_new_int(query_params.offset));
json_object_object_add(reply, "limit", json_object_new_int(query_params.limit)); json_object_object_add(reply, "limit", json_object_new_int(query_params.limit));
ret = evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(reply)); ret = evbuffer_add_printf(hreq->out_body, "%s", json_object_to_json_string(reply));
if (ret < 0) if (ret < 0)
DPRINTF(E_LOG, L_WEB, "playlist tracks: Couldn't add tracks to response buffer.\n"); DPRINTF(E_LOG, L_WEB, "playlist tracks: Couldn't add tracks to response buffer.\n");
@ -3789,7 +3789,7 @@ jsonapi_reply_library_playlist_playlists(struct httpd_request *hreq)
json_object_object_add(reply, "offset", json_object_new_int(query_params.offset)); json_object_object_add(reply, "offset", json_object_new_int(query_params.offset));
json_object_object_add(reply, "limit", json_object_new_int(query_params.limit)); json_object_object_add(reply, "limit", json_object_new_int(query_params.limit));
ret = evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(reply)); ret = evbuffer_add_printf(hreq->out_body, "%s", json_object_to_json_string(reply));
if (ret < 0) if (ret < 0)
DPRINTF(E_LOG, L_WEB, "playlist tracks: Couldn't add tracks to response buffer.\n"); DPRINTF(E_LOG, L_WEB, "playlist tracks: Couldn't add tracks to response buffer.\n");
@ -3950,7 +3950,7 @@ jsonapi_reply_library_browse(struct httpd_request *hreq)
json_object_object_add(reply, "offset", json_object_new_int(query_params.offset)); json_object_object_add(reply, "offset", json_object_new_int(query_params.offset));
json_object_object_add(reply, "limit", json_object_new_int(query_params.limit)); json_object_object_add(reply, "limit", json_object_new_int(query_params.limit));
ret = evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(reply)); ret = evbuffer_add_printf(hreq->out_body, "%s", json_object_to_json_string(reply));
if (ret < 0) if (ret < 0)
DPRINTF(E_LOG, L_WEB, "browse: Couldn't add browse items to response buffer.\n"); DPRINTF(E_LOG, L_WEB, "browse: Couldn't add browse items to response buffer.\n");
@ -4023,7 +4023,7 @@ jsonapi_reply_library_browseitem(struct httpd_request *hreq)
goto error; goto error;
} }
ret = evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(reply)); ret = evbuffer_add_printf(hreq->out_body, "%s", json_object_to_json_string(reply));
if (ret < 0) if (ret < 0)
DPRINTF(E_LOG, L_WEB, "browse: Couldn't add browse item to response buffer.\n"); DPRINTF(E_LOG, L_WEB, "browse: Couldn't add browse item to response buffer.\n");
@ -4088,7 +4088,7 @@ jsonapi_reply_library_count(struct httpd_request *hreq)
free(qp.filter); free(qp.filter);
CHECK_ERRNO(L_WEB, evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(jreply))); CHECK_ERRNO(L_WEB, evbuffer_add_printf(hreq->out_body, "%s", json_object_to_json_string(jreply)));
jparse_free(jreply); jparse_free(jreply);
return HTTP_OK; return HTTP_OK;
@ -4182,7 +4182,7 @@ jsonapi_reply_library_files(struct httpd_request *hreq)
json_object_object_add(playlists, "limit", json_object_new_int(query_params.limit)); json_object_object_add(playlists, "limit", json_object_new_int(query_params.limit));
// Build JSON response // Build JSON response
ret = evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(reply)); ret = evbuffer_add_printf(hreq->out_body, "%s", json_object_to_json_string(reply));
if (ret < 0) if (ret < 0)
DPRINTF(E_LOG, L_WEB, "browse: Couldn't add directories to response buffer.\n"); DPRINTF(E_LOG, L_WEB, "browse: Couldn't add directories to response buffer.\n");
@ -4587,7 +4587,7 @@ jsonapi_reply_search(struct httpd_request *hreq)
goto error; goto error;
} }
ret = evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(reply)); ret = evbuffer_add_printf(hreq->out_body, "%s", json_object_to_json_string(reply));
if (ret < 0) if (ret < 0)
DPRINTF(E_LOG, L_WEB, "playlist tracks: Couldn't add tracks to response buffer.\n"); DPRINTF(E_LOG, L_WEB, "playlist tracks: Couldn't add tracks to response buffer.\n");
@ -4721,8 +4721,6 @@ jsonapi_request(struct httpd_request *hreq)
return; return;
} }
CHECK_NULL(L_WEB, hreq->reply = evbuffer_new());
status_code = hreq->handler(hreq); status_code = hreq->handler(hreq);
if (status_code >= 400) if (status_code >= 400)
@ -4732,10 +4730,10 @@ jsonapi_request(struct httpd_request *hreq)
{ {
case HTTP_OK: /* 200 OK */ case HTTP_OK: /* 200 OK */
httpd_header_add(hreq->out_headers, "Content-Type", "application/json"); httpd_header_add(hreq->out_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->out_body, HTTPD_SEND_NO_GZIP);
break; break;
case HTTP_NOCONTENT: /* 204 No Content */ case HTTP_NOCONTENT: /* 204 No Content */
httpd_send_reply(hreq, status_code, "No Content", hreq->reply, HTTPD_SEND_NO_GZIP); httpd_send_reply(hreq, status_code, "No Content", hreq->out_body, HTTPD_SEND_NO_GZIP);
break; break;
case HTTP_NOTMODIFIED: /* 304 Not Modified */ case HTTP_NOTMODIFIED: /* 304 Not Modified */
httpd_send_reply(hreq, HTTP_NOTMODIFIED, NULL, NULL, HTTPD_SEND_NO_GZIP); httpd_send_reply(hreq, HTTP_NOTMODIFIED, NULL, NULL, HTTPD_SEND_NO_GZIP);
@ -4757,8 +4755,6 @@ jsonapi_request(struct httpd_request *hreq)
httpd_send_error(hreq, HTTP_INTERNAL, "Internal Server Error"); httpd_send_error(hreq, HTTP_INTERNAL, "Internal Server Error");
break; break;
} }
evbuffer_free(hreq->reply);
} }
static int static int

View File

@ -193,6 +193,12 @@ httpd_backend_input_buffer_get(httpd_backend *backend)
return evhttp_request_get_input_buffer(backend); return evhttp_request_get_input_buffer(backend);
} }
struct evbuffer *
httpd_backend_output_buffer_get(httpd_backend *backend)
{
return evhttp_request_get_output_buffer(backend);
}
int int
httpd_backend_peer_get(char **addr, uint16_t *port, httpd_backend *backend) httpd_backend_peer_get(char **addr, uint16_t *port, httpd_backend *backend)
{ {

View File

@ -54,12 +54,12 @@ oauth_reply_spotify(struct httpd_request *hreq)
ret = spotifywebapi_oauth_callback(hreq->query, redirect_uri, &errmsg); ret = spotifywebapi_oauth_callback(hreq->query, redirect_uri, &errmsg);
if (ret < 0) if (ret < 0)
{ {
DPRINTF(E_LOG, L_WEB, "Could not parse Spotify OAuth callback '%s': %s\n", hreq->uri_parsed->uri, errmsg); DPRINTF(E_LOG, L_WEB, "Could not parse Spotify OAuth callback '%s': %s\n", hreq->uri, errmsg);
httpd_send_error(hreq, HTTP_INTERNAL, errmsg); httpd_send_error(hreq, HTTP_INTERNAL, errmsg);
return -1; return -1;
} }
httpd_redirect_to(hreq->req, "/#/settings/online-services"); httpd_redirect_to(hreq, "/#/settings/online-services");
return 0; return 0;
} }