mirror of
https://github.com/owntone/owntone-server.git
synced 2025-02-10 05:08:11 -05:00
[httpd] Start abstracting evhttp out of httpd.c
This commit is contained in:
parent
db25a714bd
commit
2ec0b27468
198
src/httpd.c
198
src/httpd.c
@ -99,7 +99,7 @@ struct content_type_map {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct stream_ctx {
|
struct stream_ctx {
|
||||||
struct evhttp_request *req;
|
struct httpd_request *hreq;
|
||||||
uint8_t *buf;
|
uint8_t *buf;
|
||||||
struct evbuffer *evbuf;
|
struct evbuffer *evbuf;
|
||||||
struct event *ev;
|
struct event *ev;
|
||||||
@ -294,8 +294,6 @@ modules_search(const char *path)
|
|||||||
static int
|
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)
|
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)
|
||||||
{
|
{
|
||||||
;
|
|
||||||
struct evhttp_connection *evcon;
|
|
||||||
struct httpd_uri_map *uri;
|
struct httpd_uri_map *uri;
|
||||||
int req_method;
|
int req_method;
|
||||||
int ret;
|
int ret;
|
||||||
@ -303,10 +301,10 @@ request_set(struct httpd_request *hreq, struct evhttp_request *req, struct httpd
|
|||||||
memset(hreq, 0, 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->backend = req;
|
||||||
hreq->uri = uri_parsed->uri;
|
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->query);
|
||||||
req_method = 0;
|
req_method = 0;
|
||||||
|
|
||||||
if (req)
|
if (req)
|
||||||
@ -314,12 +312,7 @@ request_set(struct httpd_request *hreq, struct evhttp_request *req, struct httpd
|
|||||||
hreq->in_body = evhttp_request_get_input_buffer(req);
|
hreq->in_body = evhttp_request_get_input_buffer(req);
|
||||||
hreq->in_headers = evhttp_request_get_input_headers(req);
|
hreq->in_headers = evhttp_request_get_input_headers(req);
|
||||||
hreq->user_agent = evhttp_find_header(hreq->in_headers, "User-Agent");
|
hreq->user_agent = evhttp_find_header(hreq->in_headers, "User-Agent");
|
||||||
|
httpd_request_peer_get(&hreq->peer_address, &hreq->peer_port, hreq);
|
||||||
evcon = evhttp_request_get_connection(req);
|
|
||||||
if (evcon)
|
|
||||||
evhttp_connection_get_peer(evcon, &hreq->peer_address, &hreq->peer_port);
|
|
||||||
else
|
|
||||||
DPRINTF(E_LOG, L_HTTPD, "Connection to client lost or missing\n");
|
|
||||||
|
|
||||||
req_method = evhttp_request_get_command(req);
|
req_method = evhttp_request_get_command(req);
|
||||||
}
|
}
|
||||||
@ -349,10 +342,10 @@ request_set(struct httpd_request *hreq, struct evhttp_request *req, struct httpd
|
|||||||
void
|
void
|
||||||
httpd_redirect_to(struct httpd_request *hreq, const char *path)
|
httpd_redirect_to(struct httpd_request *hreq, const char *path)
|
||||||
{
|
{
|
||||||
struct evkeyvalq *headers;
|
httpd_headers *headers;
|
||||||
|
|
||||||
headers = evhttp_request_get_output_headers(hreq->req);
|
headers = httpd_request_output_headers_get(hreq);
|
||||||
evhttp_add_header(headers, "Location", path);
|
httpd_header_add(headers, "Location", path);
|
||||||
|
|
||||||
httpd_send_reply(hreq, HTTP_MOVETEMP, "Moved", NULL, HTTPD_SEND_NO_GZIP);
|
httpd_send_reply(hreq, HTTP_MOVETEMP, "Moved", NULL, HTTPD_SEND_NO_GZIP);
|
||||||
}
|
}
|
||||||
@ -371,21 +364,19 @@ httpd_redirect_to(struct httpd_request *hreq, const char *path)
|
|||||||
bool
|
bool
|
||||||
httpd_request_etag_matches(struct httpd_request *hreq, const char *etag)
|
httpd_request_etag_matches(struct httpd_request *hreq, const char *etag)
|
||||||
{
|
{
|
||||||
struct evkeyvalq *input_headers;
|
httpd_headers *output_headers;
|
||||||
struct evkeyvalq *output_headers;
|
|
||||||
const char *none_match;
|
const char *none_match;
|
||||||
|
|
||||||
input_headers = evhttp_request_get_input_headers(hreq->req);
|
none_match = httpd_header_find(hreq->in_headers, "If-None-Match");
|
||||||
none_match = evhttp_find_header(input_headers, "If-None-Match");
|
|
||||||
|
|
||||||
// Return not modified, if given timestamp matches "If-Modified-Since" request header
|
// Return not modified, if given timestamp matches "If-Modified-Since" request header
|
||||||
if (none_match && (strcasecmp(etag, none_match) == 0))
|
if (none_match && (strcasecmp(etag, none_match) == 0))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Add cache headers to allow client side caching
|
// Add cache headers to allow client side caching
|
||||||
output_headers = evhttp_request_get_output_headers(hreq->req);
|
output_headers = httpd_request_output_headers_get(hreq);
|
||||||
evhttp_add_header(output_headers, "Cache-Control", "private,no-cache,max-age=0");
|
httpd_header_add(output_headers, "Cache-Control", "private,no-cache,max-age=0");
|
||||||
evhttp_add_header(output_headers, "ETag", etag);
|
httpd_header_add(output_headers, "ETag", etag);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -404,14 +395,12 @@ httpd_request_etag_matches(struct httpd_request *hreq, const char *etag)
|
|||||||
bool
|
bool
|
||||||
httpd_request_not_modified_since(struct httpd_request *hreq, time_t mtime)
|
httpd_request_not_modified_since(struct httpd_request *hreq, time_t mtime)
|
||||||
{
|
{
|
||||||
struct evkeyvalq *input_headers;
|
httpd_headers *output_headers;
|
||||||
struct evkeyvalq *output_headers;
|
|
||||||
char last_modified[1000];
|
char last_modified[1000];
|
||||||
const char *modified_since;
|
const char *modified_since;
|
||||||
struct tm timebuf;
|
struct tm timebuf;
|
||||||
|
|
||||||
input_headers = evhttp_request_get_input_headers(hreq->req);
|
modified_since = evhttp_find_header(hreq->in_headers, "If-Modified-Since");
|
||||||
modified_since = evhttp_find_header(input_headers, "If-Modified-Since");
|
|
||||||
|
|
||||||
strftime(last_modified, sizeof(last_modified), "%a, %d %b %Y %H:%M:%S %Z", gmtime_r(&mtime, &timebuf));
|
strftime(last_modified, sizeof(last_modified), "%a, %d %b %Y %H:%M:%S %Z", gmtime_r(&mtime, &timebuf));
|
||||||
|
|
||||||
@ -420,9 +409,9 @@ httpd_request_not_modified_since(struct httpd_request *hreq, time_t mtime)
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Add cache headers to allow client side caching
|
// Add cache headers to allow client side caching
|
||||||
output_headers = evhttp_request_get_output_headers(hreq->req);
|
output_headers = httpd_request_output_headers_get(hreq);
|
||||||
evhttp_add_header(output_headers, "Cache-Control", "private,no-cache,max-age=0");
|
httpd_header_add(output_headers, "Cache-Control", "private,no-cache,max-age=0");
|
||||||
evhttp_add_header(output_headers, "Last-Modified", last_modified);
|
httpd_header_add(output_headers, "Last-Modified", last_modified);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -430,17 +419,17 @@ httpd_request_not_modified_since(struct httpd_request *hreq, time_t mtime)
|
|||||||
void
|
void
|
||||||
httpd_response_not_cachable(struct httpd_request *hreq)
|
httpd_response_not_cachable(struct httpd_request *hreq)
|
||||||
{
|
{
|
||||||
struct evkeyvalq *output_headers;
|
httpd_headers *output_headers;
|
||||||
|
|
||||||
output_headers = evhttp_request_get_output_headers(hreq->req);
|
output_headers = httpd_request_output_headers_get(hreq);
|
||||||
|
|
||||||
// Remove potentially set cache control headers
|
// Remove potentially set cache control headers
|
||||||
evhttp_remove_header(output_headers, "Cache-Control");
|
httpd_header_remove(output_headers, "Cache-Control");
|
||||||
evhttp_remove_header(output_headers, "Last-Modified");
|
httpd_header_remove(output_headers, "Last-Modified");
|
||||||
evhttp_remove_header(output_headers, "ETag");
|
httpd_header_remove(output_headers, "ETag");
|
||||||
|
|
||||||
// Tell clients that they are not allowed to cache this response
|
// Tell clients that they are not allowed to cache this response
|
||||||
evhttp_add_header(output_headers, "Cache-Control", "no-store");
|
httpd_header_add(output_headers, "Cache-Control", "no-store");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -451,7 +440,7 @@ serve_file(struct httpd_request *hreq, const char *uri)
|
|||||||
char deref[PATH_MAX];
|
char deref[PATH_MAX];
|
||||||
char *ctype;
|
char *ctype;
|
||||||
struct evbuffer *evbuf;
|
struct evbuffer *evbuf;
|
||||||
struct evkeyvalq *output_headers;
|
httpd_headers *output_headers;
|
||||||
struct stat sb;
|
struct stat sb;
|
||||||
int fd;
|
int fd;
|
||||||
int i;
|
int i;
|
||||||
@ -595,8 +584,8 @@ serve_file(struct httpd_request *hreq, const char *uri)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
output_headers = evhttp_request_get_output_headers(hreq->req);
|
output_headers = httpd_request_output_headers_get(hreq);
|
||||||
evhttp_add_header(output_headers, "Content-Type", ctype);
|
httpd_header_add(output_headers, "Content-Type", ctype);
|
||||||
|
|
||||||
httpd_send_reply(hreq, HTTP_OK, "OK", evbuf, HTTPD_SEND_NO_GZIP);
|
httpd_send_reply(hreq, HTTP_OK, "OK", evbuf, HTTPD_SEND_NO_GZIP);
|
||||||
|
|
||||||
@ -616,15 +605,10 @@ serve_file(struct httpd_request *hreq, const char *uri)
|
|||||||
static void
|
static void
|
||||||
stream_end(struct stream_ctx *st, int failed)
|
stream_end(struct stream_ctx *st, int failed)
|
||||||
{
|
{
|
||||||
struct evhttp_connection *evcon;
|
httpd_request_closecb_set(st->hreq, NULL, NULL);
|
||||||
|
|
||||||
evcon = evhttp_request_get_connection(st->req);
|
|
||||||
|
|
||||||
if (evcon)
|
|
||||||
evhttp_connection_set_closecb(evcon, NULL, NULL);
|
|
||||||
|
|
||||||
if (!failed)
|
if (!failed)
|
||||||
evhttp_send_reply_end(st->req);
|
httpd_reply_end_send(st->hreq);
|
||||||
|
|
||||||
evbuffer_free(st->evbuf);
|
evbuffer_free(st->evbuf);
|
||||||
event_free(st->ev);
|
event_free(st->ev);
|
||||||
@ -656,7 +640,7 @@ stream_end_register(struct stream_ctx *st)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
stream_chunk_resched_cb(struct evhttp_connection *evcon, void *arg)
|
stream_chunk_resched_cb(httpd_connection *conn, void *arg)
|
||||||
{
|
{
|
||||||
struct stream_ctx *st;
|
struct stream_ctx *st;
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
@ -721,7 +705,7 @@ stream_chunk_xcode_cb(int fd, short event, void *arg)
|
|||||||
else
|
else
|
||||||
ret = xcoded;
|
ret = xcoded;
|
||||||
|
|
||||||
evhttp_send_reply_chunk_with_cb(st->req, st->evbuf, stream_chunk_resched_cb, st);
|
httpd_reply_chunk_send(st->hreq, st->evbuf, stream_chunk_resched_cb, st);
|
||||||
|
|
||||||
st->offset += ret;
|
st->offset += ret;
|
||||||
|
|
||||||
@ -777,7 +761,7 @@ stream_chunk_raw_cb(int fd, short event, void *arg)
|
|||||||
|
|
||||||
evbuffer_add(st->evbuf, st->buf, ret);
|
evbuffer_add(st->evbuf, st->buf, ret);
|
||||||
|
|
||||||
evhttp_send_reply_chunk_with_cb(st->req, st->evbuf, stream_chunk_resched_cb, st);
|
httpd_reply_chunk_send(st->hreq, st->evbuf, stream_chunk_resched_cb, st);
|
||||||
|
|
||||||
st->offset += ret;
|
st->offset += ret;
|
||||||
|
|
||||||
@ -785,7 +769,7 @@ stream_chunk_raw_cb(int fd, short event, void *arg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
stream_fail_cb(struct evhttp_connection *evcon, void *arg)
|
stream_fail_cb(httpd_connection *conn, void *arg)
|
||||||
{
|
{
|
||||||
struct stream_ctx *st;
|
struct stream_ctx *st;
|
||||||
|
|
||||||
@ -838,7 +822,7 @@ 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 = { .req = req }; // TODO clean this up
|
struct httpd_request hreq = { .backend = req }; // TODO clean this up
|
||||||
struct httpd_uri_parsed *parsed;
|
struct httpd_uri_parsed *parsed;
|
||||||
struct httpd_module *m;
|
struct httpd_module *m;
|
||||||
const char *uri;
|
const char *uri;
|
||||||
@ -856,9 +840,9 @@ httpd_gen_cb(struct evhttp_request *req, void *arg)
|
|||||||
{
|
{
|
||||||
output_headers = evhttp_request_get_output_headers(req);
|
output_headers = evhttp_request_get_output_headers(req);
|
||||||
|
|
||||||
evhttp_add_header(output_headers, "Access-Control-Allow-Origin", allow_origin);
|
httpd_header_add(output_headers, "Access-Control-Allow-Origin", allow_origin);
|
||||||
evhttp_add_header(output_headers, "Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
httpd_header_add(output_headers, "Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
||||||
evhttp_add_header(output_headers, "Access-Control-Allow-Headers", "authorization");
|
httpd_header_add(output_headers, "Access-Control-Allow-Headers", "authorization");
|
||||||
|
|
||||||
// In this case there is no reason to go through httpd_send_reply
|
// In this case there is no reason to go through httpd_send_reply
|
||||||
evhttp_send_reply(req, HTTP_OK, "OK", NULL);
|
evhttp_send_reply(req, HTTP_OK, "OK", NULL);
|
||||||
@ -921,7 +905,7 @@ httpd_uri_free(struct httpd_uri_parsed *parsed)
|
|||||||
free(parsed->path_parts[i]);
|
free(parsed->path_parts[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
evhttp_clear_headers(&(parsed->ev_query));
|
httpd_query_clear(&(parsed->query));
|
||||||
|
|
||||||
if (parsed->ev_uri)
|
if (parsed->ev_uri)
|
||||||
evhttp_uri_free(parsed->ev_uri);
|
evhttp_uri_free(parsed->ev_uri);
|
||||||
@ -961,7 +945,7 @@ httpd_uri_parse(const char *uri)
|
|||||||
query = evhttp_uri_get_query(parsed->ev_uri);
|
query = evhttp_uri_get_query(parsed->ev_uri);
|
||||||
if (query && strchr(query, '='))
|
if (query && strchr(query, '='))
|
||||||
{
|
{
|
||||||
ret = evhttp_parse_query_str(query, &(parsed->ev_query));
|
ret = evhttp_parse_query_str(query, &(parsed->query));
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_DAAP, "Invalid query '%s' in request: '%s'\n", query, parsed->uri);
|
DPRINTF(E_LOG, L_DAAP, "Invalid query '%s' in request: '%s'\n", query, parsed->uri);
|
||||||
@ -1023,12 +1007,9 @@ httpd_stream_file(struct httpd_request *hreq, int id)
|
|||||||
void (*stream_cb)(int fd, short event, void *arg);
|
void (*stream_cb)(int fd, short event, void *arg);
|
||||||
struct stat sb;
|
struct stat sb;
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
struct evhttp_connection *evcon;
|
httpd_headers *output_headers;
|
||||||
struct evkeyvalq *input_headers;
|
|
||||||
struct evkeyvalq *output_headers;
|
|
||||||
const char *param;
|
const char *param;
|
||||||
const char *param_end;
|
const char *param_end;
|
||||||
const char *ua;
|
|
||||||
const char *client_codecs;
|
const char *client_codecs;
|
||||||
char buf[64];
|
char buf[64];
|
||||||
int64_t offset;
|
int64_t offset;
|
||||||
@ -1040,9 +1021,7 @@ httpd_stream_file(struct httpd_request *hreq, int id)
|
|||||||
offset = 0;
|
offset = 0;
|
||||||
end_offset = 0;
|
end_offset = 0;
|
||||||
|
|
||||||
input_headers = evhttp_request_get_input_headers(hreq->req);
|
param = httpd_header_find(hreq->in_headers, "Range");
|
||||||
|
|
||||||
param = evhttp_find_header(input_headers, "Range");
|
|
||||||
if (param)
|
if (param)
|
||||||
{
|
{
|
||||||
DPRINTF(E_DBG, L_HTTPD, "Found Range header: %s\n", param);
|
DPRINTF(E_DBG, L_HTTPD, "Found Range header: %s\n", param);
|
||||||
@ -1104,12 +1083,11 @@ httpd_stream_file(struct httpd_request *hreq, int id)
|
|||||||
memset(st, 0, sizeof(struct stream_ctx));
|
memset(st, 0, sizeof(struct stream_ctx));
|
||||||
st->fd = -1;
|
st->fd = -1;
|
||||||
|
|
||||||
ua = evhttp_find_header(input_headers, "User-Agent");
|
client_codecs = httpd_header_find(hreq->in_headers, "Accept-Codecs");
|
||||||
client_codecs = evhttp_find_header(input_headers, "Accept-Codecs");
|
|
||||||
|
|
||||||
transcode = transcode_needed(ua, client_codecs, mfi->codectype);
|
transcode = transcode_needed(hreq->user_agent, client_codecs, mfi->codectype);
|
||||||
|
|
||||||
output_headers = evhttp_request_get_output_headers(hreq->req);
|
output_headers = httpd_request_output_headers_get(hreq);
|
||||||
|
|
||||||
if (transcode)
|
if (transcode)
|
||||||
{
|
{
|
||||||
@ -1127,7 +1105,7 @@ httpd_stream_file(struct httpd_request *hreq, int id)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!evhttp_find_header(output_headers, "Content-Type"))
|
if (!evhttp_find_header(output_headers, "Content-Type"))
|
||||||
evhttp_add_header(output_headers, "Content-Type", "audio/wav");
|
httpd_header_add(output_headers, "Content-Type", "audio/wav");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1187,8 +1165,8 @@ httpd_stream_file(struct httpd_request *hreq, int id)
|
|||||||
DPRINTF(E_LOG, L_HTTPD, "Content-Type too large for buffer, dropping\n");
|
DPRINTF(E_LOG, L_HTTPD, "Content-Type too large for buffer, dropping\n");
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
evhttp_remove_header(output_headers, "Content-Type");
|
httpd_header_remove(output_headers, "Content-Type");
|
||||||
evhttp_add_header(output_headers, "Content-Type", buf);
|
httpd_header_add(output_headers, "Content-Type", buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* If no Content-Type has been set and we're streaming audio, add a proper
|
/* If no Content-Type has been set and we're streaming audio, add a proper
|
||||||
@ -1201,7 +1179,7 @@ httpd_stream_file(struct httpd_request *hreq, int id)
|
|||||||
if ((ret < 0) || (ret >= sizeof(buf)))
|
if ((ret < 0) || (ret >= sizeof(buf)))
|
||||||
DPRINTF(E_LOG, L_HTTPD, "Content-Type too large for buffer, dropping\n");
|
DPRINTF(E_LOG, L_HTTPD, "Content-Type too large for buffer, dropping\n");
|
||||||
else
|
else
|
||||||
evhttp_add_header(output_headers, "Content-Type", buf);
|
httpd_header_add(output_headers, "Content-Type", buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1236,7 +1214,7 @@ httpd_stream_file(struct httpd_request *hreq, int id)
|
|||||||
st->id = mfi->id;
|
st->id = mfi->id;
|
||||||
st->start_offset = offset;
|
st->start_offset = offset;
|
||||||
st->stream_size = st->size;
|
st->stream_size = st->size;
|
||||||
st->req = hreq->req;
|
st->hreq = hreq;
|
||||||
|
|
||||||
if ((offset == 0) && (end_offset == 0))
|
if ((offset == 0) && (end_offset == 0))
|
||||||
{
|
{
|
||||||
@ -1250,10 +1228,10 @@ httpd_stream_file(struct httpd_request *hreq, int id)
|
|||||||
if ((ret < 0) || (ret >= sizeof(buf)))
|
if ((ret < 0) || (ret >= sizeof(buf)))
|
||||||
DPRINTF(E_LOG, L_HTTPD, "Content-Length too large for buffer, dropping\n");
|
DPRINTF(E_LOG, L_HTTPD, "Content-Length too large for buffer, dropping\n");
|
||||||
else
|
else
|
||||||
evhttp_add_header(output_headers, "Content-Length", buf);
|
httpd_header_add(output_headers, "Content-Length", buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
evhttp_send_reply_start(hreq->req, HTTP_OK, "OK");
|
httpd_reply_start_send(hreq, HTTP_OK, "OK");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1269,15 +1247,15 @@ httpd_stream_file(struct httpd_request *hreq, int id)
|
|||||||
if ((ret < 0) || (ret >= sizeof(buf)))
|
if ((ret < 0) || (ret >= sizeof(buf)))
|
||||||
DPRINTF(E_LOG, L_HTTPD, "Content-Range too large for buffer, dropping\n");
|
DPRINTF(E_LOG, L_HTTPD, "Content-Range too large for buffer, dropping\n");
|
||||||
else
|
else
|
||||||
evhttp_add_header(output_headers, "Content-Range", buf);
|
httpd_header_add(output_headers, "Content-Range", buf);
|
||||||
|
|
||||||
ret = snprintf(buf, sizeof(buf), "%" PRIi64, ((end_offset) ? end_offset + 1 : (int64_t)st->size) - offset);
|
ret = snprintf(buf, sizeof(buf), "%" PRIi64, ((end_offset) ? end_offset + 1 : (int64_t)st->size) - offset);
|
||||||
if ((ret < 0) || (ret >= sizeof(buf)))
|
if ((ret < 0) || (ret >= sizeof(buf)))
|
||||||
DPRINTF(E_LOG, L_HTTPD, "Content-Length too large for buffer, dropping\n");
|
DPRINTF(E_LOG, L_HTTPD, "Content-Length too large for buffer, dropping\n");
|
||||||
else
|
else
|
||||||
evhttp_add_header(output_headers, "Content-Length", buf);
|
httpd_header_add(output_headers, "Content-Length", buf);
|
||||||
|
|
||||||
evhttp_send_reply_start(hreq->req, 206, "Partial Content");
|
httpd_reply_start_send(hreq, 206, "Partial Content");
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_POSIX_FADVISE
|
#ifdef HAVE_POSIX_FADVISE
|
||||||
@ -1291,9 +1269,7 @@ httpd_stream_file(struct httpd_request *hreq, int id)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
evcon = evhttp_request_get_connection(hreq->req);
|
httpd_request_closecb_set(hreq, stream_fail_cb, st);
|
||||||
|
|
||||||
evhttp_connection_set_closecb(evcon, stream_fail_cb, st);
|
|
||||||
|
|
||||||
DPRINTF(E_INFO, L_HTTPD, "Kicking off streaming for %s\n", mfi->path);
|
DPRINTF(E_INFO, L_HTTPD, "Kicking off streaming for %s\n", mfi->path);
|
||||||
|
|
||||||
@ -1387,32 +1363,30 @@ void
|
|||||||
httpd_send_reply(struct httpd_request *hreq, int code, const char *reason, struct evbuffer *evbuf, enum httpd_send_flags flags)
|
httpd_send_reply(struct httpd_request *hreq, int code, const char *reason, struct evbuffer *evbuf, enum httpd_send_flags flags)
|
||||||
{
|
{
|
||||||
struct evbuffer *gzbuf;
|
struct evbuffer *gzbuf;
|
||||||
struct evkeyvalq *input_headers;
|
httpd_headers *output_headers;
|
||||||
struct evkeyvalq *output_headers;
|
|
||||||
const char *param;
|
const char *param;
|
||||||
int do_gzip;
|
int do_gzip;
|
||||||
|
|
||||||
if (!hreq->req)
|
if (!hreq->backend)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
input_headers = evhttp_request_get_input_headers(hreq->req);
|
output_headers = httpd_request_output_headers_get(hreq);
|
||||||
output_headers = evhttp_request_get_output_headers(hreq->req);
|
|
||||||
|
|
||||||
do_gzip = ( (!(flags & HTTPD_SEND_NO_GZIP)) &&
|
do_gzip = ( (!(flags & HTTPD_SEND_NO_GZIP)) &&
|
||||||
evbuf && (evbuffer_get_length(evbuf) > 512) &&
|
evbuf && (evbuffer_get_length(evbuf) > 512) &&
|
||||||
(param = evhttp_find_header(input_headers, "Accept-Encoding")) &&
|
(param = httpd_header_find(hreq->in_headers, "Accept-Encoding")) &&
|
||||||
(strstr(param, "gzip") || strstr(param, "*"))
|
(strstr(param, "gzip") || strstr(param, "*"))
|
||||||
);
|
);
|
||||||
|
|
||||||
if (allow_origin)
|
if (allow_origin)
|
||||||
evhttp_add_header(output_headers, "Access-Control-Allow-Origin", allow_origin);
|
httpd_header_add(output_headers, "Access-Control-Allow-Origin", allow_origin);
|
||||||
|
|
||||||
if (do_gzip && (gzbuf = httpd_gzip_deflate(evbuf)))
|
if (do_gzip && (gzbuf = httpd_gzip_deflate(evbuf)))
|
||||||
{
|
{
|
||||||
DPRINTF(E_DBG, L_HTTPD, "Gzipping response\n");
|
DPRINTF(E_DBG, L_HTTPD, "Gzipping response\n");
|
||||||
|
|
||||||
evhttp_add_header(output_headers, "Content-Encoding", "gzip");
|
httpd_header_add(output_headers, "Content-Encoding", "gzip");
|
||||||
evhttp_send_reply(hreq->req, code, reason, gzbuf);
|
httpd_reply_backend_send(hreq, code, reason, gzbuf);
|
||||||
evbuffer_free(gzbuf);
|
evbuffer_free(gzbuf);
|
||||||
|
|
||||||
// Drain original buffer, as would be after evhttp_send_reply()
|
// Drain original buffer, as would be after evhttp_send_reply()
|
||||||
@ -1420,7 +1394,7 @@ httpd_send_reply(struct httpd_request *hreq, int code, const char *reason, struc
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
evhttp_send_reply(hreq->req, code, reason, evbuf);
|
httpd_reply_backend_send(hreq, code, reason, evbuf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1428,22 +1402,17 @@ httpd_send_reply(struct httpd_request *hreq, int code, const char *reason, struc
|
|||||||
void
|
void
|
||||||
httpd_send_error(struct httpd_request *hreq, int error, const char *reason)
|
httpd_send_error(struct httpd_request *hreq, int error, const char *reason)
|
||||||
{
|
{
|
||||||
struct evkeyvalq *output_headers;
|
httpd_headers *output_headers;
|
||||||
struct evbuffer *evbuf;
|
struct evbuffer *evbuf;
|
||||||
|
|
||||||
if (!allow_origin)
|
output_headers = httpd_request_output_headers_get(hreq);
|
||||||
{
|
|
||||||
evhttp_send_error(hreq->req, error, reason);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
output_headers = evhttp_request_get_output_headers(hreq->req);
|
httpd_headers_clear(output_headers);
|
||||||
|
|
||||||
evhttp_clear_headers(output_headers);
|
if (allow_origin)
|
||||||
|
httpd_header_add(output_headers, "Access-Control-Allow-Origin", allow_origin);
|
||||||
evhttp_add_header(output_headers, "Access-Control-Allow-Origin", allow_origin);
|
httpd_header_add(output_headers, "Content-Type", "text/html");
|
||||||
evhttp_add_header(output_headers, "Content-Type", "text/html");
|
httpd_header_add(output_headers, "Connection", "close");
|
||||||
evhttp_add_header(output_headers, "Connection", "close");
|
|
||||||
|
|
||||||
evbuf = evbuffer_new();
|
evbuf = evbuffer_new();
|
||||||
if (!evbuf)
|
if (!evbuf)
|
||||||
@ -1451,7 +1420,7 @@ httpd_send_error(struct httpd_request *hreq, int error, const char *reason)
|
|||||||
else
|
else
|
||||||
evbuffer_add_printf(evbuf, ERR_PAGE, error, reason, reason);
|
evbuffer_add_printf(evbuf, ERR_PAGE, error, reason, reason);
|
||||||
|
|
||||||
evhttp_send_reply(hreq->req, error, reason, evbuf);
|
httpd_reply_backend_send(hreq, error, reason, evbuf);
|
||||||
|
|
||||||
if (evbuf)
|
if (evbuf)
|
||||||
evbuffer_free(evbuf);
|
evbuffer_free(evbuf);
|
||||||
@ -1460,28 +1429,16 @@ httpd_send_error(struct httpd_request *hreq, int error, const char *reason)
|
|||||||
bool
|
bool
|
||||||
httpd_admin_check_auth(struct httpd_request *hreq)
|
httpd_admin_check_auth(struct httpd_request *hreq)
|
||||||
{
|
{
|
||||||
struct evhttp_connection *evcon;
|
|
||||||
char *addr;
|
|
||||||
uint16_t port;
|
|
||||||
const char *passwd;
|
const char *passwd;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
evcon = evhttp_request_get_connection(hreq->req);
|
if (net_peer_address_is_trusted(hreq->peer_address))
|
||||||
if (!evcon)
|
|
||||||
{
|
|
||||||
DPRINTF(E_LOG, L_HTTPD, "Connection to client lost or missing\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
evhttp_connection_get_peer(evcon, &addr, &port);
|
|
||||||
|
|
||||||
if (net_peer_address_is_trusted(addr))
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
passwd = cfg_getstr(cfg_getsec(cfg, "general"), "admin_password");
|
passwd = cfg_getstr(cfg_getsec(cfg, "general"), "admin_password");
|
||||||
if (!passwd)
|
if (!passwd)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_HTTPD, "Web interface request to '%s' denied: No password set in the config\n", evhttp_request_get_uri(hreq->req));
|
DPRINTF(E_LOG, L_HTTPD, "Web interface request to '%s' denied: No password set in the config\n", hreq->uri);
|
||||||
|
|
||||||
httpd_send_error(hreq, 403, "Forbidden");
|
httpd_send_error(hreq, 403, "Forbidden");
|
||||||
return false;
|
return false;
|
||||||
@ -1492,7 +1449,7 @@ httpd_admin_check_auth(struct httpd_request *hreq)
|
|||||||
ret = httpd_basic_auth(hreq, "admin", passwd, PACKAGE " web interface");
|
ret = httpd_basic_auth(hreq, "admin", passwd, PACKAGE " web interface");
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_HTTPD, "Web interface request to '%s' denied: Incorrect password\n", evhttp_request_get_uri(hreq->req));
|
DPRINTF(E_LOG, L_HTTPD, "Web interface request to '%s' denied: Incorrect password\n", hreq->uri);
|
||||||
|
|
||||||
// httpd_basic_auth has sent a reply
|
// httpd_basic_auth has sent a reply
|
||||||
return false;
|
return false;
|
||||||
@ -1507,15 +1464,14 @@ int
|
|||||||
httpd_basic_auth(struct httpd_request *hreq, const char *user, const char *passwd, const char *realm)
|
httpd_basic_auth(struct httpd_request *hreq, const char *user, const char *passwd, const char *realm)
|
||||||
{
|
{
|
||||||
struct evbuffer *evbuf;
|
struct evbuffer *evbuf;
|
||||||
struct evkeyvalq *headers;
|
httpd_headers *output_headers;
|
||||||
char header[256];
|
char header[256];
|
||||||
const char *auth;
|
const char *auth;
|
||||||
char *authuser;
|
char *authuser;
|
||||||
char *authpwd;
|
char *authpwd;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
headers = evhttp_request_get_input_headers(hreq->req);
|
auth = httpd_header_find(hreq->in_headers, "Authorization");
|
||||||
auth = evhttp_find_header(headers, "Authorization");
|
|
||||||
if (!auth)
|
if (!auth)
|
||||||
{
|
{
|
||||||
DPRINTF(E_DBG, L_HTTPD, "No Authorization header\n");
|
DPRINTF(E_DBG, L_HTTPD, "No Authorization header\n");
|
||||||
@ -1590,8 +1546,8 @@ httpd_basic_auth(struct httpd_request *hreq, const char *user, const char *passw
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
headers = evhttp_request_get_output_headers(hreq->req);
|
output_headers = httpd_request_output_headers_get(hreq);
|
||||||
evhttp_add_header(headers, "WWW-Authenticate", header);
|
httpd_header_add(output_headers, "WWW-Authenticate", header);
|
||||||
|
|
||||||
evbuffer_add_printf(evbuf, ERR_PAGE, 401, "Unauthorized", "Authorization required");
|
evbuffer_add_printf(evbuf, ERR_PAGE, 401, "Unauthorized", "Authorization required");
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ struct httpd_uri_parsed
|
|||||||
{
|
{
|
||||||
const char *uri;
|
const char *uri;
|
||||||
struct evhttp_uri *ev_uri;
|
struct evhttp_uri *ev_uri;
|
||||||
struct evkeyvalq ev_query;
|
httpd_query query;
|
||||||
char *uri_decoded;
|
char *uri_decoded;
|
||||||
char *path;
|
char *path;
|
||||||
char *path_parts[31];
|
char *path_parts[31];
|
||||||
@ -64,14 +64,14 @@ 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;
|
||||||
// Shortcut to &uri_parsed->uri
|
|
||||||
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->query
|
||||||
httpd_query *query;
|
httpd_query *query;
|
||||||
// http request struct (if available)
|
// Shortcut to &uri_parsed->uri
|
||||||
struct evhttp_request *req;
|
const char *uri;
|
||||||
|
// Backend private request object
|
||||||
|
void *backend;
|
||||||
// Source IP address (ipv4 or ipv6) and port of the request (if available)
|
// Source IP address (ipv4 or ipv6) and port of the request (if available)
|
||||||
char *peer_address;
|
char *peer_address;
|
||||||
unsigned short peer_port;
|
unsigned short peer_port;
|
||||||
@ -212,6 +212,9 @@ httpd_query_value_find(httpd_query *query, const char *key);
|
|||||||
void
|
void
|
||||||
httpd_query_iterate(httpd_query *query, httpd_query_iteratecb cb, void *arg);
|
httpd_query_iterate(httpd_query *query, httpd_query_iteratecb cb, void *arg);
|
||||||
|
|
||||||
|
void
|
||||||
|
httpd_query_clear(httpd_query *query);
|
||||||
|
|
||||||
const char *
|
const char *
|
||||||
httpd_header_find(httpd_headers *headers, const char *key);
|
httpd_header_find(httpd_headers *headers, const char *key);
|
||||||
|
|
||||||
@ -232,10 +235,10 @@ httpd_request_output_headers_get(struct httpd_request *hreq);
|
|||||||
|
|
||||||
int
|
int
|
||||||
httpd_connection_closecb_set(httpd_connection *conn, httpd_connection_closecb cb, void *arg);
|
httpd_connection_closecb_set(httpd_connection *conn, httpd_connection_closecb cb, void *arg);
|
||||||
/*
|
|
||||||
int
|
int
|
||||||
httpd_connection_peer_get(char **addr, uint16_t *port, struct httpd_connection *conn);
|
httpd_connection_peer_get(char **addr, uint16_t *port, httpd_connection *conn);
|
||||||
*/
|
|
||||||
void
|
void
|
||||||
httpd_connection_free(httpd_connection *conn);
|
httpd_connection_free(httpd_connection *conn);
|
||||||
|
|
||||||
@ -244,10 +247,10 @@ httpd_request_connection_get(struct httpd_request *hreq);
|
|||||||
/*
|
/*
|
||||||
const char *
|
const char *
|
||||||
httpd_request_uri_get(struct httpd_request *hreq);
|
httpd_request_uri_get(struct httpd_request *hreq);
|
||||||
|
*/
|
||||||
int
|
int
|
||||||
httpd_request_peer_get(char **addr, uint16_t *port, struct httpd_request *hreq);
|
httpd_request_peer_get(char **addr, uint16_t *port, struct httpd_request *hreq);
|
||||||
*/
|
|
||||||
int
|
int
|
||||||
httpd_request_method_get(enum httpd_methods *method, struct httpd_request *hreq);
|
httpd_request_method_get(enum httpd_methods *method, struct httpd_request *hreq);
|
||||||
|
|
||||||
@ -256,10 +259,10 @@ httpd_request_backend_free(struct httpd_request *hreq);
|
|||||||
|
|
||||||
int
|
int
|
||||||
httpd_request_closecb_set(struct httpd_request *hreq, httpd_connection_closecb cb, void *arg);
|
httpd_request_closecb_set(struct httpd_request *hreq, httpd_connection_closecb cb, void *arg);
|
||||||
/*
|
|
||||||
void
|
void
|
||||||
httpd_reply_send(struct httpd_request *hreq, int code, const char *reason, struct evbuffer *evbuf)
|
httpd_reply_backend_send(struct httpd_request *hreq, int code, const char *reason, struct evbuffer *evbuf);
|
||||||
*/
|
|
||||||
void
|
void
|
||||||
httpd_reply_start_send(struct httpd_request *hreq, int code, const char *reason);
|
httpd_reply_start_send(struct httpd_request *hreq, int code, const char *reason);
|
||||||
|
|
||||||
|
@ -11,13 +11,14 @@ httpd_connection_closecb_set(httpd_connection *conn, httpd_connection_closecb cb
|
|||||||
evhttp_connection_set_closecb(conn, cb, arg);
|
evhttp_connection_set_closecb(conn, cb, arg);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
int
|
int
|
||||||
httpd_connection_peer_get(char **addr, uint16_t *port, httpd_connection *conn)
|
httpd_connection_peer_get(char **addr, uint16_t *port, httpd_connection *conn)
|
||||||
{
|
{
|
||||||
return evhttp_connection_get_peer(conn, addr, port);
|
evhttp_connection_get_peer(conn, addr, port);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
void
|
void
|
||||||
httpd_connection_free(httpd_connection *conn)
|
httpd_connection_free(httpd_connection *conn)
|
||||||
{
|
{
|
||||||
@ -27,7 +28,7 @@ httpd_connection_free(httpd_connection *conn)
|
|||||||
httpd_connection *
|
httpd_connection *
|
||||||
httpd_request_connection_get(struct httpd_request *hreq)
|
httpd_request_connection_get(struct httpd_request *hreq)
|
||||||
{
|
{
|
||||||
return evhttp_request_get_connection(hreq->req);
|
return evhttp_request_get_connection(hreq->backend);
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
const char *
|
const char *
|
||||||
@ -35,21 +36,21 @@ httpd_request_uri_get(httpd_request *req)
|
|||||||
{
|
{
|
||||||
return evhttp_request_get_uri(req);
|
return evhttp_request_get_uri(req);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
int
|
int
|
||||||
httpd_request_peer_get(char **addr, uint16_t *port, httpd_request *req)
|
httpd_request_peer_get(char **addr, uint16_t *port, struct httpd_request *hreq)
|
||||||
{
|
{
|
||||||
httpd_connection *conn = httpd_request_connection_get(req);
|
httpd_connection *conn = httpd_request_connection_get(hreq->backend);
|
||||||
if (!conn)
|
if (!conn)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return httpd_connection_peer_get(addr, port, conn);
|
return httpd_connection_peer_get(addr, port, conn);
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
int
|
int
|
||||||
httpd_request_method_get(enum httpd_methods *method, struct httpd_request *hreq)
|
httpd_request_method_get(enum httpd_methods *method, struct httpd_request *hreq)
|
||||||
{
|
{
|
||||||
enum evhttp_cmd_type cmd = evhttp_request_get_command(hreq->req);
|
enum evhttp_cmd_type cmd = evhttp_request_get_command(hreq->backend);
|
||||||
|
|
||||||
switch (cmd)
|
switch (cmd)
|
||||||
{
|
{
|
||||||
@ -71,7 +72,7 @@ httpd_request_method_get(enum httpd_methods *method, struct httpd_request *hreq)
|
|||||||
void
|
void
|
||||||
httpd_request_backend_free(struct httpd_request *hreq)
|
httpd_request_backend_free(struct httpd_request *hreq)
|
||||||
{
|
{
|
||||||
evhttp_request_free(hreq->req);
|
evhttp_request_free(hreq->backend);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
@ -101,6 +102,12 @@ httpd_query_iterate(httpd_query *query, httpd_query_iteratecb cb, void *arg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
httpd_query_clear(httpd_query *query)
|
||||||
|
{
|
||||||
|
evhttp_clear_headers(query);
|
||||||
|
}
|
||||||
|
|
||||||
const char *
|
const char *
|
||||||
httpd_header_find(httpd_headers *headers, const char *key)
|
httpd_header_find(httpd_headers *headers, const char *key)
|
||||||
{
|
{
|
||||||
@ -128,36 +135,35 @@ httpd_headers_clear(httpd_headers *headers)
|
|||||||
httpd_headers *
|
httpd_headers *
|
||||||
httpd_request_input_headers_get(struct httpd_request *hreq)
|
httpd_request_input_headers_get(struct httpd_request *hreq)
|
||||||
{
|
{
|
||||||
return evhttp_request_get_input_headers(hreq->req);
|
return evhttp_request_get_input_headers(hreq->backend);
|
||||||
}
|
}
|
||||||
|
|
||||||
httpd_headers *
|
httpd_headers *
|
||||||
httpd_request_output_headers_get(struct httpd_request *hreq)
|
httpd_request_output_headers_get(struct httpd_request *hreq)
|
||||||
{
|
{
|
||||||
return evhttp_request_get_output_headers(hreq->req);
|
return evhttp_request_get_output_headers(hreq->backend);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
void
|
void
|
||||||
httpd_reply_send(httpd_request *req, int code, const char *reason, struct evbuffer *evbuf)
|
httpd_reply_backend_send(struct httpd_request *hreq, int code, const char *reason, struct evbuffer *evbuf)
|
||||||
{
|
{
|
||||||
evhttp_send_reply(req, code, reason, evbuf);
|
evhttp_send_reply(hreq->backend, code, reason, evbuf);
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
void
|
void
|
||||||
httpd_reply_start_send(struct httpd_request *hreq, int code, const char *reason)
|
httpd_reply_start_send(struct httpd_request *hreq, int code, const char *reason)
|
||||||
{
|
{
|
||||||
evhttp_send_reply_start(hreq->req, code, reason);
|
evhttp_send_reply_start(hreq->backend, code, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
httpd_reply_chunk_send(struct httpd_request *hreq, struct evbuffer *evbuf, httpd_connection_chunkcb cb, void *arg)
|
httpd_reply_chunk_send(struct httpd_request *hreq, struct evbuffer *evbuf, httpd_connection_chunkcb cb, void *arg)
|
||||||
{
|
{
|
||||||
evhttp_send_reply_chunk_with_cb(hreq->req, evbuf, cb, arg);
|
evhttp_send_reply_chunk_with_cb(hreq->backend, evbuf, cb, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
httpd_reply_end_send(struct httpd_request *hreq)
|
httpd_reply_end_send(struct httpd_request *hreq)
|
||||||
{
|
{
|
||||||
evhttp_send_reply_end(hreq->req);
|
evhttp_send_reply_end(hreq->backend);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user