From dd9e58a2e05dc4833f2d57a22581afc56012b9a4 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sat, 23 Nov 2013 21:43:45 +0000 Subject: [PATCH] http: added new HTTP client code to use CURL this is being used within IPTV code, and eventually I might make this a bit more generic so I can use from elsewhere, like imagecache. --- src/http.h | 21 +++- src/http/http_client.c | 248 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 267 insertions(+), 2 deletions(-) create mode 100644 src/http/http_client.c diff --git a/src/http.h b/src/http.h index 08878b46..acba6240 100644 --- a/src/http.h +++ b/src/http.h @@ -20,10 +20,14 @@ #define HTTP_H_ #include "htsbuf.h" +#include "url.h" TAILQ_HEAD(http_arg_list, http_arg); +typedef RB_HEAD(,http_arg) http_arg_tree_t; + typedef struct http_arg { + RB_ENTRY(http_arg) rb_link; TAILQ_ENTRY(http_arg) link; char *key; char *val; @@ -131,12 +135,25 @@ typedef struct http_path { http_path_t *http_path_add(const char *path, void *opaque, http_callback_t *callback, uint32_t accessmask); - - void http_server_init(const char *bindaddr); int http_access_verify(http_connection_t *hc, int mask); void http_deescape(char *s); +typedef struct http_client http_client_t; + +typedef void (http_client_conn_cb) (void *p); +typedef size_t (http_client_data_cb) (void *p, void *buf, size_t len); +typedef void (http_client_fail_cb) (void *p); + +void http_client_init ( void ); +http_client_t* +http_connect ( const url_t *url, + http_client_conn_cb conn_cb, + http_client_data_cb data_cb, + http_client_fail_cb fail_cb, + void *p ); +void http_close ( http_client_t *hc ); + #endif /* HTTP_H_ */ diff --git a/src/http/http_client.c b/src/http/http_client.c new file mode 100644 index 00000000..0448537e --- /dev/null +++ b/src/http/http_client.c @@ -0,0 +1,248 @@ +/* + * Tvheadend - HTTP client functions + * + * Copyright (C) 2013 Adam Sutton + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "tvheadend.h" +#include "tvhpoll.h" +#include "redblack.h" +#include "queue.h" +#include "url.h" +#include "http.h" + +#if ENABLE_CURL + +#include +#include +#include +#include + + +/* + * Client definition + */ +struct http_client +{ + CURL *hc_curl; + int hc_fd; + url_t hc_url; + int hc_begin; + + /* Callbacks */ + http_client_conn_cb *hc_conn; + http_client_data_cb *hc_data; + http_client_fail_cb *hc_fail; + void *hc_opaque; + + TAILQ_ENTRY(http_client) hc_link; +}; + +/* + * Global state + */ +static tvhpoll_t *http_poll; +static TAILQ_HEAD(,http_client) http_clients; +static pthread_mutex_t http_lock; +static CURLM *http_curlm; + +/* + * Disable + */ +static void +http_remove ( http_client_t *hc ) +{ + tvhpoll_event_t ev; + ev.fd = hc->hc_fd; + + /* Remove */ + curl_multi_remove_handle(http_curlm, hc->hc_curl); + tvhpoll_rem(http_poll, &ev, 1); + TAILQ_REMOVE(&http_clients, hc, hc_link); + + /* Free CURL memory */ + curl_easy_cleanup(hc->hc_curl); + hc->hc_curl = NULL; +} + +/* + * New socket + */ +static int +http_curl_socket ( CURL *c, int fd, int a, void *u, void *s ) +{ + http_client_t *hc; + tvhpoll_event_t ev = { 0 }; + ev.fd = fd; + + /* Find client */ + TAILQ_FOREACH(hc, &http_clients, hc_link) + if (hc->hc_curl == c) + break; + + /* Invalid */ + if (!hc) + goto done; + + /* Remove */ + if (a == CURL_POLL_REMOVE) { + //http_remove(hc); + + /* Set */ + } else if (a & CURL_POLL_INOUT) { + if (a & CURL_POLL_IN) + ev.events |= TVHPOLL_IN; + if (a & CURL_POLL_OUT) + ev.events |= TVHPOLL_OUT; + ev.data.fd = fd; + hc->hc_fd = fd; + tvhpoll_add(http_poll, &ev, 1); + } + + /* Done */ +done: + return 0; +} + +/* + * Data + */ +static size_t +http_curl_data ( void *buf, size_t len, size_t n, void *p ) +{ + http_client_t *hc = p; + if (!hc->hc_begin && hc->hc_conn) + hc->hc_conn(hc->hc_opaque); + hc->hc_begin = 1; + len = hc->hc_data(hc->hc_opaque, buf, len * n); + return len; +} + +/* + * Data thread + */ +static void * +http_thread ( void *p ) +{ + int n, e, run = 0; + tvhpoll_event_t ev; + http_client_t *hc; + + while (1) { + n = tvhpoll_wait(http_poll, &ev, 1, -1); + if (n < 0) { + tvherror("http_client", "tvhpoll_wait() error"); + break; + } else { + pthread_mutex_lock(&http_lock); + TAILQ_FOREACH(hc, &http_clients, hc_link) + if (hc->hc_fd == ev.data.fd) + break; + if (hc && (ev.events & (TVHPOLL_IN | TVHPOLL_OUT))) { + e = 0; + if (ev.events & TVHPOLL_IN) e |= CURL_POLL_IN; + if (ev.events & TVHPOLL_OUT) e |= CURL_POLL_OUT; + curl_multi_socket_action(http_curlm, ev.data.fd, 0, &run); + } + pthread_mutex_unlock(&http_lock); + } + } + + return NULL; +} + +/* + * Setup a connection (async) + */ +http_client_t * +http_connect + ( const url_t *url, + http_client_conn_cb conn_cb, + http_client_data_cb data_cb, + http_client_fail_cb fail_cb, + void *p ) +{ + int run; + + /* Setup structure */ + http_client_t *hc = calloc(1, sizeof(http_client_t)); + hc->hc_curl = curl_easy_init(); + hc->hc_url = *url; + hc->hc_conn = conn_cb; + hc->hc_data = data_cb; + hc->hc_fail = fail_cb; + hc->hc_opaque = p; + + /* Store */ + pthread_mutex_lock(&http_lock); + TAILQ_INSERT_TAIL(&http_clients, hc, hc_link); + + /* Setup connection */ + curl_easy_setopt(hc->hc_curl, CURLOPT_URL, url->raw); + curl_easy_setopt(hc->hc_curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(hc->hc_curl, CURLOPT_WRITEFUNCTION, http_curl_data); + curl_easy_setopt(hc->hc_curl, CURLOPT_WRITEDATA, hc); + curl_multi_add_handle(http_curlm, hc->hc_curl); + curl_multi_socket_action(http_curlm, CURL_SOCKET_TIMEOUT, 0, &run); + pthread_mutex_unlock(&http_lock); + + return hc; +} + +/* + * Cancel + */ +void +http_close ( http_client_t *hc ) +{ + pthread_mutex_lock(&http_lock); + http_remove(hc); + free(hc); + pthread_mutex_unlock(&http_lock); +} + +/* + * Initialise subsystem + */ +void +http_client_init ( void ) +{ + pthread_t tid; + + /* Setup list */ + pthread_mutex_init(&http_lock, NULL); + TAILQ_INIT(&http_clients); + + /* Initialise curl */ + curl_global_init(CURL_GLOBAL_ALL); + http_curlm = curl_multi_init(); + curl_multi_setopt(http_curlm, CURLMOPT_SOCKETFUNCTION, http_curl_socket); + + /* Setup poll */ + http_poll = tvhpoll_create(10); + + /* Setup thread */ + tvhthread_create(&tid, NULL, http_thread, NULL, 1); +} + +#else /* ENABLE_CURL */ + +void +http_client_init ( void ) +{ +} + +#endif /* ENABLE_CURL */