[httpd] Drop libevhtp backend

Despite that it seemingly supports multithreading it picks threads that are
busy, and not sure how that can be prevented. Also libevhtp is currently
unmaintained.
This commit is contained in:
ejurgensen 2023-01-09 22:02:53 +01:00
parent 74f1b93b42
commit 68d66c3229
4 changed files with 3 additions and 493 deletions

View File

@ -268,11 +268,6 @@ OWNTONE_ARG_WITH_CHECK([OWNTONE_OPTS], [libevent_pthreads support],
[libevent_pthreads], [LIBEVENT_PTHREADS], [libevent_pthreads],
[evthread_use_pthreads], [event2/thread.h])
dnl Build with libevhtp
OWNTONE_ARG_WITH_CHECK([OWNTONE_OPTS], [libevhtp support], [libevhtp], [LIBEVHTP],
[evhtp])
AM_CONDITIONAL([COND_LIBEVHTP], [[test "x$with_libevhtp" = "xyes"]])
dnl Build with Avahi (or Bonjour if not)
OWNTONE_ARG_WITH_CHECK([OWNTONE_OPTS], [Avahi mDNS], [avahi], [AVAHI],
[avahi-client >= 0.6.24], [avahi_client_new], [avahi-client/client.h])

View File

@ -46,12 +46,6 @@ if COND_LIBWEBSOCKETS
LIBWEBSOCKETS_SRC=websocket.c websocket.h
endif
if COND_LIBEVHTP
HTTPDBACKEND_SRC=httpd_libevhtp.c
else
HTTPDBACKEND_SRC=httpd_libevhttp.c
endif
GPERF_FILES = \
daap_query.gperf \
dacp_prop.gperf \
@ -98,7 +92,7 @@ owntone_SOURCES = main.c \
library.c library.h \
$(MDNS_SRC) mdns.h \
remote_pairing.c remote_pairing.h \
$(HTTPDBACKEND_SRC) \
httpd_libevhttp.c \
httpd.c httpd.h httpd_internal.h \
httpd_rsp.c \
httpd_daap.c httpd_daap.h \

View File

@ -39,23 +39,8 @@
struct httpd_request;
#ifdef HAVE_LIBEVHTP
struct evhtp_s;
struct evhtp_connection_s;
struct evhtp_request_s;
struct evhtp_kvs_s;
struct httpd_uri_parsed;
struct httpd_backend_data;
typedef struct evhtp_s httpd_server;
typedef struct evhtp_connection_s httpd_connection;
typedef struct evhtp_request_s httpd_backend;
typedef struct evhtp_kvs_s httpd_headers;
typedef struct evhtp_kvs_s httpd_query;
typedef struct httpd_uri_parsed httpd_uri_parsed;
typedef struct httpd_backend_data httpd_backend_data;
#else
// Declaring here instead of including event2/http.h makes it easier to support
// other backends than evhttp in the future, e.g. libevhtp
struct evhttp;
struct evhttp_connection;
struct evhttp_request;
@ -69,7 +54,6 @@ typedef struct evkeyvalq httpd_headers;
typedef struct evkeyvalq httpd_query;
typedef struct httpd_uri_parsed httpd_uri_parsed;
typedef void httpd_backend_data; // Not used for evhttp
#endif
typedef char *httpd_uri_path_parts[31];
typedef void (*httpd_general_cb)(httpd_backend *backend, void *arg);

View File

