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.
This commit is contained in:
Adam Sutton 2013-11-23 21:43:45 +00:00
parent de84d759cc
commit dd9e58a2e0
2 changed files with 267 additions and 2 deletions

View file

@ -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_ */

248
src/http/http_client.c Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "tvheadend.h"
#include "tvhpoll.h"
#include "redblack.h"
#include "queue.h"
#include "url.h"
#include "http.h"
#if ENABLE_CURL
#include <curl/curl.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
/*
* 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 */