mirror of
https://github.com/owntone/owntone-server.git
synced 2025-02-09 12:48:09 -05:00
[httpd] More abstraction of evhttp from httpd.c and modules
This commit is contained in:
parent
9d290f3870
commit
573a628b37
321
src/httpd.c
321
src/httpd.c
@ -41,7 +41,6 @@
|
||||
#endif
|
||||
#include <event2/event.h>
|
||||
#include <event2/http.h>
|
||||
#include <event2/http_struct.h>
|
||||
|
||||
#include <regex.h>
|
||||
#include <zlib.h>
|
||||
@ -137,10 +136,10 @@ static int exit_pipe[2];
|
||||
#endif
|
||||
static int httpd_exit;
|
||||
static struct event *exitev;
|
||||
static struct evhttp *evhttpd;
|
||||
static httpd_server *httpd_serv;
|
||||
static pthread_t tid_httpd;
|
||||
|
||||
static const char *allow_origin;
|
||||
static const char *httpd_allow_origin;
|
||||
static int httpd_port;
|
||||
|
||||
|
||||
@ -291,61 +290,76 @@ modules_search(const char *path)
|
||||
|
||||
/* --------------------------- REQUEST HELPERS ------------------------------ */
|
||||
|
||||
static int
|
||||
request_set(struct httpd_request *hreq, struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed, const char *user_agent, struct httpd_uri_map *uri_map)
|
||||
static void
|
||||
request_unset(struct httpd_request *hreq)
|
||||
{
|
||||
struct httpd_uri_map *uri;
|
||||
int req_method;
|
||||
httpd_uri_free(hreq->uri_parsed);
|
||||
}
|
||||
|
||||
static void
|
||||
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_module *module;
|
||||
int ret;
|
||||
|
||||
memset(hreq, 0, sizeof(struct httpd_request));
|
||||
|
||||
// Note req is allowed to be NULL
|
||||
hreq->backend = req;
|
||||
hreq->uri = uri_parsed->uri;
|
||||
hreq->uri_parsed = uri_parsed;
|
||||
hreq->query = &(uri_parsed->query);
|
||||
req_method = 0;
|
||||
|
||||
if (req)
|
||||
// Populate hreq by getting values from the backend (or from the caller)
|
||||
hreq->backend = backend;
|
||||
if (backend)
|
||||
{
|
||||
hreq->in_body = evhttp_request_get_input_buffer(req);
|
||||
hreq->in_headers = evhttp_request_get_input_headers(req);
|
||||
hreq->user_agent = evhttp_find_header(hreq->in_headers, "User-Agent");
|
||||
httpd_request_peer_get(&hreq->peer_address, &hreq->peer_port, hreq);
|
||||
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->out_headers = httpd_backend_output_headers_get(backend);
|
||||
httpd_backend_method_get(&hreq->method, backend);
|
||||
httpd_backend_peer_get(&hreq->peer_address, &hreq->peer_port, backend);
|
||||
|
||||
req_method = evhttp_request_get_command(req);
|
||||
hreq->user_agent = httpd_header_find(hreq->in_headers, "User-Agent");
|
||||
}
|
||||
else
|
||||
{
|
||||
hreq->uri = uri;
|
||||
hreq->user_agent = user_agent;
|
||||
}
|
||||
|
||||
if (user_agent)
|
||||
hreq->user_agent = user_agent;
|
||||
uri_parsed = httpd_uri_parse(hreq->uri);
|
||||
if (!uri_parsed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Find a handler for the path
|
||||
for (uri = uri_map; uri->handler; uri++)
|
||||
hreq->uri_parsed = uri_parsed;
|
||||
hreq->query = &(hreq->uri_parsed->query);
|
||||
|
||||
// Path with e.g. /api -> JSON module
|
||||
module = modules_search(uri_parsed->path);
|
||||
if (!module)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (map = module->handlers; map->handler; map++)
|
||||
{
|
||||
// Check if handler supports the current http request method
|
||||
if (uri->method && req_method && !(req_method & uri->method))
|
||||
if (map->method && hreq->method && !(map->method & hreq->method))
|
||||
continue;
|
||||
|
||||
ret = regexec(uri->preg, uri_parsed->path, 0, NULL, 0);
|
||||
ret = regexec(map->preg, uri_parsed->path, 0, NULL, 0);
|
||||
if (ret != 0)
|
||||
continue;
|
||||
|
||||
hreq->handler = uri->handler;
|
||||
return 0; // Success
|
||||
hreq->handler = map->handler;
|
||||
break;
|
||||
}
|
||||
|
||||
// Handler not found, that's an error
|
||||
return -1;
|
||||
}
|
||||
|
||||
void
|
||||
httpd_redirect_to(struct httpd_request *hreq, const char *path)
|
||||
{
|
||||
httpd_headers *headers;
|
||||
|
||||
headers = httpd_request_output_headers_get(hreq);
|
||||
httpd_header_add(headers, "Location", path);
|
||||
httpd_header_add(hreq->out_headers, "Location", path);
|
||||
|
||||
httpd_send_reply(hreq, HTTP_MOVETEMP, "Moved", NULL, HTTPD_SEND_NO_GZIP);
|
||||
}
|
||||
@ -364,7 +378,6 @@ httpd_redirect_to(struct httpd_request *hreq, const char *path)
|
||||
bool
|
||||
httpd_request_etag_matches(struct httpd_request *hreq, const char *etag)
|
||||
{
|
||||
httpd_headers *output_headers;
|
||||
const char *none_match;
|
||||
|
||||
none_match = httpd_header_find(hreq->in_headers, "If-None-Match");
|
||||
@ -374,9 +387,8 @@ httpd_request_etag_matches(struct httpd_request *hreq, const char *etag)
|
||||
return true;
|
||||
|
||||
// Add cache headers to allow client side caching
|
||||
output_headers = httpd_request_output_headers_get(hreq);
|
||||
httpd_header_add(output_headers, "Cache-Control", "private,no-cache,max-age=0");
|
||||
httpd_header_add(output_headers, "ETag", etag);
|
||||
httpd_header_add(hreq->out_headers, "Cache-Control", "private,no-cache,max-age=0");
|
||||
httpd_header_add(hreq->out_headers, "ETag", etag);
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -395,12 +407,11 @@ httpd_request_etag_matches(struct httpd_request *hreq, const char *etag)
|
||||
bool
|
||||
httpd_request_not_modified_since(struct httpd_request *hreq, time_t mtime)
|
||||
{
|
||||
httpd_headers *output_headers;
|
||||
char last_modified[1000];
|
||||
const char *modified_since;
|
||||
struct tm timebuf;
|
||||
|
||||
modified_since = evhttp_find_header(hreq->in_headers, "If-Modified-Since");
|
||||
modified_since = httpd_header_find(hreq->in_headers, "If-Modified-Since");
|
||||
|
||||
strftime(last_modified, sizeof(last_modified), "%a, %d %b %Y %H:%M:%S %Z", gmtime_r(&mtime, &timebuf));
|
||||
|
||||
@ -409,9 +420,8 @@ httpd_request_not_modified_since(struct httpd_request *hreq, time_t mtime)
|
||||
return true;
|
||||
|
||||
// Add cache headers to allow client side caching
|
||||
output_headers = httpd_request_output_headers_get(hreq);
|
||||
httpd_header_add(output_headers, "Cache-Control", "private,no-cache,max-age=0");
|
||||
httpd_header_add(output_headers, "Last-Modified", last_modified);
|
||||
httpd_header_add(hreq->out_headers, "Cache-Control", "private,no-cache,max-age=0");
|
||||
httpd_header_add(hreq->out_headers, "Last-Modified", last_modified);
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -419,28 +429,23 @@ httpd_request_not_modified_since(struct httpd_request *hreq, time_t mtime)
|
||||
void
|
||||
httpd_response_not_cachable(struct httpd_request *hreq)
|
||||
{
|
||||
httpd_headers *output_headers;
|
||||
|
||||
output_headers = httpd_request_output_headers_get(hreq);
|
||||
|
||||
// Remove potentially set cache control headers
|
||||
httpd_header_remove(output_headers, "Cache-Control");
|
||||
httpd_header_remove(output_headers, "Last-Modified");
|
||||
httpd_header_remove(output_headers, "ETag");
|
||||
httpd_header_remove(hreq->out_headers, "Cache-Control");
|
||||
httpd_header_remove(hreq->out_headers, "Last-Modified");
|
||||
httpd_header_remove(hreq->out_headers, "ETag");
|
||||
|
||||
// Tell clients that they are not allowed to cache this response
|
||||
httpd_header_add(output_headers, "Cache-Control", "no-store");
|
||||
httpd_header_add(hreq->out_headers, "Cache-Control", "no-store");
|
||||
}
|
||||
|
||||
static void
|
||||
serve_file(struct httpd_request *hreq, const char *uri)
|
||||
serve_file(struct httpd_request *hreq)
|
||||
{
|
||||
char *ext;
|
||||
char path[PATH_MAX];
|
||||
char deref[PATH_MAX];
|
||||
char *ctype;
|
||||
struct evbuffer *evbuf;
|
||||
httpd_headers *output_headers;
|
||||
struct stat sb;
|
||||
int fd;
|
||||
int i;
|
||||
@ -452,13 +457,12 @@ serve_file(struct httpd_request *hreq, const char *uri)
|
||||
if (!httpd_admin_check_auth(hreq))
|
||||
return;
|
||||
|
||||
ret = snprintf(path, sizeof(path), "%s%s", webroot_directory, uri);
|
||||
ret = snprintf(path, sizeof(path), "%s%s", webroot_directory, hreq->uri_parsed->path);
|
||||
if ((ret < 0) || (ret >= sizeof(path)))
|
||||
{
|
||||
DPRINTF(E_LOG, L_HTTPD, "Request exceeds PATH_MAX: %s\n", uri);
|
||||
DPRINTF(E_LOG, L_HTTPD, "Request exceeds PATH_MAX: %s\n", hreq->uri);
|
||||
|
||||
httpd_send_error(hreq, HTTP_NOTFOUND, "Not Found");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -475,7 +479,6 @@ serve_file(struct httpd_request *hreq, const char *uri)
|
||||
DPRINTF(E_LOG, L_HTTPD, "Dereferenced path exceeds PATH_MAX: %s\n", path);
|
||||
|
||||
httpd_send_error(hreq, HTTP_NOTFOUND, "Not Found");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -485,7 +488,6 @@ serve_file(struct httpd_request *hreq, const char *uri)
|
||||
DPRINTF(E_WARN, L_HTTPD, "Could not lstat() %s: %s\n", deref, strerror(errno));
|
||||
|
||||
httpd_send_error(hreq, HTTP_NOTFOUND, "Not Found");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -584,8 +586,7 @@ serve_file(struct httpd_request *hreq, const char *uri)
|
||||
}
|
||||
}
|
||||
|
||||
output_headers = httpd_request_output_headers_get(hreq);
|
||||
httpd_header_add(output_headers, "Content-Type", ctype);
|
||||
httpd_header_add(hreq->out_headers, "Content-Type", ctype);
|
||||
|
||||
httpd_send_reply(hreq, HTTP_OK, "OK", evbuf, HTTPD_SEND_NO_GZIP);
|
||||
|
||||
@ -817,74 +818,69 @@ exit_cb(int fd, short event, void *arg)
|
||||
httpd_exit = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
httpd_gen_cb(struct evhttp_request *req, void *arg)
|
||||
static int
|
||||
handle_cors_preflight(struct httpd_request *hreq, const char *allow_origin)
|
||||
{
|
||||
struct evkeyvalq *input_headers;
|
||||
struct evkeyvalq *output_headers;
|
||||
struct httpd_request hreq = { .backend = req }; // TODO clean this up
|
||||
struct httpd_uri_parsed *parsed;
|
||||
struct httpd_module *m;
|
||||
const char *uri;
|
||||
bool is_cors_preflight;
|
||||
|
||||
// Clear the proxy request flag set by evhttp if the request URI was absolute.
|
||||
// It has side-effects on Connection: keep-alive
|
||||
req->flags &= ~EVHTTP_PROXY_REQUEST;
|
||||
is_cors_preflight = ( hreq->method == HTTPD_METHOD_OPTIONS && hreq->in_headers && allow_origin &&
|
||||
httpd_header_find(hreq->in_headers, "Origin") &&
|
||||
httpd_header_find(hreq->in_headers, "Access-Control-Request-Method") );
|
||||
if (!is_cors_preflight)
|
||||
return -1;
|
||||
|
||||
httpd_header_add(hreq->out_headers, "Access-Control-Allow-Origin", allow_origin);
|
||||
httpd_header_add(hreq->out_headers, "Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
||||
httpd_header_add(hreq->out_headers, "Access-Control-Allow-Headers", "authorization");
|
||||
|
||||
// In this case there is no reason to go through httpd_send_reply
|
||||
httpd_reply_backend_send(hreq, HTTP_OK, "OK", NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
httpd_gen_cb(httpd_backend *backend, void *arg)
|
||||
{
|
||||
struct httpd_request hreq;
|
||||
|
||||
// This is to make modifications to e.g. evhttps's request object
|
||||
httpd_backend_preprocess(backend);
|
||||
|
||||
// Populates the hreq struct
|
||||
request_set(&hreq, backend, NULL, NULL);
|
||||
|
||||
// Did we get a CORS preflight request?
|
||||
input_headers = evhttp_request_get_input_headers(req);
|
||||
if ( input_headers && allow_origin &&
|
||||
(evhttp_request_get_command(req) == EVHTTP_REQ_OPTIONS) &&
|
||||
evhttp_find_header(input_headers, "Origin") &&
|
||||
evhttp_find_header(input_headers, "Access-Control-Request-Method") )
|
||||
if (handle_cors_preflight(&hreq, httpd_allow_origin) == 0)
|
||||
{
|
||||
output_headers = evhttp_request_get_output_headers(req);
|
||||
|
||||
httpd_header_add(output_headers, "Access-Control-Allow-Origin", allow_origin);
|
||||
httpd_header_add(output_headers, "Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
||||
httpd_header_add(output_headers, "Access-Control-Allow-Headers", "authorization");
|
||||
|
||||
// In this case there is no reason to go through httpd_send_reply
|
||||
evhttp_send_reply(req, HTTP_OK, "OK", NULL);
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
uri = evhttp_request_get_uri(req);
|
||||
if (!uri)
|
||||
if (!(&hreq)->uri || !(&hreq)->uri_parsed)
|
||||
{
|
||||
DPRINTF(E_WARN, L_HTTPD, "No URI in request\n");
|
||||
DPRINTF(E_WARN, L_HTTPD, "Invalid URI in request: '%s'\n", (&hreq)->uri);
|
||||
httpd_redirect_to(&hreq, "/");
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
parsed = httpd_uri_parse(uri);
|
||||
if (!parsed || !parsed->path)
|
||||
else if (!(&hreq)->uri_parsed->path)
|
||||
{
|
||||
DPRINTF(E_WARN, L_HTTPD, "Invalid path in request: '%s'\n", (&hreq)->uri);
|
||||
httpd_redirect_to(&hreq, "/");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (strcmp(parsed->path, "/") == 0)
|
||||
if ((&hreq)->handler)
|
||||
{
|
||||
goto serve_file;
|
||||
(&hreq)->handler(&hreq);
|
||||
}
|
||||
|
||||
m = modules_search(parsed->path);
|
||||
if (m)
|
||||
else
|
||||
{
|
||||
request_set(&hreq, req, parsed, NULL, m->handlers);
|
||||
m->request(&hreq);
|
||||
goto out;
|
||||
// Serve web interface files
|
||||
DPRINTF(E_DBG, L_HTTPD, "HTTP request: '%s'\n", (&hreq)->uri);
|
||||
serve_file(&hreq);
|
||||
}
|
||||
|
||||
DPRINTF(E_DBG, L_HTTPD, "HTTP request: '%s'\n", parsed->uri);
|
||||
|
||||
/* Serve web interface files */
|
||||
serve_file:
|
||||
serve_file(&hreq, parsed->path);
|
||||
|
||||
out:
|
||||
httpd_uri_free(parsed);
|
||||
request_unset(&hreq);
|
||||
}
|
||||
|
||||
|
||||
@ -991,10 +987,16 @@ httpd_uri_parse(const char *uri)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
httpd_request_set(struct httpd_request *hreq, struct httpd_uri_parsed *uri_parsed, const char *user_agent, struct httpd_uri_map *uri_map)
|
||||
void
|
||||
httpd_request_unset(struct httpd_request *hreq)
|
||||
{
|
||||
return request_set(hreq, NULL, uri_parsed, user_agent, uri_map);
|
||||
request_unset(hreq);
|
||||
}
|
||||
|
||||
void
|
||||
httpd_request_set(struct httpd_request *hreq, const char *uri, const char *user_agent)
|
||||
{
|
||||
request_set(hreq, NULL, uri, user_agent);
|
||||
}
|
||||
|
||||
/* Thread: httpd */
|
||||
@ -1007,7 +1009,6 @@ httpd_stream_file(struct httpd_request *hreq, int id)
|
||||
void (*stream_cb)(int fd, short event, void *arg);
|
||||
struct stat sb;
|
||||
struct timeval tv;
|
||||
httpd_headers *output_headers;
|
||||
const char *param;
|
||||
const char *param_end;
|
||||
const char *client_codecs;
|
||||
@ -1087,8 +1088,6 @@ httpd_stream_file(struct httpd_request *hreq, int id)
|
||||
|
||||
transcode = transcode_needed(hreq->user_agent, client_codecs, mfi->codectype);
|
||||
|
||||
output_headers = httpd_request_output_headers_get(hreq);
|
||||
|
||||
if (transcode)
|
||||
{
|
||||
DPRINTF(E_INFO, L_HTTPD, "Preparing to transcode %s\n", mfi->path);
|
||||
@ -1104,8 +1103,8 @@ httpd_stream_file(struct httpd_request *hreq, int id)
|
||||
goto out_free_st;
|
||||
}
|
||||
|
||||
if (!evhttp_find_header(output_headers, "Content-Type"))
|
||||
httpd_header_add(output_headers, "Content-Type", "audio/wav");
|
||||
if (!httpd_header_find(hreq->out_headers, "Content-Type"))
|
||||
httpd_header_add(hreq->out_headers, "Content-Type", "audio/wav");
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1165,21 +1164,21 @@ httpd_stream_file(struct httpd_request *hreq, int id)
|
||||
DPRINTF(E_LOG, L_HTTPD, "Content-Type too large for buffer, dropping\n");
|
||||
else
|
||||
{
|
||||
httpd_header_remove(output_headers, "Content-Type");
|
||||
httpd_header_add(output_headers, "Content-Type", buf);
|
||||
httpd_header_remove(hreq->out_headers, "Content-Type");
|
||||
httpd_header_add(hreq->out_headers, "Content-Type", buf);
|
||||
}
|
||||
}
|
||||
/* If no Content-Type has been set and we're streaming audio, add a proper
|
||||
* Content-Type for the file we're streaming. Remember DAAP streams audio
|
||||
* with application/x-dmap-tagged as the Content-Type (ugh!).
|
||||
*/
|
||||
else if (!evhttp_find_header(output_headers, "Content-Type") && mfi->type)
|
||||
else if (!httpd_header_find(hreq->out_headers, "Content-Type") && mfi->type)
|
||||
{
|
||||
ret = snprintf(buf, sizeof(buf), "audio/%s", mfi->type);
|
||||
if ((ret < 0) || (ret >= sizeof(buf)))
|
||||
DPRINTF(E_LOG, L_HTTPD, "Content-Type too large for buffer, dropping\n");
|
||||
else
|
||||
httpd_header_add(output_headers, "Content-Type", buf);
|
||||
httpd_header_add(hreq->out_headers, "Content-Type", buf);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1228,7 +1227,7 @@ httpd_stream_file(struct httpd_request *hreq, int id)
|
||||
if ((ret < 0) || (ret >= sizeof(buf)))
|
||||
DPRINTF(E_LOG, L_HTTPD, "Content-Length too large for buffer, dropping\n");
|
||||
else
|
||||
httpd_header_add(output_headers, "Content-Length", buf);
|
||||
httpd_header_add(hreq->out_headers, "Content-Length", buf);
|
||||
}
|
||||
|
||||
httpd_reply_start_send(hreq, HTTP_OK, "OK");
|
||||
@ -1247,13 +1246,13 @@ httpd_stream_file(struct httpd_request *hreq, int id)
|
||||
if ((ret < 0) || (ret >= sizeof(buf)))
|
||||
DPRINTF(E_LOG, L_HTTPD, "Content-Range too large for buffer, dropping\n");
|
||||
else
|
||||
httpd_header_add(output_headers, "Content-Range", buf);
|
||||
httpd_header_add(hreq->out_headers, "Content-Range", buf);
|
||||
|
||||
ret = snprintf(buf, sizeof(buf), "%" PRIi64, ((end_offset) ? end_offset + 1 : (int64_t)st->size) - offset);
|
||||
if ((ret < 0) || (ret >= sizeof(buf)))
|
||||
DPRINTF(E_LOG, L_HTTPD, "Content-Length too large for buffer, dropping\n");
|
||||
else
|
||||
httpd_header_add(output_headers, "Content-Length", buf);
|
||||
httpd_header_add(hreq->out_headers, "Content-Length", buf);
|
||||
|
||||
httpd_reply_start_send(hreq, 206, "Partial Content");
|
||||
}
|
||||
@ -1363,29 +1362,26 @@ void
|
||||
httpd_send_reply(struct httpd_request *hreq, int code, const char *reason, struct evbuffer *evbuf, enum httpd_send_flags flags)
|
||||
{
|
||||
struct evbuffer *gzbuf;
|
||||
httpd_headers *output_headers;
|
||||
const char *param;
|
||||
int do_gzip;
|
||||
|
||||
if (!hreq->backend)
|
||||
return;
|
||||
|
||||
output_headers = httpd_request_output_headers_get(hreq);
|
||||
|
||||
do_gzip = ( (!(flags & HTTPD_SEND_NO_GZIP)) &&
|
||||
evbuf && (evbuffer_get_length(evbuf) > 512) &&
|
||||
(param = httpd_header_find(hreq->in_headers, "Accept-Encoding")) &&
|
||||
(strstr(param, "gzip") || strstr(param, "*"))
|
||||
);
|
||||
|
||||
if (allow_origin)
|
||||
httpd_header_add(output_headers, "Access-Control-Allow-Origin", allow_origin);
|
||||
if (httpd_allow_origin)
|
||||
httpd_header_add(hreq->out_headers, "Access-Control-Allow-Origin", httpd_allow_origin);
|
||||
|
||||
if (do_gzip && (gzbuf = httpd_gzip_deflate(evbuf)))
|
||||
{
|
||||
DPRINTF(E_DBG, L_HTTPD, "Gzipping response\n");
|
||||
|
||||
httpd_header_add(output_headers, "Content-Encoding", "gzip");
|
||||
httpd_header_add(hreq->out_headers, "Content-Encoding", "gzip");
|
||||
httpd_reply_backend_send(hreq, code, reason, gzbuf);
|
||||
evbuffer_free(gzbuf);
|
||||
|
||||
@ -1402,17 +1398,14 @@ httpd_send_reply(struct httpd_request *hreq, int code, const char *reason, struc
|
||||
void
|
||||
httpd_send_error(struct httpd_request *hreq, int error, const char *reason)
|
||||
{
|
||||
httpd_headers *output_headers;
|
||||
struct evbuffer *evbuf;
|
||||
|
||||
output_headers = httpd_request_output_headers_get(hreq);
|
||||
httpd_headers_clear(hreq->out_headers);
|
||||
|
||||
httpd_headers_clear(output_headers);
|
||||
|
||||
if (allow_origin)
|
||||
httpd_header_add(output_headers, "Access-Control-Allow-Origin", allow_origin);
|
||||
httpd_header_add(output_headers, "Content-Type", "text/html");
|
||||
httpd_header_add(output_headers, "Connection", "close");
|
||||
if (httpd_allow_origin)
|
||||
httpd_header_add(hreq->out_headers, "Access-Control-Allow-Origin", httpd_allow_origin);
|
||||
httpd_header_add(hreq->out_headers, "Content-Type", "text/html");
|
||||
httpd_header_add(hreq->out_headers, "Connection", "close");
|
||||
|
||||
evbuf = evbuffer_new();
|
||||
if (!evbuf)
|
||||
@ -1464,7 +1457,6 @@ int
|
||||
httpd_basic_auth(struct httpd_request *hreq, const char *user, const char *passwd, const char *realm)
|
||||
{
|
||||
struct evbuffer *evbuf;
|
||||
httpd_headers *output_headers;
|
||||
char header[256];
|
||||
const char *auth;
|
||||
char *authuser;
|
||||
@ -1546,8 +1538,7 @@ httpd_basic_auth(struct httpd_request *hreq, const char *user, const char *passw
|
||||
return -1;
|
||||
}
|
||||
|
||||
output_headers = httpd_request_output_headers_get(hreq);
|
||||
httpd_header_add(output_headers, "WWW-Authenticate", header);
|
||||
httpd_header_add(hreq->out_headers, "WWW-Authenticate", header);
|
||||
|
||||
evbuffer_add_printf(evbuf, ERR_PAGE, 401, "Unauthorized", "Authorization required");
|
||||
|
||||
@ -1572,19 +1563,16 @@ httpd_init(const char *webroot)
|
||||
if (ret < 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_HTTPD, "Could not stat() web root directory '%s': %s\n", webroot, strerror(errno));
|
||||
|
||||
return -1;
|
||||
}
|
||||
if (!S_ISDIR(sb.st_mode))
|
||||
{
|
||||
DPRINTF(E_LOG, L_HTTPD, "Web root directory '%s' is not a directory\n", webroot);
|
||||
|
||||
return -1;
|
||||
}
|
||||
if (!realpath(webroot, webroot_directory))
|
||||
{
|
||||
DPRINTF(E_LOG, L_HTTPD, "Web root directory '%s' could not be dereferenced: %s\n", webroot, strerror(errno));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -1592,7 +1580,6 @@ httpd_init(const char *webroot)
|
||||
if (!evbase_httpd)
|
||||
{
|
||||
DPRINTF(E_FATAL, L_HTTPD, "Could not create an event base\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -1601,7 +1588,6 @@ httpd_init(const char *webroot)
|
||||
if (ret < 0)
|
||||
{
|
||||
DPRINTF(E_FATAL, L_HTTPD, "Websocket init failed\n");
|
||||
|
||||
goto websocket_fail;
|
||||
}
|
||||
#endif
|
||||
@ -1610,7 +1596,6 @@ httpd_init(const char *webroot)
|
||||
if (ret < 0)
|
||||
{
|
||||
DPRINTF(E_FATAL, L_HTTPD, "Modules init failed\n");
|
||||
|
||||
goto modules_fail;
|
||||
}
|
||||
|
||||
@ -1619,7 +1604,6 @@ httpd_init(const char *webroot)
|
||||
if (exit_efd < 0)
|
||||
{
|
||||
DPRINTF(E_FATAL, L_HTTPD, "Could not create eventfd: %s\n", strerror(errno));
|
||||
|
||||
goto pipe_fail;
|
||||
}
|
||||
|
||||
@ -1633,7 +1617,6 @@ httpd_init(const char *webroot)
|
||||
if (ret < 0)
|
||||
{
|
||||
DPRINTF(E_FATAL, L_HTTPD, "Could not create pipe: %s\n", strerror(errno));
|
||||
|
||||
goto pipe_fail;
|
||||
}
|
||||
|
||||
@ -1642,45 +1625,28 @@ httpd_init(const char *webroot)
|
||||
if (!exitev)
|
||||
{
|
||||
DPRINTF(E_FATAL, L_HTTPD, "Could not create exit event\n");
|
||||
|
||||
goto exitev_fail;
|
||||
}
|
||||
event_add(exitev, NULL);
|
||||
|
||||
evhttpd = evhttp_new(evbase_httpd);
|
||||
if (!evhttpd)
|
||||
httpd_port = cfg_getint(cfg_getsec(cfg, "library"), "port");
|
||||
httpd_serv = httpd_server_new(evbase_httpd, httpd_port, httpd_gen_cb, NULL);
|
||||
if (!httpd_serv)
|
||||
{
|
||||
DPRINTF(E_FATAL, L_HTTPD, "Could not create HTTP server\n");
|
||||
|
||||
goto evhttpd_fail;
|
||||
DPRINTF(E_FATAL, L_HTTPD, "Could not create HTTP server on port %d (server already running?)\n", httpd_port);
|
||||
goto httpd_server_fail;
|
||||
}
|
||||
|
||||
// For CORS headers
|
||||
allow_origin = cfg_getstr(cfg_getsec(cfg, "general"), "allow_origin");
|
||||
if (allow_origin)
|
||||
{
|
||||
if (strlen(allow_origin) != 0)
|
||||
evhttp_set_allowed_methods(evhttpd, EVHTTP_REQ_GET | EVHTTP_REQ_POST | EVHTTP_REQ_PUT | EVHTTP_REQ_DELETE | EVHTTP_REQ_HEAD | EVHTTP_REQ_OPTIONS);
|
||||
else
|
||||
allow_origin = NULL;
|
||||
}
|
||||
|
||||
httpd_port = cfg_getint(cfg_getsec(cfg, "library"), "port");
|
||||
|
||||
ret = net_evhttp_bind(evhttpd, httpd_port, "httpd");
|
||||
if (ret < 0)
|
||||
{
|
||||
DPRINTF(E_FATAL, L_HTTPD, "Could not bind to port %d (server already running?)\n", httpd_port);
|
||||
goto bind_fail;
|
||||
}
|
||||
|
||||
evhttp_set_gencb(evhttpd, httpd_gen_cb, NULL);
|
||||
httpd_allow_origin = cfg_getstr(cfg_getsec(cfg, "general"), "allow_origin");
|
||||
if (strlen(httpd_allow_origin) == 0)
|
||||
httpd_allow_origin = NULL;
|
||||
httpd_server_allow_origin_set(httpd_serv, httpd_allow_origin);
|
||||
|
||||
ret = pthread_create(&tid_httpd, NULL, httpd, NULL);
|
||||
if (ret != 0)
|
||||
{
|
||||
DPRINTF(E_FATAL, L_HTTPD, "Could not spawn HTTPd thread: %s\n", strerror(errno));
|
||||
|
||||
goto thread_fail;
|
||||
}
|
||||
|
||||
@ -1689,9 +1655,8 @@ httpd_init(const char *webroot)
|
||||
return 0;
|
||||
|
||||
thread_fail:
|
||||
bind_fail:
|
||||
evhttp_free(evhttpd);
|
||||
evhttpd_fail:
|
||||
httpd_server_free(httpd_serv);
|
||||
httpd_server_fail:
|
||||
event_free(exitev);
|
||||
exitev_fail:
|
||||
#ifdef HAVE_EVENTFD
|
||||
@ -1759,6 +1724,6 @@ httpd_deinit(void)
|
||||
close(exit_pipe[1]);
|
||||
#endif
|
||||
event_free(exitev);
|
||||
evhttp_free(evhttpd);
|
||||
httpd_server_free(httpd_serv);
|
||||
event_base_free(evbase_httpd);
|
||||
}
|
||||
|
@ -62,14 +62,10 @@ request_process(struct httpd_request *hreq, uint32_t *max_w, uint32_t *max_h)
|
||||
static int
|
||||
response_process(struct httpd_request *hreq, int format)
|
||||
{
|
||||
httpd_headers *headers;
|
||||
|
||||
headers = httpd_request_output_headers_get(hreq);
|
||||
|
||||
if (format == ART_FMT_PNG)
|
||||
httpd_header_add(headers, "Content-Type", "image/png");
|
||||
httpd_header_add(hreq->out_headers, "Content-Type", "image/png");
|
||||
else if (format == ART_FMT_JPEG)
|
||||
httpd_header_add(headers, "Content-Type", "image/jpeg");
|
||||
httpd_header_add(hreq->out_headers, "Content-Type", "image/jpeg");
|
||||
else
|
||||
return HTTP_NOCONTENT;
|
||||
|
||||
|
@ -1935,7 +1935,6 @@ daap_reply_browse(struct httpd_request *hreq)
|
||||
static enum daap_reply_result
|
||||
daap_reply_extra_data(struct httpd_request *hreq)
|
||||
{
|
||||
httpd_headers *headers;
|
||||
char clen[32];
|
||||
const char *param;
|
||||
char *ctype;
|
||||
@ -2008,11 +2007,10 @@ daap_reply_extra_data(struct httpd_request *hreq)
|
||||
goto no_artwork;
|
||||
}
|
||||
|
||||
headers = httpd_request_output_headers_get(hreq);
|
||||
httpd_header_remove(headers, "Content-Type");
|
||||
httpd_header_add(headers, "Content-Type", ctype);
|
||||
httpd_header_remove(hreq->out_headers, "Content-Type");
|
||||
httpd_header_add(hreq->out_headers, "Content-Type", ctype);
|
||||
snprintf(clen, sizeof(clen), "%ld", (long)len);
|
||||
httpd_header_add(headers, "Content-Length", clen);
|
||||
httpd_header_add(hreq->out_headers, "Content-Length", clen);
|
||||
|
||||
return DAAP_REPLY_OK_NO_GZIP;
|
||||
|
||||
@ -2216,7 +2214,6 @@ static struct httpd_uri_map daap_handlers[] =
|
||||
static void
|
||||
daap_request(struct httpd_request *hreq)
|
||||
{
|
||||
httpd_headers *headers;
|
||||
struct timespec start;
|
||||
struct timespec end;
|
||||
struct daap_session session;
|
||||
@ -2261,13 +2258,12 @@ daap_request(struct httpd_request *hreq)
|
||||
}
|
||||
|
||||
// Set reply headers
|
||||
headers = httpd_request_output_headers_get(hreq);
|
||||
httpd_header_add(headers, "Accept-Ranges", "bytes");
|
||||
httpd_header_add(headers, "DAAP-Server", PACKAGE_NAME "/" VERSION);
|
||||
httpd_header_add(hreq->out_headers, "Accept-Ranges", "bytes");
|
||||
httpd_header_add(hreq->out_headers, "DAAP-Server", PACKAGE_NAME "/" VERSION);
|
||||
// Content-Type for all replies, even the actual audio streaming. Note that
|
||||
// video streaming will override this Content-Type with a more appropriate
|
||||
// video/<type> Content-Type as expected by clients like Front Row.
|
||||
httpd_header_add(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());
|
||||
@ -2277,7 +2273,7 @@ daap_request(struct httpd_request *hreq)
|
||||
if (ret == 0)
|
||||
{
|
||||
// The cache will return the data gzipped, so httpd_send_reply won't need to do it
|
||||
httpd_header_add(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
|
||||
|
||||
evbuffer_free(hreq->reply);
|
||||
@ -2320,7 +2316,6 @@ struct evbuffer *
|
||||
daap_reply_build(const char *uri, const char *user_agent, int is_remote)
|
||||
{
|
||||
struct httpd_request hreq;
|
||||
struct httpd_uri_parsed *uri_parsed;
|
||||
struct evbuffer *reply;
|
||||
struct daap_session session;
|
||||
int ret;
|
||||
@ -2329,14 +2324,10 @@ daap_reply_build(const char *uri, const char *user_agent, int is_remote)
|
||||
|
||||
reply = NULL;
|
||||
|
||||
uri_parsed = httpd_uri_parse(uri);
|
||||
if (!uri_parsed)
|
||||
return NULL;
|
||||
|
||||
ret = httpd_request_set(&hreq, uri_parsed, user_agent, daap_handlers);
|
||||
if (ret < 0)
|
||||
httpd_request_set(&hreq, uri, user_agent);
|
||||
if (!(&hreq)->handler)
|
||||
{
|
||||
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 in request: '%s'\n", uri);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -2357,7 +2348,7 @@ daap_reply_build(const char *uri, const char *user_agent, int is_remote)
|
||||
reply = hreq.reply;
|
||||
|
||||
out:
|
||||
httpd_uri_free(uri_parsed);
|
||||
httpd_request_unset(&hreq);
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
@ -2316,7 +2316,6 @@ static int
|
||||
dacp_reply_nowplayingartwork(struct httpd_request *hreq)
|
||||
{
|
||||
char clen[32];
|
||||
httpd_headers *headers;
|
||||
const char *param;
|
||||
char *ctype;
|
||||
size_t len;
|
||||
@ -2381,11 +2380,10 @@ dacp_reply_nowplayingartwork(struct httpd_request *hreq)
|
||||
goto no_artwork;
|
||||
}
|
||||
|
||||
headers = httpd_request_output_headers_get(hreq);
|
||||
httpd_header_remove(headers, "Content-Type");
|
||||
httpd_header_add(headers, "Content-Type", ctype);
|
||||
httpd_header_remove(hreq->out_headers, "Content-Type");
|
||||
httpd_header_add(hreq->out_headers, "Content-Type", ctype);
|
||||
snprintf(clen, sizeof(clen), "%ld", (long)len);
|
||||
httpd_header_add(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);
|
||||
return 0;
|
||||
@ -2855,8 +2853,6 @@ static struct httpd_uri_map dacp_handlers[] =
|
||||
static void
|
||||
dacp_request(struct httpd_request *hreq)
|
||||
{
|
||||
httpd_headers *headers;
|
||||
|
||||
DPRINTF(E_DBG, L_DACP, "DACP request: '%s'\n", hreq->uri);
|
||||
|
||||
if (!hreq->handler)
|
||||
@ -2867,10 +2863,9 @@ dacp_request(struct httpd_request *hreq)
|
||||
return;
|
||||
}
|
||||
|
||||
headers = httpd_request_output_headers_get(hreq);
|
||||
httpd_header_add(headers, "DAAP-Server", PACKAGE_NAME "/" VERSION);
|
||||
httpd_header_add(hreq->out_headers, "DAAP-Server", PACKAGE_NAME "/" VERSION);
|
||||
/* Content-Type for all DACP replies; can be overriden as needed */
|
||||
httpd_header_add(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());
|
||||
|
||||
|
@ -4,17 +4,16 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
#include <event2/event.h>
|
||||
#include <event2/http.h>
|
||||
#include <event2/keyvalq_struct.h>
|
||||
|
||||
typedef struct evhttp httpd_server;
|
||||
typedef struct evhttp_connection httpd_connection;
|
||||
typedef struct evhttp_request httpd_backend;
|
||||
typedef struct evkeyvalq httpd_headers;
|
||||
typedef struct evkeyvalq httpd_query;
|
||||
|
||||
typedef void (*httpd_connection_closecb)(httpd_connection *conn, void *arg);
|
||||
typedef void (*httpd_connection_chunkcb)(httpd_connection *conn, void *arg);
|
||||
typedef void (*httpd_query_iteratecb)(const char *key, const char *val, void *arg);
|
||||
|
||||
enum httpd_methods
|
||||
{
|
||||
HTTPD_METHOD_GET = 1 << 0,
|
||||
@ -62,16 +61,18 @@ struct httpd_uri_parsed
|
||||
* evbuffer.
|
||||
*/
|
||||
struct httpd_request {
|
||||
// Request method
|
||||
enum httpd_methods method;
|
||||
// The request URI
|
||||
const char *uri;
|
||||
// User-agent (if available)
|
||||
const char *user_agent;
|
||||
// The parsed request URI given to us by httpd_uri_parse
|
||||
struct httpd_uri_parsed *uri_parsed;
|
||||
// Shortcut to &uri_parsed->query
|
||||
httpd_query *query;
|
||||
// Shortcut to &uri_parsed->uri
|
||||
const char *uri;
|
||||
// Backend private request object
|
||||
void *backend;
|
||||
httpd_backend *backend;
|
||||
// Source IP address (ipv4 or ipv6) and port of the request (if available)
|
||||
char *peer_address;
|
||||
unsigned short peer_port;
|
||||
@ -82,6 +83,8 @@ struct httpd_request {
|
||||
httpd_headers *in_headers;
|
||||
// Request body
|
||||
struct evbuffer *in_body;
|
||||
// Response headers
|
||||
httpd_headers *out_headers;
|
||||
// Reply evbuffer
|
||||
struct evbuffer *reply;
|
||||
|
||||
@ -149,8 +152,11 @@ httpd_uri_parse(const char *uri);
|
||||
void
|
||||
httpd_stream_file(struct httpd_request *hreq, int id);
|
||||
|
||||
int
|
||||
httpd_request_set(struct httpd_request *hreq, struct httpd_uri_parsed *uri_parsed, const char *user_agent, struct httpd_uri_map *uri_map);
|
||||
void
|
||||
httpd_request_set(struct httpd_request *hreq, const char *uri, const char *user_agent);
|
||||
|
||||
void
|
||||
httpd_request_unset(struct httpd_request *hreq);
|
||||
|
||||
bool
|
||||
httpd_request_not_modified_since(struct httpd_request *hreq, time_t mtime);
|
||||
@ -206,6 +212,11 @@ httpd_basic_auth(struct httpd_request *hreq, const char *user, const char *passw
|
||||
|
||||
/*-------------------------- WRAPPERS FOR EVHTTP -----------------------------*/
|
||||
|
||||
typedef void (*httpd_general_cb)(httpd_backend *backend, void *arg);
|
||||
typedef void (*httpd_connection_closecb)(httpd_connection *conn, void *arg);
|
||||
typedef void (*httpd_connection_chunkcb)(httpd_connection *conn, void *arg);
|
||||
typedef void (*httpd_query_iteratecb)(const char *key, const char *val, void *arg);
|
||||
|
||||
const char *
|
||||
httpd_query_value_find(httpd_query *query, const char *key);
|
||||
|
||||
@ -227,12 +238,6 @@ httpd_header_add(httpd_headers *headers, const char *key, const char *val);
|
||||
void
|
||||
httpd_headers_clear(httpd_headers *headers);
|
||||
|
||||
httpd_headers *
|
||||
httpd_request_input_headers_get(struct httpd_request *hreq);
|
||||
|
||||
httpd_headers *
|
||||
httpd_request_output_headers_get(struct httpd_request *hreq);
|
||||
|
||||
int
|
||||
httpd_connection_closecb_set(httpd_connection *conn, httpd_connection_closecb cb, void *arg);
|
||||
|
||||
@ -244,15 +249,6 @@ httpd_connection_free(httpd_connection *conn);
|
||||
|
||||
httpd_connection *
|
||||
httpd_request_connection_get(struct httpd_request *hreq);
|
||||
/*
|
||||
const char *
|
||||
httpd_request_uri_get(struct httpd_request *hreq);
|
||||
*/
|
||||
int
|
||||
httpd_request_peer_get(char **addr, uint16_t *port, struct httpd_request *hreq);
|
||||
|
||||
int
|
||||
httpd_request_method_get(enum httpd_methods *method, struct httpd_request *hreq);
|
||||
|
||||
void
|
||||
httpd_request_backend_free(struct httpd_request *hreq);
|
||||
@ -272,4 +268,40 @@ httpd_reply_chunk_send(struct httpd_request *hreq, struct evbuffer *evbuf, httpd
|
||||
void
|
||||
httpd_reply_end_send(struct httpd_request *hreq);
|
||||
|
||||
void
|
||||
httpd_server_free(httpd_server *server);
|
||||
|
||||
httpd_server *
|
||||
httpd_server_new(struct event_base *evbase, unsigned short port, httpd_general_cb cb, void *arg);
|
||||
|
||||
void
|
||||
httpd_server_allow_origin_set(httpd_server *server, bool allow);
|
||||
|
||||
|
||||
/*---------- Only called by httpd.c to populate struct httpd_request ---------*/
|
||||
|
||||
httpd_connection *
|
||||
httpd_backend_connection_get(httpd_backend *backend);
|
||||
|
||||
const char *
|
||||
httpd_backend_uri_get(httpd_backend *backend);
|
||||
|
||||
httpd_headers *
|
||||
httpd_backend_input_headers_get(httpd_backend *backend);
|
||||
|
||||
httpd_headers *
|
||||
httpd_backend_output_headers_get(httpd_backend *backend);
|
||||
|
||||
struct evbuffer *
|
||||
httpd_backend_input_buffer_get(httpd_backend *backend);
|
||||
|
||||
int
|
||||
httpd_backend_peer_get(char **addr, uint16_t *port, httpd_backend *backend);
|
||||
|
||||
int
|
||||
httpd_backend_method_get(enum httpd_methods *method, httpd_backend *backend);
|
||||
|
||||
void
|
||||
httpd_backend_preprocess(httpd_backend *backend);
|
||||
|
||||
#endif /* !__HTTPD_INTERNAL_H__ */
|
||||
|
@ -4705,8 +4705,6 @@ static struct httpd_uri_map adm_handlers[] =
|
||||
static void
|
||||
jsonapi_request(struct httpd_request *hreq)
|
||||
{
|
||||
;
|
||||
httpd_headers *headers;
|
||||
int status_code;
|
||||
|
||||
DPRINTF(E_DBG, L_WEB, "JSON api request: '%s'\n", hreq->uri);
|
||||
@ -4733,8 +4731,7 @@ jsonapi_request(struct httpd_request *hreq)
|
||||
switch (status_code)
|
||||
{
|
||||
case HTTP_OK: /* 200 OK */
|
||||
headers = httpd_request_output_headers_get(hreq);
|
||||
httpd_header_add(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);
|
||||
break;
|
||||
case HTTP_NOCONTENT: /* 204 No Content */
|
||||
|
@ -1,8 +1,10 @@
|
||||
#include <sys/queue.h>
|
||||
|
||||
#include <event2/http.h>
|
||||
#include <event2/http_struct.h>
|
||||
#include <event2/keyvalq_struct.h>
|
||||
|
||||
#include "misc.h" // For net_evhttp_bind
|
||||
#include "httpd_internal.h"
|
||||
|
||||
int
|
||||
@ -28,45 +30,7 @@ httpd_connection_free(httpd_connection *conn)
|
||||
httpd_connection *
|
||||
httpd_request_connection_get(struct httpd_request *hreq)
|
||||
{
|
||||
return evhttp_request_get_connection(hreq->backend);
|
||||
}
|
||||
/*
|
||||
const char *
|
||||
httpd_request_uri_get(httpd_request *req)
|
||||
{
|
||||
return evhttp_request_get_uri(req);
|
||||
}
|
||||
*/
|
||||
int
|
||||
httpd_request_peer_get(char **addr, uint16_t *port, struct httpd_request *hreq)
|
||||
{
|
||||
httpd_connection *conn = httpd_request_connection_get(hreq->backend);
|
||||
if (!conn)
|
||||
return -1;
|
||||
|
||||
return httpd_connection_peer_get(addr, port, conn);
|
||||
}
|
||||
|
||||
int
|
||||
httpd_request_method_get(enum httpd_methods *method, struct httpd_request *hreq)
|
||||
{
|
||||
enum evhttp_cmd_type cmd = evhttp_request_get_command(hreq->backend);
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case EVHTTP_REQ_GET: *method = HTTPD_METHOD_GET; break;
|
||||
case EVHTTP_REQ_POST: *method = HTTPD_METHOD_POST; break;
|
||||
case EVHTTP_REQ_HEAD: *method = HTTPD_METHOD_HEAD; break;
|
||||
case EVHTTP_REQ_PUT: *method = HTTPD_METHOD_PUT; break;
|
||||
case EVHTTP_REQ_DELETE: *method = HTTPD_METHOD_DELETE; break;
|
||||
case EVHTTP_REQ_OPTIONS: *method = HTTPD_METHOD_OPTIONS; break;
|
||||
case EVHTTP_REQ_TRACE: *method = HTTPD_METHOD_TRACE; break;
|
||||
case EVHTTP_REQ_CONNECT: *method = HTTPD_METHOD_CONNECT; break;
|
||||
case EVHTTP_REQ_PATCH: *method = HTTPD_METHOD_PATCH; break;
|
||||
default: *method = HTTPD_METHOD_GET; return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return httpd_backend_connection_get(hreq->backend);
|
||||
}
|
||||
|
||||
void
|
||||
@ -132,12 +96,6 @@ httpd_headers_clear(httpd_headers *headers)
|
||||
evhttp_clear_headers(headers);
|
||||
}
|
||||
|
||||
httpd_headers *
|
||||
httpd_request_input_headers_get(struct httpd_request *hreq)
|
||||
{
|
||||
return evhttp_request_get_input_headers(hreq->backend);
|
||||
}
|
||||
|
||||
httpd_headers *
|
||||
httpd_request_output_headers_get(struct httpd_request *hreq)
|
||||
{
|
||||
@ -167,3 +125,110 @@ httpd_reply_end_send(struct httpd_request *hreq)
|
||||
{
|
||||
evhttp_send_reply_end(hreq->backend);
|
||||
}
|
||||
|
||||
void
|
||||
httpd_server_free(httpd_server *server)
|
||||
{
|
||||
if (!server)
|
||||
return;
|
||||
|
||||
evhttp_free(server);
|
||||
}
|
||||
|
||||
httpd_server *
|
||||
httpd_server_new(struct event_base *evbase, unsigned short port, httpd_general_cb cb, void *arg)
|
||||
{
|
||||
int ret;
|
||||
struct evhttp *server = evhttp_new(evbase);
|
||||
|
||||
if (!server)
|
||||
goto error;
|
||||
|
||||
ret = net_evhttp_bind(server, port, "httpd");
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
evhttp_set_gencb(server, cb, arg);
|
||||
|
||||
return server;
|
||||
|
||||
error:
|
||||
httpd_server_free(server);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
httpd_server_allow_origin_set(httpd_server *server, bool allow)
|
||||
{
|
||||
evhttp_set_allowed_methods(server, EVHTTP_REQ_GET | EVHTTP_REQ_POST | EVHTTP_REQ_PUT | EVHTTP_REQ_DELETE | EVHTTP_REQ_HEAD | EVHTTP_REQ_OPTIONS);
|
||||
}
|
||||
|
||||
httpd_connection *
|
||||
httpd_backend_connection_get(httpd_backend *backend)
|
||||
{
|
||||
return evhttp_request_get_connection(backend);
|
||||
}
|
||||
|
||||
const char *
|
||||
httpd_backend_uri_get(httpd_backend *backend)
|
||||
{
|
||||
return evhttp_request_get_uri(backend);
|
||||
}
|
||||
|
||||
httpd_headers *
|
||||
httpd_backend_input_headers_get(httpd_backend *backend)
|
||||
{
|
||||
return evhttp_request_get_input_headers(backend);
|
||||
}
|
||||
|
||||
httpd_headers *
|
||||
httpd_backend_output_headers_get(httpd_backend *backend)
|
||||
{
|
||||
return evhttp_request_get_output_headers(backend);
|
||||
}
|
||||
|
||||
struct evbuffer *
|
||||
httpd_backend_input_buffer_get(httpd_backend *backend)
|
||||
{
|
||||
return evhttp_request_get_input_buffer(backend);
|
||||
}
|
||||
|
||||
int
|
||||
httpd_backend_peer_get(char **addr, uint16_t *port, httpd_backend *backend)
|
||||
{
|
||||
httpd_connection *conn = httpd_backend_connection_get(backend);
|
||||
if (!conn)
|
||||
return -1;
|
||||
|
||||
return httpd_connection_peer_get(addr, port, conn);
|
||||
}
|
||||
|
||||
int
|
||||
httpd_backend_method_get(enum httpd_methods *method, httpd_backend *backend)
|
||||
{
|
||||
enum evhttp_cmd_type cmd = evhttp_request_get_command(backend);
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case EVHTTP_REQ_GET: *method = HTTPD_METHOD_GET; break;
|
||||
case EVHTTP_REQ_POST: *method = HTTPD_METHOD_POST; break;
|
||||
case EVHTTP_REQ_HEAD: *method = HTTPD_METHOD_HEAD; break;
|
||||
case EVHTTP_REQ_PUT: *method = HTTPD_METHOD_PUT; break;
|
||||
case EVHTTP_REQ_DELETE: *method = HTTPD_METHOD_DELETE; break;
|
||||
case EVHTTP_REQ_OPTIONS: *method = HTTPD_METHOD_OPTIONS; break;
|
||||
case EVHTTP_REQ_TRACE: *method = HTTPD_METHOD_TRACE; break;
|
||||
case EVHTTP_REQ_CONNECT: *method = HTTPD_METHOD_CONNECT; break;
|
||||
case EVHTTP_REQ_PATCH: *method = HTTPD_METHOD_PATCH; break;
|
||||
default: *method = HTTPD_METHOD_GET; return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
httpd_backend_preprocess(httpd_backend *backend)
|
||||
{
|
||||
// Clear the proxy request flag set by evhttp if the request URI was absolute.
|
||||
// It has side-effects on Connection: keep-alive
|
||||
backend->flags &= ~EVHTTP_PROXY_REQUEST;
|
||||
}
|
||||
|
@ -160,7 +160,6 @@ static void
|
||||
rsp_send_error(struct httpd_request *hreq, char *errmsg)
|
||||
{
|
||||
struct evbuffer *evbuf;
|
||||
httpd_headers *headers;
|
||||
mxml_node_t *reply;
|
||||
mxml_node_t *status;
|
||||
mxml_node_t *node;
|
||||
@ -196,9 +195,8 @@ rsp_send_error(struct httpd_request *hreq, char *errmsg)
|
||||
return;
|
||||
}
|
||||
|
||||
headers = httpd_request_output_headers_get(hreq);
|
||||
httpd_header_add(headers, "Content-Type", "text/xml; charset=utf-8");
|
||||
httpd_header_add(headers, "Connection", "close");
|
||||
httpd_header_add(hreq->out_headers, "Content-Type", "text/xml; charset=utf-8");
|
||||
httpd_header_add(hreq->out_headers, "Connection", "close");
|
||||
|
||||
httpd_send_reply(hreq, HTTP_OK, "OK", evbuf, HTTPD_SEND_NO_GZIP);
|
||||
|
||||
@ -280,7 +278,6 @@ static void
|
||||
rsp_send_reply(struct httpd_request *hreq, mxml_node_t *reply)
|
||||
{
|
||||
struct evbuffer *evbuf;
|
||||
httpd_headers *headers;
|
||||
|
||||
evbuf = mxml_to_evbuf(reply);
|
||||
mxmlDelete(reply);
|
||||
@ -292,9 +289,8 @@ rsp_send_reply(struct httpd_request *hreq, mxml_node_t *reply)
|
||||
return;
|
||||
}
|
||||
|
||||
headers = httpd_request_output_headers_get(hreq);
|
||||
httpd_header_add(headers, "Content-Type", "text/xml; charset=utf-8");
|
||||
httpd_header_add(headers, "Connection", "close");
|
||||
httpd_header_add(hreq->out_headers, "Content-Type", "text/xml; charset=utf-8");
|
||||
httpd_header_add(hreq->out_headers, "Connection", "close");
|
||||
|
||||
httpd_send_reply(hreq, HTTP_OK, "OK", evbuf, 0);
|
||||
|
||||
|
@ -537,7 +537,6 @@ static void
|
||||
streaming_request(struct httpd_request *hreq)
|
||||
{
|
||||
struct streaming_session *session;
|
||||
httpd_headers *output_headers;
|
||||
cfg_t *lib;
|
||||
const char *name;
|
||||
const char *param;
|
||||
@ -561,21 +560,20 @@ streaming_request(struct httpd_request *hreq)
|
||||
lib = cfg_getsec(cfg, "library");
|
||||
name = cfg_getstr(lib, "name");
|
||||
|
||||
output_headers = httpd_request_output_headers_get(hreq);
|
||||
httpd_header_add(output_headers, "Content-Type", "audio/mpeg");
|
||||
httpd_header_add(output_headers, "Server", PACKAGE_NAME "/" VERSION);
|
||||
httpd_header_add(output_headers, "Cache-Control", "no-cache");
|
||||
httpd_header_add(output_headers, "Pragma", "no-cache");
|
||||
httpd_header_add(output_headers, "Expires", "Mon, 31 Aug 2015 06:00:00 GMT");
|
||||
httpd_header_add(hreq->out_headers, "Content-Type", "audio/mpeg");
|
||||
httpd_header_add(hreq->out_headers, "Server", PACKAGE_NAME "/" VERSION);
|
||||
httpd_header_add(hreq->out_headers, "Cache-Control", "no-cache");
|
||||
httpd_header_add(hreq->out_headers, "Pragma", "no-cache");
|
||||
httpd_header_add(hreq->out_headers, "Expires", "Mon, 31 Aug 2015 06:00:00 GMT");
|
||||
if (require_icy)
|
||||
{
|
||||
++streaming_icy_clients;
|
||||
httpd_header_add(output_headers, "icy-name", name);
|
||||
httpd_header_add(hreq->out_headers, "icy-name", name);
|
||||
snprintf(buf, sizeof(buf)-1, "%d", streaming_icy_metaint);
|
||||
httpd_header_add(output_headers, "icy-metaint", buf);
|
||||
httpd_header_add(hreq->out_headers, "icy-metaint", buf);
|
||||
}
|
||||
httpd_header_add(output_headers, "Access-Control-Allow-Origin", "*");
|
||||
httpd_header_add(output_headers, "Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
||||
httpd_header_add(hreq->out_headers, "Access-Control-Allow-Origin", "*");
|
||||
httpd_header_add(hreq->out_headers, "Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
||||
|
||||
httpd_reply_start_send(hreq, HTTP_OK, "OK");
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user