@ -1,463 +0,0 @@
#include <string.h>
#include <evhtp.h>
#include "misc.h"
#include "httpd_internal.h"
#include "logger.h"
struct httpd_backend_data
{
char peer_address[32];
uint16_t peer_port;
httpd_connection_closecb closecb;
void *closecb_arg;
char *uri;
};
struct httpd_uri_parsed
{
evhtp_uri_t *ev_uri;
bool ev_uri_is_standalone; // true if ev_uri was allocated without a request, but via _fromuri
unsigned char *path_parts_buffer; // Allocated to hold the path parts in one buffer
httpd_uri_path_parts path_parts;
};
const char *
httpd_query_value_find(httpd_query *query, const char *key)
{
return evhtp_kv_find(query, key);
}
void
httpd_query_iterate(httpd_query *query, httpd_query_iteratecb cb, void *arg)
{
evhtp_kv_t *param;
TAILQ_FOREACH(param, query, next)
{
cb(param->key, param->val, arg);
}
}
void
httpd_query_clear(httpd_query *query)
{
evhtp_kv_t *param;
TAILQ_FOREACH(param, query, next)
{
evhtp_kv_rm_and_free(query, param);
}
}
const char *
httpd_header_find(httpd_headers *headers, const char *key)
{
return evhtp_header_find(headers, key);
}
void
httpd_header_remove(httpd_headers *headers, const char *key)
{
evhtp_header_rm_and_free(headers, evhtp_headers_find_header(headers, key));
}
void
httpd_header_add(httpd_headers *headers, const char *key, const char *val)
{
evhtp_header_t *header = evhtp_header_new(key, val, 1, 1); // 1, 1 = Copy key/val
evhtp_headers_add_header(headers, header);
}
void
httpd_headers_clear(httpd_headers *headers)
{
evhtp_kv_t *param;
TAILQ_FOREACH(param, headers, next)
{
evhtp_kv_rm_and_free(headers, param);
}
}
void
httpd_connection_free(httpd_connection *conn)
{
if (!conn)
return;
evhtp_connection_free(conn);
}
httpd_connection *
httpd_request_connection_get(struct httpd_request *hreq)
{
return evhtp_request_get_connection(hreq->backend);
}
void
httpd_request_backend_free(struct httpd_request *hreq)
{
evhtp_request_free(hreq->backend);
}
static short unsigned
closecb_wrapper(httpd_connection *conn, void *arg)
{
httpd_backend_data *backend_data = arg;
backend_data->closecb(conn, backend_data->closecb_arg);
return 0;
}
int
httpd_request_closecb_set(struct httpd_request *hreq, httpd_connection_closecb cb, void *arg)
{
httpd_connection *conn;
hreq->backend_data->closecb = cb;
hreq->backend_data->closecb_arg = arg;
conn = httpd_request_connection_get(hreq);
if (conn)
return -1;
if (!cb)
return evhtp_connection_unset_hook(conn, evhtp_hook_on_connection_fini);
return evhtp_connection_set_hook(conn, evhtp_hook_on_connection_fini, closecb_wrapper, hreq->backend_data);
}
struct event_base *
httpd_request_evbase_get(struct httpd_request *hreq)
{
httpd_connection *conn = httpd_request_connection_get(hreq);
if (conn)
return NULL;
return conn->evbase;
}
void
httpd_server_free(httpd_server *server)
{
if (!server)
return;
evhtp_free(server);
}
httpd_server *
httpd_server_new(struct event_base *evbase, unsigned short port, httpd_general_cb cb, void *arg)
{
evhtp_t *server;
int fd;
server = evhtp_new(evbase, NULL);
if (!server)
goto error;
fd = net_bind(&port, SOCK_STREAM | SOCK_NONBLOCK, "httpd");
if (fd < 0)
goto error;
if (evhtp_accept_socket(server, fd, -1) != 0)
goto error;
evhtp_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)
{
}
httpd_backend_data *
httpd_backend_data_create(httpd_backend *backend)
{
httpd_backend_data *backend_data;
backend_data = calloc(1, sizeof(httpd_backend_data));
if (!backend_data)
return NULL;
return backend_data;
}
void
httpd_backend_data_free(httpd_backend_data *backend_data)
{
free(backend_data->uri);
free(backend_data);
}
void
httpd_backend_reply_send(httpd_backend *backend, int code, const char *reason, struct evbuffer *evbuf)
{
if (evbuf)
evbuffer_add_buffer(backend->buffer_out, evbuf);
evhtp_send_reply(backend, code);
}
void
httpd_backend_reply_start_send(httpd_backend *backend, int code, const char *reason)
{
evhtp_send_reply_chunk_start(backend, code);
}
void
httpd_backend_reply_chunk_send(httpd_backend *backend, struct evbuffer *evbuf, httpd_connection_chunkcb cb, void *arg)
{
// TODO
}
void
httpd_backend_reply_end_send(httpd_backend *backend)
{
evhtp_send_reply_chunk_end(backend);
}
httpd_connection *
httpd_backend_connection_get(httpd_backend *backend)
{
return evhtp_request_get_connection(backend);
}
const char *
httpd_backend_uri_get(httpd_backend *backend, httpd_backend_data *backend_data)
{
evhtp_uri_t *uri = backend->uri;
if (!uri || !uri->path)
return NULL;
free(backend_data->uri);
if (backend->uri->query_raw)
backend_data->uri = safe_asprintf("%s?%s", uri->path->full, backend->uri->query_raw);
else
backend_data->uri = safe_asprintf("%s", uri->path->full);
return (const char *)backend_data->uri;
}
httpd_headers *
httpd_backend_input_headers_get(httpd_backend *backend)
{
return backend->headers_in;
}
httpd_headers *
httpd_backend_output_headers_get(httpd_backend *backend)
{
return backend->headers_out;
}
struct evbuffer *
httpd_backend_input_buffer_get(httpd_backend *backend)
{
return backend->buffer_in;
}
int
httpd_backend_peer_get(const char **addr, uint16_t *port, httpd_backend *backend, httpd_backend_data *backend_data)
{
httpd_connection *conn;
union net_sockaddr naddr;
socklen_t sa_len = sizeof(naddr);
*addr = NULL;
*port = 0;
conn = evhtp_request_get_connection(backend);
if (!conn)
return -1;
// We cannot use conn->saddr as we don't have the size, so it won't work for ipv6
getpeername(conn->sock, &naddr.sa, &sa_len);
net_address_get(backend_data->peer_address, sizeof(backend_data->peer_address), &naddr);
net_port_get(&backend_data->peer_port, &naddr);
*addr = backend_data->peer_address;
*port = backend_data->peer_port;
return 0;
}
int
httpd_backend_method_get(enum httpd_methods *method, httpd_backend *backend)
{
htp_method cmd = evhtp_request_get_method(backend);
switch (cmd)
{
case htp_method_GET: *method = HTTPD_METHOD_GET; break;
case htp_method_POST: *method = HTTPD_METHOD_POST; break;
case htp_method_HEAD: *method = HTTPD_METHOD_HEAD; break;
case htp_method_PUT: *method = HTTPD_METHOD_PUT; break;
case htp_method_DELETE: *method = HTTPD_METHOD_DELETE; break;
case htp_method_OPTIONS: *method = HTTPD_METHOD_OPTIONS; break;
case htp_method_TRACE: *method = HTTPD_METHOD_TRACE; break;
case htp_method_CONNECT: *method = HTTPD_METHOD_CONNECT; break;
case htp_method_PATCH: *method = HTTPD_METHOD_PATCH; break;
default: *method = HTTPD_METHOD_GET; return -1;
}
return 0;
}
void
httpd_backend_preprocess(httpd_backend *backend)
{
// Nothing to do here
}
static int
query_decode(evhtp_kvs_t **query)
{
evhtp_kvs_t *query_decoded;
evhtp_kv_t *encoded;
evhtp_kv_t *decoded;
char buf[2048];
unsigned char *out;
size_t val_size;
query_decoded = evhtp_kvs_new();
if (!query_decoded)
return -1;
TAILQ_FOREACH(encoded, *query, next)
{
// Must include zero terminator in length or output won't be terminated
// (not very clear from evhtp docs)
val_size = strlen(encoded->val) + 1;
if (val_size > sizeof(buf))
continue;
// Isn't done by evhtp_unescape_string :-(
safe_snreplace(encoded->val, val_size, "+", " ");
out = (unsigned char *)buf;
evhtp_unescape_string(&out, (unsigned char *)encoded->val, val_size);
decoded = evhtp_kv_new(encoded->key, buf, 1, 1); // 1, 1 = Copy key/val
evhtp_kvs_add_kv(query_decoded, decoded);
}
evhtp_kvs_free(*query);
*query = query_decoded;
return 0;
}
httpd_uri_parsed *
httpd_uri_parsed_create(httpd_backend *backend)
{
httpd_uri_parsed *parsed = NULL;
char *path = NULL;
size_t path_len;
char *path_part;
off_t path_part_offset;
char *ptr;
unsigned char *unescaped_part;
int i;
if (!backend->uri->path->path) // Not sure if this can happen
goto error;
path_len = strlen(backend->uri->path->path);
if (path_len == 0)
goto error;
path = strdup(backend->uri->path->path);
if (!path)
goto error;
parsed = calloc(1, sizeof(struct httpd_uri_parsed));
if (!parsed)
goto error;
// Pointers of parsed->path_parts will point into this buffer, so it will hold
// the uri decoded path parts separated by zeroes
parsed->path_parts_buffer = calloc(1, path_len + 1);
if (!parsed->path_parts_buffer)
goto error;
parsed->ev_uri = backend->uri;
path_part = strtok_r(path, "/", &ptr);
path_part_offset = path_part - path;
for (i = 0; (i < ARRAY_SIZE(parsed->path_parts) && path_part); i++)
{
// libevhtp's evhtp_unescape_string() is wonky (and feels unsafe...), for
// some reason it wants a double pointer to a user allocated buffer.
unescaped_part = parsed->path_parts_buffer + (path_part - path) - path_part_offset;
parsed->path_parts[i] = (char *)unescaped_part;
evhtp_unescape_string(&unescaped_part, (unsigned char *)path_part, strlen(path_part));
path_part = strtok_r(NULL, "/", &ptr);
}
// If "path_part" is not NULL, we have path tokens that could not be parsed into the "parsed->path_parts" array
if (path_part)
goto error;
// uri->query isn't uri decoded, so we replace it with one that is
if (backend->uri->query)
query_decode(&backend->uri->query);
free(path);
return parsed;
error:
httpd_uri_parsed_free(parsed);
free(path);
return NULL;
}
httpd_uri_parsed *
httpd_uri_parsed_create_fromuri(const char *uri)
{
// TODO
return NULL;
}
void
httpd_uri_parsed_free(httpd_uri_parsed *parsed)
{
if (!parsed)
return;
// TODO
// if (parsed->ev_uri_is_standalone)
// free ev_uri;
free(parsed->path_parts_buffer);
free(parsed);
}
httpd_query *
httpd_uri_query_get(httpd_uri_parsed *parsed)
{
return parsed->ev_uri->query;
}
const char *
httpd_uri_path_get(httpd_uri_parsed *parsed)
{
if (!parsed->ev_uri->path)
return NULL;
return parsed->ev_uri->path->full;
}
void
httpd_uri_path_parts_get(httpd_uri_path_parts *path_parts, httpd_uri_parsed *parsed)
{
memcpy(path_parts, parsed->path_parts, sizeof(httpd_uri_path_parts));
}