[http/spotify] Add support for http sessions across multiple requests

This change allows to reuse curl handles for multiple requests.
Reusing a curl handle improves performance if more than one request is
made against a service (connection, session cache, dns cache are kept
between requests).
This commit is contained in:
chme 2019-02-09 17:04:25 +01:00
parent 8531a27235
commit d277f7c7b3
8 changed files with 63 additions and 18 deletions

View File

@ -439,7 +439,7 @@ artwork_read_byurl(struct evbuffer *evbuf, const char *url)
client.input_headers = kv;
client.input_body = evbuf;
ret = http_client_request(&client);
ret = http_client_request(&client, NULL);
if (ret < 0)
{
DPRINTF(E_LOG, L_ART, "Request to '%s' failed with return value %d\n", url, ret);
@ -1288,7 +1288,7 @@ online_source_search(struct online_source *src, struct artwork_ctx *ctx)
client.url = url;
client.output_headers = &output_headers;
ret = http_client_request(&client);
ret = http_client_request(&client, NULL);
keyval_clear(&output_headers);
if (ret < 0 || client.response_code != HTTP_OK)
{

View File

@ -55,6 +55,19 @@
// Number of seconds the client will wait for a response before aborting
#define HTTP_CLIENT_TIMEOUT 8
void
http_client_session_init(struct http_client_session *session)
{
session->curl = curl_easy_init();
}
void
http_client_session_deinit(struct http_client_session *session)
{
curl_easy_cleanup(session->curl);
}
static void
curl_headers_save(struct keyval *kv, CURL *curl)
{
@ -95,7 +108,7 @@ curl_request_cb(char *ptr, size_t size, size_t nmemb, void *userdata)
}
int
http_client_request(struct http_client_ctx *ctx)
http_client_request(struct http_client_ctx *ctx, struct http_client_session *session)
{
CURL *curl;
CURLcode res;
@ -106,7 +119,15 @@ http_client_request(struct http_client_ctx *ctx)
char header[1024];
long response_code;
curl = curl_easy_init();
if (session)
{
curl = session->curl;
curl_easy_reset(curl);
}
else
{
curl = curl_easy_init();
}
if (!curl)
{
DPRINTF(E_LOG, L_HTTP, "Error: Could not get curl handle\n");
@ -153,7 +174,10 @@ http_client_request(struct http_client_ctx *ctx)
{
DPRINTF(E_LOG, L_HTTP, "Request to %s failed: %s\n", ctx->url, curl_easy_strerror(res));
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
if (!session)
{
curl_easy_cleanup(curl);
}
return -1;
}
@ -162,7 +186,10 @@ http_client_request(struct http_client_ctx *ctx)
curl_headers_save(ctx->input_headers, curl);
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
if (!session)
{
curl_easy_cleanup(curl);
}
return 0;
}
@ -262,7 +289,7 @@ http_stream_setup(char **stream, const char *url)
ctx.url = url;
ctx.input_body = evbuf;
ret = http_client_request(&ctx);
ret = http_client_request(&ctx, NULL);
if (ret < 0)
{
DPRINTF(E_LOG, L_HTTP, "Couldn't fetch internet playlist: %s\n", url);

View File

@ -4,10 +4,16 @@
#include <event2/buffer.h>
#include <event2/http.h>
#include <curl/curl.h>
#include "misc.h"
#include <libavformat/avformat.h>
struct http_client_session
{
CURL *curl;
};
struct http_client_ctx
{
/* Destination URL, header and body of outgoing request body. If output_body
@ -54,6 +60,11 @@ struct http_icy_metadata
uint32_t hash;
};
void
http_client_session_init(struct http_client_session *session);
void
http_client_session_deinit(struct http_client_session *session);
/* Make a http(s) request. We use libcurl to make https requests. We could use
* libevent and avoid the dependency, but for SSL, libevent needs to be v2.1
@ -63,7 +74,7 @@ struct http_icy_metadata
* @return 0 if successful, -1 if an error occurred (e.g. no libcurl)
*/
int
http_client_request(struct http_client_ctx *ctx);
http_client_request(struct http_client_ctx *ctx, struct http_client_session *session);
/* Converts the keyval dictionary to a application/x-www-form-urlencoded string.

View File

@ -194,7 +194,7 @@ streamurl_process(struct input_metadata *metadata, const char *url)
client.input_headers = &kv;
client.input_body = evbuf;
ret = http_client_request(&client);
ret = http_client_request(&client, NULL);
if (ret < 0 || client.response_code != HTTP_OK)
{
DPRINTF(E_WARN, L_PLAYER, "Request for StreamUrl resource '%s' failed, response code %d\n", url, client.response_code);

View File

@ -228,7 +228,7 @@ https_get_cb(char **out, const char *url)
ctx.url = url;
ctx.input_body = evbuffer_new();
ret = http_client_request(&ctx);
ret = http_client_request(&ctx, NULL);
if (ret < 0 || ctx.response_code != HTTP_OK)
{
DPRINTF(E_LOG, L_SPOTIFY, "Failed to AP list from '%s' (return %d, error code %d)\n", ctx.url, ret, ctx.response_code);

View File

@ -239,7 +239,7 @@ request_post(const char *url, struct keyval *kv, char **errmsg)
ctx.url = url;
ctx.input_body = evbuffer_new();
ret = http_client_request(&ctx);
ret = http_client_request(&ctx, NULL);
if (ret < 0)
goto out_free_ctx;

View File

@ -144,7 +144,7 @@ apple_rss_feedurl_get(const char *rss_url)
ctx.url = url;
ctx.input_body = evbuf;
ret = http_client_request(&ctx);
ret = http_client_request(&ctx, NULL);
if (ret < 0 || ctx.response_code != HTTP_OK)
{
evbuffer_free(evbuf);
@ -256,7 +256,7 @@ rss_xml_get(const char *url)
CHECK_NULL(L_LIB, ctx.input_body = evbuffer_new());
ctx.url = feedurl;
ret = http_client_request(&ctx);
ret = http_client_request(&ctx, NULL);
if (ret < 0 || ctx.response_code != HTTP_OK)
{
DPRINTF(E_LOG, L_LIB, "Failed to fetch RSS from '%s' (return %d, error code %d)\n", ctx.url, ret, ctx.response_code);

View File

@ -166,6 +166,7 @@ static const char *spotify_shows_uri = "https://api.spotify.com/v1/me/
static const char *spotify_shows_episodes_uri = "https://api.spotify.com/v1/shows/%s/episodes";
static const char *spotify_episode_uri = "https://api.spotify.com/v1/episodes/%s";
static struct http_client_session session = { 0 };
static enum spotify_item_type
parse_type_from_uri(const char *uri)
@ -256,7 +257,7 @@ request_access_tokens(struct keyval *kv, const char **err)
ctx.output_body = param;
ctx.input_body = evbuffer_new();
ret = http_client_request(&ctx);
ret = http_client_request(&ctx, NULL);
if (ret < 0)
{
*err = "Did not get a reply from Spotify";
@ -366,7 +367,7 @@ request_endpoint(const char *uri)
DPRINTF(E_DBG, L_SPOTIFY, "Request Spotify API endpoint: '%s')\n", uri);
ret = http_client_request(ctx);
ret = http_client_request(ctx, &session);
if (ret < 0)
{
DPRINTF(E_LOG, L_SPOTIFY, "Request for '%s' failed\n", uri);
@ -2321,10 +2322,15 @@ spotifywebapi_access_token_get(struct spotifywebapi_access_token *info)
static int
spotifywebapi_init()
{
CHECK_ERR(L_SPOTIFY, mutex_init(&token_lck));
int ret;
// Required for libspotify backend
return spotify_init();
CHECK_ERR(L_SPOTIFY, mutex_init(&token_lck));
ret = spotify_init();
if (ret < 0)
return -1;
http_client_session_init(&session);
return 0;
}
static void
@ -2333,6 +2339,7 @@ spotifywebapi_deinit()
CHECK_ERR(L_SPOTIFY, pthread_mutex_destroy(&token_lck));
spotify_deinit();
http_client_session_deinit(&session);
free_credentials();
}