mirror of
https://github.com/owntone/owntone-server.git
synced 2025-02-26 04:49:18 -05:00
[httpd] Refactor to use modules for daap, dacp, json api etc.
Removes a lot of code duplication which hopefully will make it easier to add support for evhtp as http engine.
This commit is contained in:
parent
83b8a4eb3f
commit
2778088c52
@ -92,14 +92,14 @@ owntone_SOURCES = main.c \
|
|||||||
library.c library.h \
|
library.c library.h \
|
||||||
$(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.c httpd.h httpd_internal.h \
|
||||||
httpd_rsp.c httpd_rsp.h \
|
httpd_rsp.c \
|
||||||
httpd_daap.c httpd_daap.h \
|
httpd_daap.c httpd_daap.h \
|
||||||
httpd_dacp.c httpd_dacp.h \
|
httpd_dacp.c \
|
||||||
httpd_jsonapi.c httpd_jsonapi.h \
|
httpd_jsonapi.c \
|
||||||
httpd_streaming.c httpd_streaming.h \
|
httpd_streaming.c httpd_streaming.h \
|
||||||
httpd_oauth.c httpd_oauth.h \
|
httpd_oauth.c \
|
||||||
httpd_artworkapi.c httpd_artworkapi.h \
|
httpd_artworkapi.c \
|
||||||
http.c http.h \
|
http.c http.h \
|
||||||
dmap_common.c dmap_common.h \
|
dmap_common.c dmap_common.h \
|
||||||
transcode.c transcode.h \
|
transcode.c transcode.h \
|
||||||
|
382
src/httpd.c
382
src/httpd.c
@ -52,13 +52,7 @@
|
|||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
#include "worker.h"
|
#include "worker.h"
|
||||||
#include "httpd.h"
|
#include "httpd.h"
|
||||||
#include "httpd_rsp.h"
|
#include "httpd_internal.h"
|
||||||
#include "httpd_daap.h"
|
|
||||||
#include "httpd_dacp.h"
|
|
||||||
#include "httpd_jsonapi.h"
|
|
||||||
#include "httpd_streaming.h"
|
|
||||||
#include "httpd_oauth.h"
|
|
||||||
#include "httpd_artworkapi.h"
|
|
||||||
#include "transcode.h"
|
#include "transcode.h"
|
||||||
#ifdef LASTFM
|
#ifdef LASTFM
|
||||||
# include "lastfm.h"
|
# include "lastfm.h"
|
||||||
@ -67,7 +61,6 @@
|
|||||||
# include "websocket.h"
|
# include "websocket.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#define STREAM_CHUNK_SIZE (64 * 1024)
|
#define STREAM_CHUNK_SIZE (64 * 1024)
|
||||||
#define ERR_PAGE "<html>\n<head>\n" \
|
#define ERR_PAGE "<html>\n<head>\n" \
|
||||||
"<title>%d %s</title>\n" \
|
"<title>%d %s</title>\n" \
|
||||||
@ -79,6 +72,26 @@
|
|||||||
#define HTTPD_STREAM_BPS 16
|
#define HTTPD_STREAM_BPS 16
|
||||||
#define HTTPD_STREAM_CHANNELS 2
|
#define HTTPD_STREAM_CHANNELS 2
|
||||||
|
|
||||||
|
extern struct httpd_module httpd_dacp;
|
||||||
|
extern struct httpd_module httpd_daap;
|
||||||
|
extern struct httpd_module httpd_jsonapi;
|
||||||
|
extern struct httpd_module httpd_artworkapi;
|
||||||
|
extern struct httpd_module httpd_streaming;
|
||||||
|
extern struct httpd_module httpd_oauth;
|
||||||
|
extern struct httpd_module httpd_rsp;
|
||||||
|
|
||||||
|
// Must be in sync with enum httpd_modules
|
||||||
|
static struct httpd_module *httpd_modules[] = {
|
||||||
|
&httpd_dacp,
|
||||||
|
&httpd_daap,
|
||||||
|
&httpd_jsonapi,
|
||||||
|
&httpd_artworkapi,
|
||||||
|
&httpd_streaming,
|
||||||
|
&httpd_oauth,
|
||||||
|
&httpd_rsp,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
struct content_type_map {
|
struct content_type_map {
|
||||||
char *ext;
|
char *ext;
|
||||||
@ -114,8 +127,6 @@ static const struct content_type_map ext2ctype[] =
|
|||||||
{ NULL, NULL }
|
{ NULL, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *http_reply_401 = "<html><head><title>401 Unauthorized</title></head><body>Authorization required</body></html>";
|
|
||||||
|
|
||||||
static char webroot_directory[PATH_MAX];
|
static char webroot_directory[PATH_MAX];
|
||||||
struct event_base *evbase_httpd;
|
struct event_base *evbase_httpd;
|
||||||
|
|
||||||
@ -161,98 +172,125 @@ scrobble_cb(void *arg)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
|
||||||
* This disabled in the commit after d8cdc89 because my tests work fine without
|
/* --------------------------- MODULES INTERFACE ---------------------------- */
|
||||||
* it, and it seems that nowadays iTunes and Remote encodes the query just fine.
|
|
||||||
* However, I'm keeping it around for a while in case problems show up. If you
|
static void
|
||||||
* are from the future, you can probably safely remove it for good.
|
modules_handlers_unset(struct httpd_uri_map *uri_map)
|
||||||
*
|
|
||||||
static char *
|
|
||||||
httpd_fixup_uri(struct evhttp_request *req)
|
|
||||||
{
|
{
|
||||||
struct evkeyvalq *headers;
|
struct httpd_uri_map *uri;
|
||||||
const char *ua;
|
|
||||||
const char *uri;
|
|
||||||
const char *u;
|
|
||||||
const char *q;
|
|
||||||
char *fixed;
|
|
||||||
char *f;
|
|
||||||
int len;
|
|
||||||
|
|
||||||
uri = evhttp_request_get_uri(req);
|
for (uri = uri_map; uri->preg; uri++)
|
||||||
if (!uri)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
// No query string, nothing to do
|
|
||||||
q = strchr(uri, '?');
|
|
||||||
if (!q)
|
|
||||||
return strdup(uri);
|
|
||||||
|
|
||||||
headers = evhttp_request_get_input_headers(req);
|
|
||||||
ua = evhttp_find_header(headers, "User-Agent");
|
|
||||||
if (!ua)
|
|
||||||
return strdup(uri);
|
|
||||||
|
|
||||||
if ((strncmp(ua, "iTunes", strlen("iTunes")) != 0)
|
|
||||||
&& (strncmp(ua, "Remote", strlen("Remote")) != 0)
|
|
||||||
&& (strncmp(ua, "Roku", strlen("Roku")) != 0))
|
|
||||||
return strdup(uri);
|
|
||||||
|
|
||||||
// Reencode + as %2B and space as + in the query,
|
|
||||||
// which iTunes and Roku devices don't do
|
|
||||||
len = strlen(uri);
|
|
||||||
|
|
||||||
u = q;
|
|
||||||
while (*u)
|
|
||||||
{
|
{
|
||||||
if (*u == '+')
|
regfree(uri->preg); // Frees allocation by regcomp
|
||||||
len += 2;
|
free(uri->preg); // Frees our own calloc
|
||||||
|
|
||||||
u++;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fixed = (char *)malloc(len + 1);
|
static int
|
||||||
if (!fixed)
|
modules_handlers_set(struct httpd_uri_map *uri_map)
|
||||||
return NULL;
|
{
|
||||||
|
struct httpd_uri_map *uri;
|
||||||
|
char buf[64];
|
||||||
|
int ret;
|
||||||
|
|
||||||
strncpy(fixed, uri, q - uri);
|
for (uri = uri_map; uri->handler; uri++)
|
||||||
|
|
||||||
f = fixed + (q - uri);
|
|
||||||
while (*q)
|
|
||||||
{
|
{
|
||||||
switch (*q)
|
uri->preg = calloc(1, sizeof(regex_t));
|
||||||
|
if (!uri->preg)
|
||||||
{
|
{
|
||||||
case '+':
|
DPRINTF(E_LOG, L_HTTPD, "Error setting URI handler, out of memory");
|
||||||
*f = '%';
|
goto error;
|
||||||
f++;
|
|
||||||
*f = '2';
|
|
||||||
f++;
|
|
||||||
*f = 'B';
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ' ':
|
|
||||||
*f = '+';
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
*f = *q;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
q++;
|
ret = regcomp(uri->preg, uri->regexp, REG_EXTENDED | REG_NOSUB);
|
||||||
f++;
|
if (ret != 0)
|
||||||
|
{
|
||||||
|
regerror(ret, uri->preg, buf, sizeof(buf));
|
||||||
|
DPRINTF(E_LOG, L_HTTPD, "Error setting URI handler, regexp error: %s\n", buf);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*f = '\0';
|
return 0;
|
||||||
|
|
||||||
return fixed;
|
error:
|
||||||
|
modules_handlers_unset(uri_map);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
modules_init(void)
|
||||||
|
{
|
||||||
|
struct httpd_module **ptr;
|
||||||
|
struct httpd_module *m;
|
||||||
|
|
||||||
|
for (ptr = httpd_modules; *ptr; ptr++)
|
||||||
|
{
|
||||||
|
m = *ptr;
|
||||||
|
m->initialized = (!m->init || m->init() == 0);
|
||||||
|
if (!m->initialized)
|
||||||
|
{
|
||||||
|
DPRINTF(E_FATAL, L_HTTPD, "%s init failed\n", m->name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modules_handlers_set(m->handlers) != 0)
|
||||||
|
{
|
||||||
|
DPRINTF(E_FATAL, L_HTTPD, "%s handler configuration failed\n", m->name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
modules_deinit(void)
|
||||||
|
{
|
||||||
|
struct httpd_module **ptr;
|
||||||
|
struct httpd_module *m;
|
||||||
|
|
||||||
|
for (ptr = httpd_modules; *ptr; ptr++)
|
||||||
|
{
|
||||||
|
m = *ptr;
|
||||||
|
if (m->initialized && m->deinit)
|
||||||
|
m->deinit();
|
||||||
|
|
||||||
|
modules_handlers_unset(m->handlers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct httpd_module *
|
||||||
|
modules_search(const char *path)
|
||||||
|
{
|
||||||
|
struct httpd_module **ptr;
|
||||||
|
struct httpd_module *m;
|
||||||
|
const char **test;
|
||||||
|
bool is_found = false;
|
||||||
|
|
||||||
|
for (ptr = httpd_modules; *ptr; ptr++)
|
||||||
|
{
|
||||||
|
m = *ptr;
|
||||||
|
if (!m->subpaths || !m->request)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (test = m->subpaths; *test && !is_found; test++)
|
||||||
|
is_found = (strncmp(path, *test, strlen(*test)) == 0);
|
||||||
|
|
||||||
|
for (test = m->fullpaths; *test && !is_found; test++)
|
||||||
|
is_found = (strcmp(path, *test) == 0);
|
||||||
|
|
||||||
|
if (is_found)
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/* --------------------------- REQUEST HELPERS ------------------------------ */
|
/* --------------------------- REQUEST HELPERS ------------------------------ */
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
httpd_redirect_to(struct evhttp_request *req, const char *path)
|
httpd_redirect_to(struct evhttp_request *req, const char *path)
|
||||||
{
|
{
|
||||||
@ -745,7 +783,9 @@ httpd_gen_cb(struct evhttp_request *req, void *arg)
|
|||||||
{
|
{
|
||||||
struct evkeyvalq *input_headers;
|
struct evkeyvalq *input_headers;
|
||||||
struct evkeyvalq *output_headers;
|
struct evkeyvalq *output_headers;
|
||||||
|
struct httpd_request hreq;
|
||||||
struct httpd_uri_parsed *parsed;
|
struct httpd_uri_parsed *parsed;
|
||||||
|
struct httpd_module *m;
|
||||||
const char *uri;
|
const char *uri;
|
||||||
|
|
||||||
// Clear the proxy request flag set by evhttp if the request URI was absolute.
|
// Clear the proxy request flag set by evhttp if the request URI was absolute.
|
||||||
@ -790,40 +830,11 @@ httpd_gen_cb(struct evhttp_request *req, void *arg)
|
|||||||
goto serve_file;
|
goto serve_file;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dispatch protocol-specific handlers */
|
m = modules_search(parsed->path);
|
||||||
if (dacp_is_request(parsed->path))
|
if (m)
|
||||||
{
|
{
|
||||||
dacp_request(req, parsed);
|
httpd_request_parse(&hreq, req, parsed, NULL, m->handlers);
|
||||||
goto out;
|
m->request(&hreq);
|
||||||
}
|
|
||||||
else if (daap_is_request(parsed->path))
|
|
||||||
{
|
|
||||||
daap_request(req, parsed);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
else if (jsonapi_is_request(parsed->path))
|
|
||||||
{
|
|
||||||
jsonapi_request(req, parsed);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
else if (artworkapi_is_request(parsed->path))
|
|
||||||
{
|
|
||||||
artworkapi_request(req, parsed);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
else if (streaming_is_request(parsed->path))
|
|
||||||
{
|
|
||||||
streaming_request(req, parsed);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
else if (oauth_is_request(parsed->path))
|
|
||||||
{
|
|
||||||
oauth_request(req, parsed);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
else if (rsp_is_request(parsed->path))
|
|
||||||
{
|
|
||||||
rsp_request(req, parsed);
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -941,20 +952,21 @@ httpd_uri_parse(const char *uri)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct httpd_request *
|
int
|
||||||
httpd_request_parse(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed, const char *user_agent, struct httpd_uri_map *uri_map)
|
httpd_request_parse(struct httpd_request *hreq, struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed, const char *user_agent, struct httpd_uri_map *uri_map)
|
||||||
{
|
{
|
||||||
struct httpd_request *hreq;
|
;
|
||||||
struct evhttp_connection *evcon;
|
struct evhttp_connection *evcon;
|
||||||
struct evkeyvalq *headers;
|
struct evkeyvalq *headers;
|
||||||
struct httpd_uri_map *uri;
|
struct httpd_uri_map *uri;
|
||||||
int req_method;
|
int req_method;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
CHECK_NULL(L_HTTPD, hreq = calloc(1, sizeof(struct httpd_request)));
|
memset(hreq, 0, sizeof(struct httpd_request));
|
||||||
|
|
||||||
// Note req is allowed to be NULL
|
// Note req is allowed to be NULL
|
||||||
hreq->req = req;
|
hreq->req = req;
|
||||||
|
hreq->uri = uri_parsed->uri;
|
||||||
hreq->uri_parsed = uri_parsed;
|
hreq->uri_parsed = uri_parsed;
|
||||||
hreq->query = &(uri_parsed->ev_query);
|
hreq->query = &(uri_parsed->ev_query);
|
||||||
req_method = 0;
|
req_method = 0;
|
||||||
@ -988,58 +1000,13 @@ httpd_request_parse(struct evhttp_request *req, struct httpd_uri_parsed *uri_par
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
hreq->handler = uri->handler;
|
hreq->handler = uri->handler;
|
||||||
return hreq; // Success
|
return 0; // Success
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handler not found, that's an error
|
// Handler not found, that's an error
|
||||||
free(hreq);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
httpd_handlers_set(struct httpd_uri_map *uri_map)
|
|
||||||
{
|
|
||||||
struct httpd_uri_map *uri;
|
|
||||||
char buf[64];
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
for (uri = uri_map; uri->handler; uri++)
|
|
||||||
{
|
|
||||||
uri->preg = calloc(1, sizeof(regex_t));
|
|
||||||
if (!uri->preg)
|
|
||||||
{
|
|
||||||
DPRINTF(E_LOG, L_HTTPD, "Error setting URI handler, out of memory");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = regcomp(uri->preg, uri->regexp, REG_EXTENDED | REG_NOSUB);
|
|
||||||
if (ret != 0)
|
|
||||||
{
|
|
||||||
regerror(ret, uri->preg, buf, sizeof(buf));
|
|
||||||
DPRINTF(E_LOG, L_HTTPD, "Error setting URI handler, regexp error: %s\n", buf);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
error:
|
|
||||||
httpd_handlers_unset(uri_map);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
httpd_handlers_unset(struct httpd_uri_map *uri_map)
|
|
||||||
{
|
|
||||||
struct httpd_uri_map *uri;
|
|
||||||
|
|
||||||
for (uri = uri_map; uri->preg; uri++)
|
|
||||||
{
|
|
||||||
regfree(uri->preg); // Frees allocation by regcomp
|
|
||||||
free(uri->preg); // Frees our own calloc
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Thread: httpd */
|
/* Thread: httpd */
|
||||||
void
|
void
|
||||||
httpd_stream_file(struct evhttp_request *req, int id)
|
httpd_stream_file(struct evhttp_request *req, int id)
|
||||||
@ -1631,7 +1598,7 @@ httpd_basic_auth(struct evhttp_request *req, const char *user, const char *passw
|
|||||||
headers = evhttp_request_get_output_headers(req);
|
headers = evhttp_request_get_output_headers(req);
|
||||||
evhttp_add_header(headers, "WWW-Authenticate", header);
|
evhttp_add_header(headers, "WWW-Authenticate", header);
|
||||||
|
|
||||||
evbuffer_add(evbuf, http_reply_401, strlen(http_reply_401));
|
evbuffer_add_printf(evbuf, ERR_PAGE, 401, "Unauthorized", "Authorization required");
|
||||||
|
|
||||||
httpd_send_reply(req, 401, "Unauthorized", evbuf, HTTPD_SEND_NO_GZIP);
|
httpd_send_reply(req, 401, "Unauthorized", evbuf, HTTPD_SEND_NO_GZIP);
|
||||||
|
|
||||||
@ -1688,54 +1655,6 @@ httpd_init(const char *webroot)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = rsp_init();
|
|
||||||
if (ret < 0)
|
|
||||||
{
|
|
||||||
DPRINTF(E_FATAL, L_HTTPD, "RSP protocol init failed\n");
|
|
||||||
|
|
||||||
goto rsp_fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = daap_init();
|
|
||||||
if (ret < 0)
|
|
||||||
{
|
|
||||||
DPRINTF(E_FATAL, L_HTTPD, "DAAP protocol init failed\n");
|
|
||||||
|
|
||||||
goto daap_fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = dacp_init();
|
|
||||||
if (ret < 0)
|
|
||||||
{
|
|
||||||
DPRINTF(E_FATAL, L_HTTPD, "DACP protocol init failed\n");
|
|
||||||
|
|
||||||
goto dacp_fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = jsonapi_init();
|
|
||||||
if (ret < 0)
|
|
||||||
{
|
|
||||||
DPRINTF(E_FATAL, L_HTTPD, "JSON api init failed\n");
|
|
||||||
|
|
||||||
goto jsonapi_fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = artworkapi_init();
|
|
||||||
if (ret < 0)
|
|
||||||
{
|
|
||||||
DPRINTF(E_FATAL, L_HTTPD, "Artwork init failed\n");
|
|
||||||
|
|
||||||
goto artworkapi_fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = oauth_init();
|
|
||||||
if (ret < 0)
|
|
||||||
{
|
|
||||||
DPRINTF(E_FATAL, L_HTTPD, "OAuth init failed\n");
|
|
||||||
|
|
||||||
goto oauth_fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef HAVE_LIBWEBSOCKETS
|
#ifdef HAVE_LIBWEBSOCKETS
|
||||||
ret = websocket_init();
|
ret = websocket_init();
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
@ -1746,7 +1665,13 @@ httpd_init(const char *webroot)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
streaming_init();
|
ret = modules_init();
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
DPRINTF(E_FATAL, L_HTTPD, "Modules init failed\n");
|
||||||
|
|
||||||
|
goto modules_fail;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef HAVE_EVENTFD
|
#ifdef HAVE_EVENTFD
|
||||||
exit_efd = eventfd(0, EFD_CLOEXEC);
|
exit_efd = eventfd(0, EFD_CLOEXEC);
|
||||||
@ -1835,23 +1760,12 @@ httpd_init(const char *webroot)
|
|||||||
close(exit_pipe[1]);
|
close(exit_pipe[1]);
|
||||||
#endif
|
#endif
|
||||||
pipe_fail:
|
pipe_fail:
|
||||||
streaming_deinit();
|
modules_fail:
|
||||||
|
modules_deinit();
|
||||||
#ifdef HAVE_LIBWEBSOCKETS
|
#ifdef HAVE_LIBWEBSOCKETS
|
||||||
websocket_deinit();
|
websocket_deinit();
|
||||||
websocket_fail:
|
websocket_fail:
|
||||||
#endif
|
#endif
|
||||||
oauth_deinit();
|
|
||||||
oauth_fail:
|
|
||||||
artworkapi_deinit();
|
|
||||||
artworkapi_fail:
|
|
||||||
jsonapi_deinit();
|
|
||||||
jsonapi_fail:
|
|
||||||
dacp_deinit();
|
|
||||||
dacp_fail:
|
|
||||||
daap_deinit();
|
|
||||||
daap_fail:
|
|
||||||
rsp_deinit();
|
|
||||||
rsp_fail:
|
|
||||||
event_base_free(evbase_httpd);
|
event_base_free(evbase_httpd);
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
@ -1891,15 +1805,11 @@ httpd_deinit(void)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
streaming_deinit();
|
modules_deinit();
|
||||||
|
|
||||||
#ifdef HAVE_LIBWEBSOCKETS
|
#ifdef HAVE_LIBWEBSOCKETS
|
||||||
websocket_deinit();
|
websocket_deinit();
|
||||||
#endif
|
#endif
|
||||||
oauth_deinit();
|
|
||||||
jsonapi_deinit();
|
|
||||||
rsp_deinit();
|
|
||||||
dacp_deinit();
|
|
||||||
daap_deinit();
|
|
||||||
|
|
||||||
#ifdef HAVE_EVENTFD
|
#ifdef HAVE_EVENTFD
|
||||||
close(exit_efd);
|
close(exit_efd);
|
||||||
|
15
src/httpd.h
15
src/httpd.h
@ -44,6 +44,8 @@ struct httpd_uri_parsed
|
|||||||
struct httpd_request {
|
struct httpd_request {
|
||||||
// User-agent (if available)
|
// User-agent (if available)
|
||||||
const char *user_agent;
|
const char *user_agent;
|
||||||
|
// TODO
|
||||||
|
const char *uri;
|
||||||
// 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
|
||||||
@ -87,20 +89,13 @@ struct httpd_uri_parsed *
|
|||||||
httpd_uri_parse(const char *uri);
|
httpd_uri_parse(const char *uri);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Parse a request into the httpd_request struct. It can later be freed with
|
* Parse a request into the httpd_request struct. Nothing is copied, so the
|
||||||
* free(), unless the module has allocated something to *extra_data. Note that
|
* pointers in the returned struct are only valid as long as the inputs are
|
||||||
* the pointers in the returned struct are only valid as long as the inputs are
|
|
||||||
* still valid. If req is not null, then we will find the user-agent from the
|
* still valid. If req is not null, then we will find the user-agent from the
|
||||||
* request headers, except if provided as an argument to this function.
|
* request headers, except if provided as an argument to this function.
|
||||||
*/
|
*/
|
||||||
struct httpd_request *
|
|
||||||
httpd_request_parse(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed, const char *user_agent, struct httpd_uri_map *uri_map);
|
|
||||||
|
|
||||||
int
|
int
|
||||||
httpd_handlers_set(struct httpd_uri_map *uri_map);
|
httpd_request_parse(struct httpd_request *hreq, struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed, const char *user_agent, struct httpd_uri_map *uri_map);
|
||||||
|
|
||||||
void
|
|
||||||
httpd_handlers_unset(struct httpd_uri_map *uri_map);
|
|
||||||
|
|
||||||
void
|
void
|
||||||
httpd_stream_file(struct evhttp_request *req, int id);
|
httpd_stream_file(struct evhttp_request *req, int id);
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "httpd_artworkapi.h"
|
#include "httpd_internal.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
#include "player.h"
|
#include "player.h"
|
||||||
@ -149,23 +149,22 @@ static struct httpd_uri_map artworkapi_handlers[] =
|
|||||||
|
|
||||||
|
|
||||||
/* ------------------------------- API --------------------------------- */
|
/* ------------------------------- API --------------------------------- */
|
||||||
void
|
|
||||||
artworkapi_request(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed)
|
static void
|
||||||
|
artworkapi_request(struct httpd_request *hreq)
|
||||||
{
|
{
|
||||||
struct httpd_request *hreq;
|
|
||||||
int status_code;
|
int status_code;
|
||||||
|
|
||||||
DPRINTF(E_DBG, L_WEB, "Artwork api request: '%s'\n", uri_parsed->uri);
|
DPRINTF(E_DBG, L_WEB, "Artwork api request: '%s'\n", hreq->uri);
|
||||||
|
|
||||||
if (!httpd_admin_check_auth(req))
|
if (!httpd_admin_check_auth(hreq->req))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
hreq = httpd_request_parse(req, uri_parsed, NULL, artworkapi_handlers);
|
if (!hreq->handler)
|
||||||
if (!hreq)
|
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_WEB, "Unrecognized path '%s' in artwork api request: '%s'\n", uri_parsed->path, uri_parsed->uri);
|
DPRINTF(E_LOG, L_WEB, "Unrecognized path in artwork api request: '%s'\n", hreq->uri);
|
||||||
|
|
||||||
httpd_send_error(req, HTTP_BADREQUEST, "Bad Request");
|
httpd_send_error(hreq->req, HTTP_BADREQUEST, "Bad Request");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,48 +175,33 @@ artworkapi_request(struct evhttp_request *req, struct httpd_uri_parsed *uri_pars
|
|||||||
switch (status_code)
|
switch (status_code)
|
||||||
{
|
{
|
||||||
case HTTP_OK: /* 200 OK */
|
case HTTP_OK: /* 200 OK */
|
||||||
httpd_send_reply(req, status_code, "OK", hreq->reply, HTTPD_SEND_NO_GZIP);
|
httpd_send_reply(hreq->req, status_code, "OK", hreq->reply, HTTPD_SEND_NO_GZIP);
|
||||||
break;
|
break;
|
||||||
case HTTP_NOCONTENT: /* 204 No Content */
|
case HTTP_NOCONTENT: /* 204 No Content */
|
||||||
httpd_send_reply(req, status_code, "No Content", hreq->reply, HTTPD_SEND_NO_GZIP);
|
httpd_send_reply(hreq->req, status_code, "No Content", hreq->reply, HTTPD_SEND_NO_GZIP);
|
||||||
break;
|
break;
|
||||||
case HTTP_NOTMODIFIED: /* 304 Not Modified */
|
case HTTP_NOTMODIFIED: /* 304 Not Modified */
|
||||||
httpd_send_reply(req, HTTP_NOTMODIFIED, NULL, NULL, HTTPD_SEND_NO_GZIP);
|
httpd_send_reply(hreq->req, HTTP_NOTMODIFIED, NULL, NULL, HTTPD_SEND_NO_GZIP);
|
||||||
break;
|
break;
|
||||||
case HTTP_BADREQUEST: /* 400 Bad Request */
|
case HTTP_BADREQUEST: /* 400 Bad Request */
|
||||||
httpd_send_error(req, status_code, "Bad Request");
|
httpd_send_error(hreq->req, status_code, "Bad Request");
|
||||||
break;
|
break;
|
||||||
case HTTP_NOTFOUND: /* 404 Not Found */
|
case HTTP_NOTFOUND: /* 404 Not Found */
|
||||||
httpd_send_error(req, status_code, "Not Found");
|
httpd_send_error(hreq->req, status_code, "Not Found");
|
||||||
break;
|
break;
|
||||||
case HTTP_INTERNAL: /* 500 Internal Server Error */
|
case HTTP_INTERNAL: /* 500 Internal Server Error */
|
||||||
default:
|
default:
|
||||||
httpd_send_error(req, HTTP_INTERNAL, "Internal Server Error");
|
httpd_send_error(hreq->req, HTTP_INTERNAL, "Internal Server Error");
|
||||||
}
|
}
|
||||||
|
|
||||||
evbuffer_free(hreq->reply);
|
evbuffer_free(hreq->reply);
|
||||||
free(hreq);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
struct httpd_module httpd_artworkapi =
|
||||||
artworkapi_is_request(const char *path)
|
|
||||||
{
|
{
|
||||||
if (strncmp(path, "/artwork/", strlen("/artwork/")) == 0)
|
.name = "Artwork API",
|
||||||
return 1;
|
.type = MODULE_ARTWORKAPI,
|
||||||
|
.subpaths = { "/artwork/", NULL },
|
||||||
return 0;
|
.handlers = artworkapi_handlers,
|
||||||
}
|
.request = artworkapi_request,
|
||||||
|
};
|
||||||
int
|
|
||||||
artworkapi_init(void)
|
|
||||||
{
|
|
||||||
CHECK_ERR(L_WEB, httpd_handlers_set(artworkapi_handlers));
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
artworkapi_deinit(void)
|
|
||||||
{
|
|
||||||
httpd_handlers_unset(artworkapi_handlers);
|
|
||||||
}
|
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
#ifndef __HTTPD_ARTWORK_H__
|
|
||||||
#define __HTTPD_ARTWORK_H__
|
|
||||||
|
|
||||||
#include "httpd.h"
|
|
||||||
|
|
||||||
int
|
|
||||||
artworkapi_init(void);
|
|
||||||
|
|
||||||
void
|
|
||||||
artworkapi_deinit(void);
|
|
||||||
|
|
||||||
void
|
|
||||||
artworkapi_request(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed);
|
|
||||||
|
|
||||||
int
|
|
||||||
artworkapi_is_request(const char *path);
|
|
||||||
|
|
||||||
#endif
|
|
104
src/httpd_daap.c
104
src/httpd_daap.c
@ -43,6 +43,7 @@
|
|||||||
|
|
||||||
#include <event2/event.h>
|
#include <event2/event.h>
|
||||||
|
|
||||||
|
#include "httpd_internal.h"
|
||||||
#include "httpd_daap.h"
|
#include "httpd_daap.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "db.h"
|
#include "db.h"
|
||||||
@ -2224,10 +2225,9 @@ static struct httpd_uri_map daap_handlers[] =
|
|||||||
* iTunes 12.1 gives us an absolute request-uri for streaming like
|
* iTunes 12.1 gives us an absolute request-uri for streaming like
|
||||||
* http://10.1.1.20:3689/databases/1/items/1.mp3
|
* http://10.1.1.20:3689/databases/1/items/1.mp3
|
||||||
*/
|
*/
|
||||||
void
|
static void
|
||||||
daap_request(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed)
|
daap_request(struct httpd_request *hreq)
|
||||||
{
|
{
|
||||||
struct httpd_request *hreq;
|
|
||||||
struct evkeyvalq *headers;
|
struct evkeyvalq *headers;
|
||||||
struct timespec start;
|
struct timespec start;
|
||||||
struct timespec end;
|
struct timespec end;
|
||||||
@ -2237,14 +2237,13 @@ daap_request(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed)
|
|||||||
int ret;
|
int ret;
|
||||||
int msec;
|
int msec;
|
||||||
|
|
||||||
DPRINTF(E_DBG, L_DAAP, "DAAP request: '%s'\n", uri_parsed->uri);
|
DPRINTF(E_DBG, L_DAAP, "DAAP request: '%s'\n", hreq->uri);
|
||||||
|
|
||||||
hreq = httpd_request_parse(req, uri_parsed, NULL, daap_handlers);
|
if (!hreq->handler)
|
||||||
if (!hreq)
|
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_DAAP, "Unrecognized path '%s' in DAAP request: '%s'\n", uri_parsed->path, uri_parsed->uri);
|
DPRINTF(E_LOG, L_DAAP, "Unrecognized path in DAAP request: '%s'\n", hreq->uri);
|
||||||
|
|
||||||
httpd_send_error(req, HTTP_BADREQUEST, "Bad Request");
|
httpd_send_error(hreq->req, HTTP_BADREQUEST, "Bad Request");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2254,7 +2253,7 @@ daap_request(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed)
|
|||||||
{
|
{
|
||||||
ret = safe_atoi32(param, &id);
|
ret = safe_atoi32(param, &id);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
DPRINTF(E_LOG, L_DAAP, "Ignoring non-numeric session id in DAAP request: '%s'\n", uri_parsed->uri);
|
DPRINTF(E_LOG, L_DAAP, "Ignoring non-numeric session id in DAAP request: '%s'\n", hreq->uri);
|
||||||
else
|
else
|
||||||
hreq->extra_data = daap_session_get(id);
|
hreq->extra_data = daap_session_get(id);
|
||||||
}
|
}
|
||||||
@ -2270,12 +2269,11 @@ daap_request(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed)
|
|||||||
ret = daap_request_authorize(hreq);
|
ret = daap_request_authorize(hreq);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
free(hreq);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set reply headers
|
// Set reply headers
|
||||||
headers = evhttp_request_get_output_headers(req);
|
headers = evhttp_request_get_output_headers(hreq->req);
|
||||||
evhttp_add_header(headers, "Accept-Ranges", "bytes");
|
evhttp_add_header(headers, "Accept-Ranges", "bytes");
|
||||||
evhttp_add_header(headers, "DAAP-Server", PACKAGE_NAME "/" VERSION);
|
evhttp_add_header(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
|
||||||
@ -2287,15 +2285,14 @@ daap_request(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed)
|
|||||||
CHECK_NULL(L_DAAP, hreq->reply = evbuffer_new());
|
CHECK_NULL(L_DAAP, hreq->reply = evbuffer_new());
|
||||||
|
|
||||||
// Try the cache
|
// Try the cache
|
||||||
ret = cache_daap_get(hreq->reply, uri_parsed->uri);
|
ret = cache_daap_get(hreq->reply, 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
|
||||||
evhttp_add_header(headers, "Content-Encoding", "gzip");
|
evhttp_add_header(headers, "Content-Encoding", "gzip");
|
||||||
httpd_send_reply(req, HTTP_OK, "OK", hreq->reply, HTTPD_SEND_NO_GZIP); // TODO not all want this reply
|
httpd_send_reply(hreq->req, HTTP_OK, "OK", hreq->reply, HTTPD_SEND_NO_GZIP); // TODO not all want this reply
|
||||||
|
|
||||||
evbuffer_free(hreq->reply);
|
evbuffer_free(hreq->reply);
|
||||||
free(hreq);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2312,38 +2309,9 @@ daap_request(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed)
|
|||||||
DPRINTF(E_DBG, L_DAAP, "DAAP request handled in %d milliseconds\n", msec);
|
DPRINTF(E_DBG, L_DAAP, "DAAP request handled in %d milliseconds\n", msec);
|
||||||
|
|
||||||
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(uri_parsed->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);
|
evbuffer_free(hreq->reply);
|
||||||
free(hreq);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
daap_is_request(const char *path)
|
|
||||||
{
|
|
||||||
if (strncmp(path, "/databases/", strlen("/databases/")) == 0)
|
|
||||||
return 1;
|
|
||||||
if (strcmp(path, "/databases") == 0)
|
|
||||||
return 1;
|
|
||||||
if (strcmp(path, "/server-info") == 0)
|
|
||||||
return 1;
|
|
||||||
if (strcmp(path, "/content-codes") == 0)
|
|
||||||
return 1;
|
|
||||||
if (strcmp(path, "/login") == 0)
|
|
||||||
return 1;
|
|
||||||
if (strcmp(path, "/update") == 0)
|
|
||||||
return 1;
|
|
||||||
if (strcmp(path, "/activity") == 0)
|
|
||||||
return 1;
|
|
||||||
if (strcmp(path, "/logout") == 0)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
#ifdef DMAP_TEST
|
|
||||||
if (strcmp(path, "/dmap-test") == 0)
|
|
||||||
return 1;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
@ -2363,7 +2331,7 @@ daap_session_is_valid(int id)
|
|||||||
struct evbuffer *
|
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 httpd_uri_parsed *uri_parsed;
|
struct httpd_uri_parsed *uri_parsed;
|
||||||
struct evbuffer *reply;
|
struct evbuffer *reply;
|
||||||
struct daap_session session;
|
struct daap_session session;
|
||||||
@ -2377,50 +2345,46 @@ daap_reply_build(const char *uri, const char *user_agent, int is_remote)
|
|||||||
if (!uri_parsed)
|
if (!uri_parsed)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
hreq = httpd_request_parse(NULL, uri_parsed, user_agent, daap_handlers);
|
ret = httpd_request_parse(&hreq, NULL, uri_parsed, user_agent, daap_handlers);
|
||||||
if (!hreq)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_DAAP, "Cannot build reply, unrecognized path '%s' in request: '%s'\n", uri_parsed->path, uri_parsed->uri);
|
DPRINTF(E_LOG, L_DAAP, "Cannot build reply, unrecognized path '%s' in request: '%s'\n", uri_parsed->path, uri_parsed->uri);
|
||||||
goto out_free_uri;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(&session, 0, sizeof(struct daap_session));
|
memset(&session, 0, sizeof(struct daap_session));
|
||||||
session.is_remote = (bool)is_remote;
|
session.is_remote = (bool)is_remote;
|
||||||
|
|
||||||
hreq->extra_data = &session;
|
hreq.extra_data = &session;
|
||||||
|
|
||||||
CHECK_NULL(L_DAAP, hreq->reply = evbuffer_new());
|
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);
|
evbuffer_free(hreq.reply);
|
||||||
goto out_free_hreq;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
reply = hreq->reply;
|
reply = hreq.reply;
|
||||||
|
|
||||||
out_free_hreq:
|
out:
|
||||||
free(hreq);
|
|
||||||
out_free_uri:
|
|
||||||
httpd_uri_free(uri_parsed);
|
httpd_uri_free(uri_parsed);
|
||||||
|
|
||||||
return reply;
|
return reply;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int
|
||||||
daap_init(void)
|
daap_init(void)
|
||||||
{
|
{
|
||||||
srand((unsigned)time(NULL));
|
srand((unsigned)time(NULL));
|
||||||
current_rev = 2;
|
current_rev = 2;
|
||||||
update_requests = NULL;
|
update_requests = NULL;
|
||||||
|
|
||||||
CHECK_ERR(L_DAAP, httpd_handlers_set(daap_handlers));
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void
|
||||||
daap_deinit(void)
|
daap_deinit(void)
|
||||||
{
|
{
|
||||||
struct daap_session *s;
|
struct daap_session *s;
|
||||||
@ -2446,6 +2410,20 @@ daap_deinit(void)
|
|||||||
|
|
||||||
update_free(ur);
|
update_free(ur);
|
||||||
}
|
}
|
||||||
|
|
||||||
httpd_handlers_unset(daap_handlers);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct httpd_module httpd_daap =
|
||||||
|
{
|
||||||
|
.name = "DAAP",
|
||||||
|
.type = MODULE_DAAP,
|
||||||
|
.subpaths = { "/databases/", NULL },
|
||||||
|
#ifdef DMAP_TEST
|
||||||
|
.fullpaths = { "/databases", "/server-info", "/content-codes", "/login", "/update", "/activity", "/logout", "/dmap-test", NULL },
|
||||||
|
#else
|
||||||
|
.fullpaths = { "/databases", "/server-info", "/content-codes", "/login", "/update", "/activity", "/logout", NULL },
|
||||||
|
#endif
|
||||||
|
.handlers = daap_handlers,
|
||||||
|
.init = daap_init,
|
||||||
|
.deinit = daap_deinit,
|
||||||
|
.request = daap_request,
|
||||||
|
};
|
||||||
|
@ -2,20 +2,6 @@
|
|||||||
#ifndef __HTTPD_DAAP_H__
|
#ifndef __HTTPD_DAAP_H__
|
||||||
#define __HTTPD_DAAP_H__
|
#define __HTTPD_DAAP_H__
|
||||||
|
|
||||||
#include "httpd.h"
|
|
||||||
|
|
||||||
int
|
|
||||||
daap_init(void);
|
|
||||||
|
|
||||||
void
|
|
||||||
daap_deinit(void);
|
|
||||||
|
|
||||||
void
|
|
||||||
daap_request(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed);
|
|
||||||
|
|
||||||
int
|
|
||||||
daap_is_request(const char *path);
|
|
||||||
|
|
||||||
int
|
int
|
||||||
daap_session_is_valid(int id);
|
daap_session_is_valid(int id);
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
|
|
||||||
#include <event2/event.h>
|
#include <event2/event.h>
|
||||||
|
|
||||||
#include "httpd_dacp.h"
|
#include "httpd_internal.h"
|
||||||
#include "httpd_daap.h"
|
#include "httpd_daap.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
@ -2862,24 +2862,22 @@ static struct httpd_uri_map dacp_handlers[] =
|
|||||||
|
|
||||||
/* ------------------------------- DACP API --------------------------------- */
|
/* ------------------------------- DACP API --------------------------------- */
|
||||||
|
|
||||||
void
|
static void
|
||||||
dacp_request(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed)
|
dacp_request(struct httpd_request *hreq)
|
||||||
{
|
{
|
||||||
struct httpd_request *hreq;
|
|
||||||
struct evkeyvalq *headers;
|
struct evkeyvalq *headers;
|
||||||
|
|
||||||
DPRINTF(E_DBG, L_DACP, "DACP request: '%s'\n", uri_parsed->uri);
|
DPRINTF(E_DBG, L_DACP, "DACP request: '%s'\n", hreq->uri);
|
||||||
|
|
||||||
hreq = httpd_request_parse(req, uri_parsed, NULL, dacp_handlers);
|
if (!hreq->handler)
|
||||||
if (!hreq)
|
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_DACP, "Unrecognized path '%s' in DACP request: '%s'\n", uri_parsed->path, uri_parsed->uri);
|
DPRINTF(E_LOG, L_DACP, "Unrecognized path in DACP request: '%s'\n", hreq->uri);
|
||||||
|
|
||||||
httpd_send_error(req, HTTP_BADREQUEST, "Bad Request");
|
httpd_send_error(hreq->req, HTTP_BADREQUEST, "Bad Request");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
headers = evhttp_request_get_output_headers(req);
|
headers = evhttp_request_get_output_headers(hreq->req);
|
||||||
evhttp_add_header(headers, "DAAP-Server", PACKAGE_NAME "/" VERSION);
|
evhttp_add_header(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");
|
evhttp_add_header(headers, "Content-Type", "application/x-dmap-tagged");
|
||||||
@ -2889,21 +2887,13 @@ dacp_request(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed)
|
|||||||
hreq->handler(hreq);
|
hreq->handler(hreq);
|
||||||
|
|
||||||
evbuffer_free(hreq->reply);
|
evbuffer_free(hreq->reply);
|
||||||
free(hreq);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
// Forward
|
||||||
dacp_is_request(const char *path)
|
static void
|
||||||
{
|
dacp_deinit(void);
|
||||||
if (strncmp(path, "/ctrl-int/", strlen("/ctrl-int/")) == 0)
|
|
||||||
return 1;
|
|
||||||
if (strcmp(path, "/ctrl-int") == 0)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
return 0;
|
static int
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
dacp_init(void)
|
dacp_init(void)
|
||||||
{
|
{
|
||||||
current_rev = 2;
|
current_rev = 2;
|
||||||
@ -2920,8 +2910,6 @@ dacp_init(void)
|
|||||||
dummy_queue_item.album = CFG_NAME_UNKNOWN_ALBUM;
|
dummy_queue_item.album = CFG_NAME_UNKNOWN_ALBUM;
|
||||||
dummy_queue_item.genre = CFG_NAME_UNKNOWN_GENRE;
|
dummy_queue_item.genre = CFG_NAME_UNKNOWN_GENRE;
|
||||||
|
|
||||||
CHECK_ERR(L_DACP, httpd_handlers_set(dacp_handlers));
|
|
||||||
|
|
||||||
#ifdef HAVE_EVENTFD
|
#ifdef HAVE_EVENTFD
|
||||||
update_efd = eventfd(0, EFD_CLOEXEC);
|
update_efd = eventfd(0, EFD_CLOEXEC);
|
||||||
if (update_efd < 0)
|
if (update_efd < 0)
|
||||||
@ -2959,7 +2947,7 @@ dacp_init(void)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void
|
||||||
dacp_deinit(void)
|
dacp_deinit(void)
|
||||||
{
|
{
|
||||||
struct dacp_update_request *ur;
|
struct dacp_update_request *ur;
|
||||||
@ -2993,6 +2981,16 @@ dacp_deinit(void)
|
|||||||
close(update_pipe[0]);
|
close(update_pipe[0]);
|
||||||
close(update_pipe[1]);
|
close(update_pipe[1]);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
httpd_handlers_unset(dacp_handlers);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct httpd_module httpd_dacp =
|
||||||
|
{
|
||||||
|
.name = "DACP",
|
||||||
|
.type = MODULE_DACP,
|
||||||
|
.subpaths = { "/ctrl-int/", NULL },
|
||||||
|
.fullpaths = { "/ctrl-int", NULL },
|
||||||
|
.handlers = dacp_handlers,
|
||||||
|
.init = dacp_init,
|
||||||
|
.deinit = dacp_deinit,
|
||||||
|
.request = dacp_request,
|
||||||
|
};
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
|
|
||||||
#ifndef __HTTPD_DACP_H__
|
|
||||||
#define __HTTPD_DACP_H__
|
|
||||||
|
|
||||||
#include "httpd.h"
|
|
||||||
|
|
||||||
int
|
|
||||||
dacp_init(void);
|
|
||||||
|
|
||||||
void
|
|
||||||
dacp_deinit(void);
|
|
||||||
|
|
||||||
void
|
|
||||||
dacp_request(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed);
|
|
||||||
|
|
||||||
int
|
|
||||||
dacp_is_request(const char *path);
|
|
||||||
|
|
||||||
#endif /* !__HTTPD_DACP_H__ */
|
|
37
src/httpd_internal.h
Normal file
37
src/httpd_internal.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
|
||||||
|
#ifndef __HTTPD_INTERNAL_H__
|
||||||
|
#define __HTTPD_INTERNAL_H__
|
||||||
|
|
||||||
|
#include "httpd.h" // TODO remove and transfer
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* !__HTTPD_INTERNAL_H__ */
|
@ -41,7 +41,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
#include "httpd_jsonapi.h"
|
#include "httpd_internal.h"
|
||||||
#include "conffile.h"
|
#include "conffile.h"
|
||||||
#include "db.h"
|
#include "db.h"
|
||||||
#ifdef LASTFM
|
#ifdef LASTFM
|
||||||
@ -4716,24 +4716,24 @@ static struct httpd_uri_map adm_handlers[] =
|
|||||||
|
|
||||||
/* ------------------------------- JSON API --------------------------------- */
|
/* ------------------------------- JSON API --------------------------------- */
|
||||||
|
|
||||||
void
|
static void
|
||||||
jsonapi_request(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed)
|
jsonapi_request(struct httpd_request *hreq)
|
||||||
{
|
{
|
||||||
struct httpd_request *hreq;
|
;
|
||||||
struct evkeyvalq *headers;
|
struct evkeyvalq *headers;
|
||||||
int status_code;
|
int status_code;
|
||||||
|
|
||||||
DPRINTF(E_DBG, L_WEB, "JSON api request: '%s'\n", uri_parsed->uri);
|
DPRINTF(E_DBG, L_WEB, "JSON api request: '%s'\n", hreq->uri);
|
||||||
|
|
||||||
if (!httpd_admin_check_auth(req))
|
if (!httpd_admin_check_auth(hreq->req))
|
||||||
return;
|
|
||||||
|
|
||||||
hreq = httpd_request_parse(req, uri_parsed, NULL, adm_handlers);
|
|
||||||
if (!hreq)
|
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_WEB, "Unrecognized path '%s' in JSON api request: '%s'\n", uri_parsed->path, uri_parsed->uri);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
httpd_send_error(req, HTTP_BADREQUEST, "Bad Request");
|
if (!hreq->handler)
|
||||||
|
{
|
||||||
|
DPRINTF(E_LOG, L_WEB, "Unrecognized JSON API request: '%s'\n", hreq->uri);
|
||||||
|
httpd_send_error(hreq->req, HTTP_BADREQUEST, "Bad Request");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4742,62 +4742,47 @@ jsonapi_request(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed)
|
|||||||
status_code = hreq->handler(hreq);
|
status_code = hreq->handler(hreq);
|
||||||
|
|
||||||
if (status_code >= 400)
|
if (status_code >= 400)
|
||||||
DPRINTF(E_LOG, L_WEB, "JSON api request failed with error code %d (%s)\n", status_code, uri_parsed->uri);
|
DPRINTF(E_LOG, L_WEB, "JSON api request failed with error code %d (%s)\n", status_code, hreq->uri);
|
||||||
|
|
||||||
switch (status_code)
|
switch (status_code)
|
||||||
{
|
{
|
||||||
case HTTP_OK: /* 200 OK */
|
case HTTP_OK: /* 200 OK */
|
||||||
headers = evhttp_request_get_output_headers(req);
|
headers = evhttp_request_get_output_headers(hreq->req);
|
||||||
evhttp_add_header(headers, "Content-Type", "application/json");
|
evhttp_add_header(headers, "Content-Type", "application/json");
|
||||||
httpd_send_reply(req, status_code, "OK", hreq->reply, HTTPD_SEND_NO_GZIP);
|
httpd_send_reply(hreq->req, status_code, "OK", hreq->reply, HTTPD_SEND_NO_GZIP);
|
||||||
break;
|
break;
|
||||||
case HTTP_NOCONTENT: /* 204 No Content */
|
case HTTP_NOCONTENT: /* 204 No Content */
|
||||||
httpd_send_reply(req, status_code, "No Content", hreq->reply, HTTPD_SEND_NO_GZIP);
|
httpd_send_reply(hreq->req, status_code, "No Content", hreq->reply, HTTPD_SEND_NO_GZIP);
|
||||||
break;
|
break;
|
||||||
case HTTP_NOTMODIFIED: /* 304 Not Modified */
|
case HTTP_NOTMODIFIED: /* 304 Not Modified */
|
||||||
httpd_send_reply(req, HTTP_NOTMODIFIED, NULL, NULL, HTTPD_SEND_NO_GZIP);
|
httpd_send_reply(hreq->req, HTTP_NOTMODIFIED, NULL, NULL, HTTPD_SEND_NO_GZIP);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case HTTP_BADREQUEST: /* 400 Bad Request */
|
case HTTP_BADREQUEST: /* 400 Bad Request */
|
||||||
httpd_send_error(req, status_code, "Bad Request");
|
httpd_send_error(hreq->req, status_code, "Bad Request");
|
||||||
break;
|
break;
|
||||||
case 403:
|
case 403:
|
||||||
httpd_send_error(req, status_code, "Forbidden");
|
httpd_send_error(hreq->req, status_code, "Forbidden");
|
||||||
break;
|
break;
|
||||||
case HTTP_NOTFOUND: /* 404 Not Found */
|
case HTTP_NOTFOUND: /* 404 Not Found */
|
||||||
httpd_send_error(req, status_code, "Not Found");
|
httpd_send_error(hreq->req, status_code, "Not Found");
|
||||||
break;
|
break;
|
||||||
case HTTP_SERVUNAVAIL: /* 503 */
|
case HTTP_SERVUNAVAIL: /* 503 */
|
||||||
httpd_send_error(req, status_code, "Service Unavailable");
|
httpd_send_error(hreq->req, status_code, "Service Unavailable");
|
||||||
break;
|
break;
|
||||||
case HTTP_INTERNAL: /* 500 Internal Server Error */
|
case HTTP_INTERNAL: /* 500 Internal Server Error */
|
||||||
default:
|
default:
|
||||||
httpd_send_error(req, HTTP_INTERNAL, "Internal Server Error");
|
httpd_send_error(hreq->req, HTTP_INTERNAL, "Internal Server Error");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
evbuffer_free(hreq->reply);
|
evbuffer_free(hreq->reply);
|
||||||
free(hreq);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int
|
||||||
jsonapi_is_request(const char *path)
|
|
||||||
{
|
|
||||||
if (strncmp(path, "/api/", strlen("/api/")) == 0)
|
|
||||||
return 1;
|
|
||||||
if (strcmp(path, "/api") == 0)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
jsonapi_init(void)
|
jsonapi_init(void)
|
||||||
{
|
{
|
||||||
char *temp_path;
|
char *temp_path;
|
||||||
|
|
||||||
CHECK_ERR(L_WEB, httpd_handlers_set(adm_handlers));
|
|
||||||
|
|
||||||
default_playlist_directory = NULL;
|
default_playlist_directory = NULL;
|
||||||
allow_modifying_stored_playlists = cfg_getbool(cfg_getsec(cfg, "library"), "allow_modifying_stored_playlists");
|
allow_modifying_stored_playlists = cfg_getbool(cfg_getsec(cfg, "library"), "allow_modifying_stored_playlists");
|
||||||
if (allow_modifying_stored_playlists)
|
if (allow_modifying_stored_playlists)
|
||||||
@ -4824,10 +4809,19 @@ jsonapi_init(void)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void
|
||||||
jsonapi_deinit(void)
|
jsonapi_deinit(void)
|
||||||
{
|
{
|
||||||
free(default_playlist_directory);
|
free(default_playlist_directory);
|
||||||
|
|
||||||
httpd_handlers_unset(adm_handlers);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct httpd_module httpd_jsonapi =
|
||||||
|
{
|
||||||
|
.name = "JSON API",
|
||||||
|
.type = MODULE_JSONAPI,
|
||||||
|
.subpaths = { "/api/", NULL },
|
||||||
|
.handlers = adm_handlers,
|
||||||
|
.init = jsonapi_init,
|
||||||
|
.deinit = jsonapi_deinit,
|
||||||
|
.request = jsonapi_request,
|
||||||
|
};
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
|
|
||||||
#ifndef __HTTPD_JSONAPI_H__
|
|
||||||
#define __HTTPD_JSONAPI_H__
|
|
||||||
|
|
||||||
#include "httpd.h"
|
|
||||||
|
|
||||||
int
|
|
||||||
jsonapi_init(void);
|
|
||||||
|
|
||||||
void
|
|
||||||
jsonapi_deinit(void);
|
|
||||||
|
|
||||||
void
|
|
||||||
jsonapi_request(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed);
|
|
||||||
|
|
||||||
int
|
|
||||||
jsonapi_is_request(const char *path);
|
|
||||||
|
|
||||||
#endif /* !__HTTPD_JSONAPI_H__ */
|
|
@ -28,7 +28,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
|
||||||
#include "httpd_oauth.h"
|
#include "httpd_internal.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
#include "conffile.h"
|
#include "conffile.h"
|
||||||
@ -90,48 +90,28 @@ static struct httpd_uri_map oauth_handlers[] =
|
|||||||
|
|
||||||
/* ------------------------------- OAUTH API -------------------------------- */
|
/* ------------------------------- OAUTH API -------------------------------- */
|
||||||
|
|
||||||
void
|
static void
|
||||||
oauth_request(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed)
|
oauth_request(struct httpd_request *hreq)
|
||||||
{
|
{
|
||||||
struct httpd_request *hreq;
|
DPRINTF(E_LOG, L_WEB, "OAuth request: '%s'\n", hreq->uri);
|
||||||
|
|
||||||
DPRINTF(E_LOG, L_WEB, "OAuth request: '%s'\n", uri_parsed->uri);
|
if (!hreq->handler)
|
||||||
|
|
||||||
hreq = httpd_request_parse(req, uri_parsed, NULL, oauth_handlers);
|
|
||||||
if (!hreq)
|
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_WEB, "Unrecognized path '%s' in OAuth request: '%s'\n", uri_parsed->path, uri_parsed->uri);
|
DPRINTF(E_LOG, L_WEB, "Unrecognized path in OAuth request: '%s'\n", hreq->uri);
|
||||||
|
|
||||||
httpd_send_error(req, HTTP_NOTFOUND, NULL);
|
httpd_send_error(hreq->req, HTTP_NOTFOUND, NULL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
hreq->handler(hreq);
|
hreq->handler(hreq);
|
||||||
|
|
||||||
free(hreq);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
struct httpd_module httpd_oauth =
|
||||||
oauth_is_request(const char *path)
|
|
||||||
{
|
{
|
||||||
if (strncmp(path, "/oauth/", strlen("/oauth/")) == 0)
|
.name = "OAuth",
|
||||||
return 1;
|
.type = MODULE_OAUTH,
|
||||||
if (strcmp(path, "/oauth") == 0)
|
.subpaths = { "/oauth/", NULL },
|
||||||
return 1;
|
.fullpaths = { "/oauth", NULL },
|
||||||
|
.handlers = oauth_handlers,
|
||||||
return 0;
|
.request = oauth_request,
|
||||||
}
|
};
|
||||||
|
|
||||||
int
|
|
||||||
oauth_init(void)
|
|
||||||
{
|
|
||||||
CHECK_ERR(L_WEB, httpd_handlers_set(oauth_handlers));
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
oauth_deinit(void)
|
|
||||||
{
|
|
||||||
httpd_handlers_unset(oauth_handlers);
|
|
||||||
}
|
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
#ifndef __HTTPD_OAUTH_H__
|
|
||||||
#define __HTTPD_OAUTH_H__
|
|
||||||
|
|
||||||
#include "httpd.h"
|
|
||||||
|
|
||||||
void
|
|
||||||
oauth_request(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed);
|
|
||||||
|
|
||||||
int
|
|
||||||
oauth_is_request(const char *path);
|
|
||||||
|
|
||||||
int
|
|
||||||
oauth_init(void);
|
|
||||||
|
|
||||||
void
|
|
||||||
oauth_deinit(void);
|
|
||||||
|
|
||||||
#endif /* !__HTTPD_OAUTH_H__ */
|
|
@ -32,12 +32,11 @@
|
|||||||
|
|
||||||
#include "mxml-compat.h"
|
#include "mxml-compat.h"
|
||||||
|
|
||||||
#include "httpd_rsp.h"
|
#include "httpd_internal.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "db.h"
|
#include "db.h"
|
||||||
#include "conffile.h"
|
#include "conffile.h"
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
#include "httpd.h"
|
|
||||||
#include "transcode.h"
|
#include "transcode.h"
|
||||||
#include "parsers/rsp_parser.h"
|
#include "parsers/rsp_parser.h"
|
||||||
|
|
||||||
@ -863,57 +862,46 @@ static struct httpd_uri_map rsp_handlers[] =
|
|||||||
|
|
||||||
/* -------------------------------- RSP API --------------------------------- */
|
/* -------------------------------- RSP API --------------------------------- */
|
||||||
|
|
||||||
void
|
static void
|
||||||
rsp_request(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed)
|
rsp_request(struct httpd_request *hreq)
|
||||||
{
|
{
|
||||||
struct httpd_request *hreq;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
DPRINTF(E_DBG, L_RSP, "RSP request: '%s'\n", uri_parsed->uri);
|
DPRINTF(E_DBG, L_RSP, "RSP request: '%s'\n", hreq->uri);
|
||||||
|
|
||||||
hreq = httpd_request_parse(req, uri_parsed, NULL, rsp_handlers);
|
if (!hreq->handler)
|
||||||
if (!hreq)
|
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_RSP, "Unrecognized path '%s' in RSP request: '%s'\n", uri_parsed->path, uri_parsed->uri);
|
DPRINTF(E_LOG, L_RSP, "Unrecognized path in RSP request: '%s'\n", hreq->uri);
|
||||||
|
|
||||||
rsp_send_error(req, "Server error");
|
rsp_send_error(hreq->req, "Server error");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = rsp_request_authorize(hreq);
|
ret = rsp_request_authorize(hreq);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
rsp_send_error(req, "Access denied");
|
rsp_send_error(hreq->req, "Access denied");
|
||||||
free(hreq);
|
free(hreq);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
hreq->handler(hreq);
|
hreq->handler(hreq);
|
||||||
|
|
||||||
free(hreq);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int
|
||||||
rsp_is_request(const char *path)
|
|
||||||
{
|
|
||||||
if (strncmp(path, "/rsp/", strlen("/rsp/")) == 0)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
rsp_init(void)
|
rsp_init(void)
|
||||||
{
|
{
|
||||||
snprintf(rsp_filter_files, sizeof(rsp_filter_files), "f.data_kind = %d", DATA_KIND_FILE);
|
snprintf(rsp_filter_files, sizeof(rsp_filter_files), "f.data_kind = %d", DATA_KIND_FILE);
|
||||||
|
|
||||||
CHECK_ERR(L_RSP, httpd_handlers_set(rsp_handlers));
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
struct httpd_module httpd_rsp =
|
||||||
rsp_deinit(void)
|
|
||||||
{
|
{
|
||||||
httpd_handlers_unset(rsp_handlers);
|
.name = "RSP",
|
||||||
}
|
.type = MODULE_RSP,
|
||||||
|
.subpaths = { "/rsp/", NULL },
|
||||||
|
.handlers = rsp_handlers,
|
||||||
|
.init = rsp_init,
|
||||||
|
.request = rsp_request,
|
||||||
|
};
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
|
|
||||||
#ifndef __HTTPD_RSP_H__
|
|
||||||
#define __HTTPD_RSP_H__
|
|
||||||
|
|
||||||
#include "httpd.h"
|
|
||||||
|
|
||||||
int
|
|
||||||
rsp_init(void);
|
|
||||||
|
|
||||||
void
|
|
||||||
rsp_deinit(void);
|
|
||||||
|
|
||||||
void
|
|
||||||
rsp_request(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed);
|
|
||||||
|
|
||||||
int
|
|
||||||
rsp_is_request(const char *path);
|
|
||||||
|
|
||||||
#endif /* !__HTTPD_RSP_H__ */
|
|
@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
#include <event2/event.h>
|
#include <event2/event.h>
|
||||||
|
|
||||||
|
#include "httpd_internal.h"
|
||||||
#include "httpd_streaming.h"
|
#include "httpd_streaming.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "conffile.h"
|
#include "conffile.h"
|
||||||
@ -523,8 +524,27 @@ streaming_write(struct output_buffer *obuf)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
// Since streaming is a one-trick pony it doesn't need handlers
|
||||||
streaming_request(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed)
|
static int
|
||||||
|
streaming_dummy_handler(struct httpd_request *hreq)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct httpd_uri_map streaming_handlers[] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
.regexp = "^/stream.mp3$",
|
||||||
|
.handler = streaming_dummy_handler
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.regexp = NULL,
|
||||||
|
.handler = NULL
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
streaming_request(struct httpd_request *hreq)
|
||||||
{
|
{
|
||||||
struct streaming_session *session;
|
struct streaming_session *session;
|
||||||
struct evhttp_connection *evcon;
|
struct evhttp_connection *evcon;
|
||||||
@ -541,13 +561,13 @@ streaming_request(struct evhttp_request *req, struct httpd_uri_parsed *uri_parse
|
|||||||
{
|
{
|
||||||
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(req, HTTP_NOTFOUND, "Not Found");
|
evhttp_send_error(hreq->req, HTTP_NOTFOUND, "Not Found");
|
||||||
return -1;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
evcon = evhttp_request_get_connection(req);
|
evcon = evhttp_request_get_connection(hreq->req);
|
||||||
httpd_peer_get(&address, &port, evcon);
|
evhttp_connection_get_peer(evcon, &address, &port);
|
||||||
param = evhttp_find_header( evhttp_request_get_input_headers(req), "Icy-MetaData");
|
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;
|
||||||
|
|
||||||
@ -556,7 +576,7 @@ streaming_request(struct evhttp_request *req, struct httpd_uri_parsed *uri_parse
|
|||||||
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(req);
|
output_headers = evhttp_request_get_output_headers(hreq->req);
|
||||||
evhttp_add_header(output_headers, "Content-Type", "audio/mpeg");
|
evhttp_add_header(output_headers, "Content-Type", "audio/mpeg");
|
||||||
evhttp_add_header(output_headers, "Server", PACKAGE_NAME "/" VERSION);
|
evhttp_add_header(output_headers, "Server", PACKAGE_NAME "/" VERSION);
|
||||||
evhttp_add_header(output_headers, "Cache-Control", "no-cache");
|
evhttp_add_header(output_headers, "Cache-Control", "no-cache");
|
||||||
@ -572,15 +592,15 @@ streaming_request(struct evhttp_request *req, struct httpd_uri_parsed *uri_parse
|
|||||||
evhttp_add_header(output_headers, "Access-Control-Allow-Origin", "*");
|
evhttp_add_header(output_headers, "Access-Control-Allow-Origin", "*");
|
||||||
evhttp_add_header(output_headers, "Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
evhttp_add_header(output_headers, "Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
||||||
|
|
||||||
evhttp_send_reply_start(req, HTTP_OK, "OK");
|
evhttp_send_reply_start(hreq->req, 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(req, HTTP_SERVUNAVAIL, "Internal Server Error");
|
evhttp_send_error(hreq->req, HTTP_SERVUNAVAIL, "Internal Server Error");
|
||||||
return -1;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_lock(&streaming_sessions_lck);
|
pthread_mutex_lock(&streaming_sessions_lck);
|
||||||
@ -591,7 +611,7 @@ streaming_request(struct evhttp_request *req, struct httpd_uri_parsed *uri_parse
|
|||||||
event_add(metaev, NULL);
|
event_add(metaev, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
session->req = req;
|
session->req = hreq->req;
|
||||||
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;
|
||||||
@ -600,23 +620,9 @@ streaming_request(struct evhttp_request *req, struct httpd_uri_parsed *uri_parse
|
|||||||
pthread_mutex_unlock(&streaming_sessions_lck);
|
pthread_mutex_unlock(&streaming_sessions_lck);
|
||||||
|
|
||||||
evhttp_connection_set_closecb(evcon, streaming_close_cb, session);
|
evhttp_connection_set_closecb(evcon, streaming_close_cb, session);
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int
|
||||||
streaming_is_request(const char *path)
|
|
||||||
{
|
|
||||||
char *ptr;
|
|
||||||
|
|
||||||
ptr = strrchr(path, '/');
|
|
||||||
if (ptr && (strcasecmp(ptr, "/stream.mp3") == 0))
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
streaming_init(void)
|
streaming_init(void)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
@ -723,7 +729,7 @@ streaming_init(void)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void
|
||||||
streaming_deinit(void)
|
streaming_deinit(void)
|
||||||
{
|
{
|
||||||
streaming_end();
|
streaming_end();
|
||||||
@ -744,3 +750,14 @@ streaming_deinit(void)
|
|||||||
|
|
||||||
pthread_mutex_destroy(&streaming_sessions_lck);
|
pthread_mutex_destroy(&streaming_sessions_lck);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct httpd_module httpd_streaming =
|
||||||
|
{
|
||||||
|
.name = "Streaming",
|
||||||
|
.type = MODULE_STREAMING,
|
||||||
|
.fullpaths = { "/stream.mp3", NULL },
|
||||||
|
.handlers = streaming_handlers,
|
||||||
|
.init = streaming_init,
|
||||||
|
.deinit = streaming_deinit,
|
||||||
|
.request = streaming_request,
|
||||||
|
};
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
#ifndef __HTTPD_STREAMING_H__
|
#ifndef __HTTPD_STREAMING_H__
|
||||||
#define __HTTPD_STREAMING_H__
|
#define __HTTPD_STREAMING_H__
|
||||||
|
|
||||||
#include "httpd.h"
|
|
||||||
#include "outputs.h"
|
#include "outputs.h"
|
||||||
|
|
||||||
/* httpd_streaming takes care of incoming requests to /stream.mp3
|
/* httpd_streaming takes care of incoming requests to /stream.mp3
|
||||||
@ -14,16 +13,4 @@
|
|||||||
void
|
void
|
||||||
streaming_write(struct output_buffer *obuf);
|
streaming_write(struct output_buffer *obuf);
|
||||||
|
|
||||||
int
|
|
||||||
streaming_request(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed);
|
|
||||||
|
|
||||||
int
|
|
||||||
streaming_is_request(const char *path);
|
|
||||||
|
|
||||||
int
|
|
||||||
streaming_init(void);
|
|
||||||
|
|
||||||
void
|
|
||||||
streaming_deinit(void);
|
|
||||||
|
|
||||||
#endif /* !__HTTPD_STREAMING_H__ */
|
#endif /* !__HTTPD_STREAMING_H__ */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user