mirror of
https://github.com/owntone/owntone-server.git
synced 2025-11-09 13:39:47 -05:00
[http] Make libcurl a hard requirement
Always using libcurl makes the code simpler, plus makes sure we always have a https client
This commit is contained in:
286
src/http.c
286
src/http.c
@@ -36,9 +36,7 @@
|
||||
|
||||
#include <event2/event.h>
|
||||
|
||||
#ifdef HAVE_LIBCURL
|
||||
#include <curl/curl.h>
|
||||
#endif
|
||||
|
||||
#include "http.h"
|
||||
#include "httpd.h"
|
||||
@@ -56,259 +54,6 @@
|
||||
// Number of seconds the client will wait for a response before aborting
|
||||
#define HTTP_CLIENT_TIMEOUT 8
|
||||
|
||||
/* The strict libevent api does not permit walking through an evkeyvalq and saving
|
||||
* all the http headers, so we predefine what we are looking for. You can add
|
||||
* extra headers here that you would like to save.
|
||||
*/
|
||||
static char *header_list[] =
|
||||
{
|
||||
"icy-name",
|
||||
"icy-description",
|
||||
"icy-metaint",
|
||||
"icy-genre",
|
||||
"Content-Type",
|
||||
};
|
||||
|
||||
/* Copies headers we are searching for from one keyval struct to another
|
||||
*
|
||||
*/
|
||||
static void
|
||||
headers_save(struct keyval *kv, struct evkeyvalq *headers)
|
||||
{
|
||||
const char *value;
|
||||
uint8_t *utf;
|
||||
int i;
|
||||
|
||||
if (!kv || !headers)
|
||||
return;
|
||||
|
||||
for (i = 0; i < (sizeof(header_list) / sizeof(header_list[0])); i++)
|
||||
if ( (value = evhttp_find_header(headers, header_list[i])) )
|
||||
{
|
||||
utf = u8_strconv_from_encoding(value, "ISO−8859−1", iconveh_question_mark);
|
||||
if (!utf)
|
||||
continue;
|
||||
|
||||
keyval_add(kv, header_list[i], (char *)utf);
|
||||
free(utf);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
request_cb(struct evhttp_request *req, void *arg)
|
||||
{
|
||||
struct http_client_ctx *ctx;
|
||||
const char *response_code_line;
|
||||
|
||||
ctx = (struct http_client_ctx *)arg;
|
||||
|
||||
if (ctx->headers_only)
|
||||
{
|
||||
ctx->ret = 0;
|
||||
|
||||
event_base_loopbreak(ctx->evbase);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!req)
|
||||
{
|
||||
DPRINTF(E_WARN, L_HTTP, "Connection to %s failed: Connection timed out\n", ctx->url);
|
||||
goto connection_error;
|
||||
}
|
||||
|
||||
ctx->response_code = evhttp_request_get_response_code(req);
|
||||
#ifndef HAVE_LIBEVENT2_OLD
|
||||
response_code_line = evhttp_request_get_response_code_line(req);
|
||||
#else
|
||||
response_code_line = "no error text";
|
||||
#endif
|
||||
|
||||
if (ctx->response_code == 0)
|
||||
{
|
||||
DPRINTF(E_WARN, L_HTTP, "Connection to %s failed: Connection refused\n", ctx->url);
|
||||
goto connection_error;
|
||||
}
|
||||
else if (ctx->response_code != 200)
|
||||
{
|
||||
DPRINTF(E_WARN, L_HTTP, "Connection to %s failed: %s (error %d)\n", ctx->url, response_code_line, ctx->response_code);
|
||||
goto connection_error;
|
||||
}
|
||||
|
||||
ctx->ret = 0;
|
||||
|
||||
if (ctx->input_headers)
|
||||
headers_save(ctx->input_headers, evhttp_request_get_input_headers(req));
|
||||
if (ctx->input_body)
|
||||
evbuffer_add_buffer(ctx->input_body, evhttp_request_get_input_buffer(req));
|
||||
|
||||
event_base_loopbreak(ctx->evbase);
|
||||
|
||||
return;
|
||||
|
||||
connection_error:
|
||||
|
||||
ctx->ret = -1;
|
||||
|
||||
event_base_loopbreak(ctx->evbase);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* This callback is only invoked if ctx->headers_only is set. Since that means
|
||||
* we only want headers, it will always return -1 to make evhttp close the
|
||||
* connection. The headers will be saved in a keyval struct in ctx, since we
|
||||
* cannot address the *evkeyvalq after the connection is free'd.
|
||||
*/
|
||||
#ifndef HAVE_LIBEVENT2_OLD
|
||||
static int
|
||||
request_header_cb(struct evhttp_request *req, void *arg)
|
||||
{
|
||||
struct http_client_ctx *ctx;
|
||||
|
||||
ctx = (struct http_client_ctx *)arg;
|
||||
|
||||
if (!ctx->input_headers)
|
||||
{
|
||||
DPRINTF(E_LOG, L_HTTP, "BUG: Header callback invoked but caller did not say where to save the headers\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
headers_save(ctx->input_headers, evhttp_request_get_input_headers(req));
|
||||
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
http_client_request_impl(struct http_client_ctx *ctx)
|
||||
{
|
||||
struct evhttp_connection *evcon;
|
||||
struct evhttp_request *req;
|
||||
struct evkeyvalq *headers;
|
||||
struct evbuffer *output_buffer;
|
||||
struct onekeyval *okv;
|
||||
enum evhttp_cmd_type method;
|
||||
const char *user_agent;
|
||||
char host[512];
|
||||
char host_port[1024];
|
||||
char path[2048];
|
||||
char tmp[128];
|
||||
int port;
|
||||
int ret;
|
||||
|
||||
ctx->ret = -1;
|
||||
|
||||
av_url_split(NULL, 0, NULL, 0, host, sizeof(host), &port, path, sizeof(path), ctx->url);
|
||||
if (strlen(host) == 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_HTTP, "Error extracting hostname from URL: %s\n", ctx->url);
|
||||
|
||||
return ctx->ret;
|
||||
}
|
||||
|
||||
if (port <= 0)
|
||||
snprintf(host_port, sizeof(host_port), "%s", host);
|
||||
else
|
||||
snprintf(host_port, sizeof(host_port), "%s:%d", host, port);
|
||||
|
||||
if (port <= 0)
|
||||
port = 80;
|
||||
|
||||
if (strlen(path) == 0)
|
||||
{
|
||||
path[0] = '/';
|
||||
path[1] = '\0';
|
||||
}
|
||||
|
||||
ctx->evbase = event_base_new();
|
||||
if (!ctx->evbase)
|
||||
{
|
||||
DPRINTF(E_LOG, L_HTTP, "Could not create or find http client event base\n");
|
||||
|
||||
return ctx->ret;
|
||||
}
|
||||
|
||||
evcon = evhttp_connection_base_new(ctx->evbase, NULL, host, (unsigned short)port);
|
||||
if (!evcon)
|
||||
{
|
||||
DPRINTF(E_LOG, L_HTTP, "Could not create connection to %s\n", host_port);
|
||||
|
||||
event_base_free(ctx->evbase);
|
||||
return ctx->ret;
|
||||
}
|
||||
|
||||
evhttp_connection_set_timeout(evcon, HTTP_CLIENT_TIMEOUT);
|
||||
|
||||
/* Set up request */
|
||||
req = evhttp_request_new(request_cb, ctx);
|
||||
if (!req)
|
||||
{
|
||||
DPRINTF(E_LOG, L_HTTP, "Could not create request to %s\n", host_port);
|
||||
|
||||
evhttp_connection_free(evcon);
|
||||
event_base_free(ctx->evbase);
|
||||
return ctx->ret;
|
||||
}
|
||||
|
||||
#ifndef HAVE_LIBEVENT2_OLD
|
||||
if (ctx->headers_only)
|
||||
evhttp_request_set_header_cb(req, request_header_cb);
|
||||
#endif
|
||||
|
||||
headers = evhttp_request_get_output_headers(req);
|
||||
evhttp_add_header(headers, "Host", host_port);
|
||||
|
||||
user_agent = cfg_getstr(cfg_getsec(cfg, "general"), "user_agent");
|
||||
evhttp_add_header(headers, "User-Agent", user_agent);
|
||||
|
||||
evhttp_add_header(headers, "Icy-MetaData", "1");
|
||||
|
||||
if (ctx->output_headers)
|
||||
{
|
||||
for (okv = ctx->output_headers->head; okv; okv = okv->next)
|
||||
evhttp_add_header(headers, okv->name, okv->value);
|
||||
}
|
||||
|
||||
if (ctx->output_body)
|
||||
{
|
||||
output_buffer = evhttp_request_get_output_buffer(req);
|
||||
evbuffer_add(output_buffer, ctx->output_body, strlen(ctx->output_body));
|
||||
evbuffer_add_printf(output_buffer, "\n");
|
||||
snprintf(tmp, sizeof(tmp), "%zu", evbuffer_get_length(output_buffer));
|
||||
evhttp_add_header(headers, "Content-Length", tmp);
|
||||
method = EVHTTP_REQ_POST;
|
||||
}
|
||||
else
|
||||
{
|
||||
evhttp_add_header(headers, "Content-Length", "0");
|
||||
method = EVHTTP_REQ_GET;
|
||||
}
|
||||
|
||||
/* Make request */
|
||||
DPRINTF(E_INFO, L_HTTP, "Making %s request for http://%s%s\n", ((method==EVHTTP_REQ_GET) ? "GET" : "POST"), host_port, path);
|
||||
|
||||
ret = evhttp_make_request(evcon, req, method, path);
|
||||
if (ret < 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_HTTP, "Error making request for http://%s%s\n", host_port, path);
|
||||
|
||||
evhttp_connection_free(evcon);
|
||||
event_base_free(ctx->evbase);
|
||||
return ctx->ret;
|
||||
}
|
||||
|
||||
event_base_dispatch(ctx->evbase);
|
||||
|
||||
evhttp_connection_free(evcon);
|
||||
event_base_free(ctx->evbase);
|
||||
|
||||
return ctx->ret;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBCURL
|
||||
|
||||
static void
|
||||
curl_headers_save(struct keyval *kv, CURL *curl)
|
||||
{
|
||||
@@ -348,8 +93,8 @@ curl_request_cb(char *ptr, size_t size, size_t nmemb, void *userdata)
|
||||
return realsize;
|
||||
}
|
||||
|
||||
static int
|
||||
https_client_request_impl(struct http_client_ctx *ctx)
|
||||
int
|
||||
http_client_request(struct http_client_ctx *ctx)
|
||||
{
|
||||
CURL *curl;
|
||||
CURLcode res;
|
||||
@@ -370,7 +115,8 @@ https_client_request_impl(struct http_client_ctx *ctx)
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, user_agent);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, ctx->url);
|
||||
|
||||
headers = NULL;
|
||||
snprintf(header, sizeof(header), "Icy-MetaData: 1");
|
||||
headers = curl_slist_append(NULL, header);
|
||||
if (ctx->output_headers)
|
||||
{
|
||||
for (okv = ctx->output_headers->head; okv; okv = okv->next)
|
||||
@@ -378,12 +124,13 @@ https_client_request_impl(struct http_client_ctx *ctx)
|
||||
snprintf(header, sizeof(header), "%s: %s", okv->name, okv->value);
|
||||
headers = curl_slist_append(headers, header);
|
||||
}
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
|
||||
}
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
|
||||
|
||||
if (ctx->output_body)
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, ctx->output_body);
|
||||
if (ctx->headers_only)
|
||||
curl_easy_setopt(curl, CURLOPT_NOBODY, 1L); // Makes curl make a HEAD request
|
||||
else if (ctx->output_body)
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, ctx->output_body); // POST request
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, HTTP_CLIENT_TIMEOUT);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_request_cb);
|
||||
@@ -414,21 +161,6 @@ https_client_request_impl(struct http_client_ctx *ctx)
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* HAVE_LIBCURL */
|
||||
|
||||
int
|
||||
http_client_request(struct http_client_ctx *ctx)
|
||||
{
|
||||
if (strncmp(ctx->url, "http:", strlen("http:")) == 0)
|
||||
return http_client_request_impl(ctx);
|
||||
#ifdef HAVE_LIBCURL
|
||||
if (strncmp(ctx->url, "https:", strlen("https:")) == 0)
|
||||
return https_client_request_impl(ctx);
|
||||
#endif
|
||||
|
||||
DPRINTF(E_LOG, L_HTTP, "Request for %s is not supported (not built with libcurl?)\n", ctx->url);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *
|
||||
http_form_urlencode(struct keyval *kv)
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
#include <libavutil/log.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavfilter/avfilter.h>
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <gcrypt.h>
|
||||
@@ -75,10 +76,6 @@ GCRY_THREAD_OPTION_PTHREAD_IMPL;
|
||||
# include "lastfm.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBCURL
|
||||
# include <curl/curl.h>
|
||||
#endif
|
||||
|
||||
#define PIDFILE STATEDIR "/run/" PACKAGE ".pid"
|
||||
#define WEB_ROOT DATADIR "/htdocs"
|
||||
|
||||
@@ -684,10 +681,8 @@ main(int argc, char **argv)
|
||||
#endif
|
||||
av_log_set_callback(logger_ffmpeg);
|
||||
|
||||
#ifdef HAVE_LIBCURL
|
||||
/* Initialize libcurl */
|
||||
curl_global_init(CURL_GLOBAL_DEFAULT);
|
||||
#endif
|
||||
|
||||
/* Initialize libgcrypt */
|
||||
gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
|
||||
@@ -991,9 +986,7 @@ main(int argc, char **argv)
|
||||
|
||||
signal_block_fail:
|
||||
gcrypt_init_fail:
|
||||
#ifdef HAVE_LIBCURL
|
||||
curl_global_cleanup();
|
||||
#endif
|
||||
#if HAVE_DECL_AVFORMAT_NETWORK_INIT
|
||||
avformat_network_deinit();
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user