Hi guys, So I caved and made HTTP headers settable by a dictionary. Let me know what you think.
If you're wondering about the hash function, I ran some light statistical tests and everything looks good. The results are almost perfectly gaussian. This is a new branch on top of the rtsp-tunneling one: http://github.com/j0sh/ffmpeg-soc/tree/http-headers Josh
From 6919a147f72647de98791af7f0473643adc5854d Mon Sep 17 00:00:00 2001 From: Josh Allmann <[email protected]> Date: Fri, 4 Jun 2010 22:58:00 -0700 Subject: [PATCH 1/2] Added dictionary data structure. This will make setting custom HTTP headers more flexible. --- libavformat/http.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 91 insertions(+), 0 deletions(-) diff --git a/libavformat/http.c b/libavformat/http.c index 9be921b..199dc99 100644 --- a/libavformat/http.c +++ b/libavformat/http.c @@ -37,6 +37,19 @@ #define URL_SIZE 4096 #define MAX_REDIRECTS 8 +#define TABLESIZE 128 + +typedef struct pair { + int hashval; + char key[128]; + char value[128]; + struct pair *next; +} pair; + +typedef struct { + pair *table[TABLESIZE]; +} DictContext; + typedef struct { URLContext *hd; unsigned char buffer[BUFFER_SIZE], *buf_ptr, *buf_end; @@ -66,6 +79,84 @@ void ff_http_set_chunked_transfer_encoding(URLContext *h, int is_chunked) ((HTTPContext*)h->priv_data)->is_chunked = is_chunked; } +/** + * bernstein hash function + * source: + * http://groups.google.com/group/comp.lang.c/msg/6b82e964887d73d9?dmode=source&output=gplain + */ +static int hash(const char *key) +{ + int h = 5381; + while (*key) { + h = ((h << 5) + h) + *key++; + } + return h & (TABLESIZE-1); +} + +static int dict_add(DictContext *hc, const char *key, const char *value) +{ + int hashval = hash(key); + pair *p = hc->table[hashval], *first = p; + + while (p && strcmp(key, p->key)) + p = p->next; + + if (!p) { + pair* newp = (pair*)av_malloc(sizeof(pair)); + if (!newp) return AVERROR(ENOMEM); + strncpy(newp->key, key, sizeof(newp->key)); + newp->key[sizeof(newp->key)-1] = '\0'; + newp->next = first; + p = hc->table[hashval] = newp; + } + + strncpy(p->value, value, sizeof(p->value)); + p->value[sizeof(p->value)-1] = '\0'; + return 0; +} + +static void dict_remove(DictContext *hc, const char *key) +{ + int hashval = hash(key); + pair *p = hc->table[hashval], *prev = NULL; + while (p && strcmp(key, p->key)) { + prev = p; + p = p->next; + } + + if (p) { + if (prev) + prev->next = p->next; + else + hc->table[hashval] = p->next; + av_free(p); + } +} + +static void free_buckets(pair *p) +{ + if (p->next) + free_buckets(p->next); + av_free(p); +} + +static void dict_free(DictContext *hc) +{ + int i; + for (i = 0; i < TABLESIZE; i++) { + pair *p = hc->table[i]; + if (p) + free_buckets(p); + } + av_free(hc); +} + +static DictContext* dict_init(DictContext **hc) +{ + *hc = (DictContext*)av_mallocz(sizeof(DictContext*)*TABLESIZE); + return *hc; +} + /* return non zero if error */ static int http_open_cnx(URLContext *h) { -- 1.7.0.4
From bc94f7e95aaf102cb67cfb684ff2eacdaf2d0b3d Mon Sep 17 00:00:00 2001 From: Josh Allmann <[email protected]> Date: Sat, 5 Jun 2010 01:11:15 -0700 Subject: [PATCH 2/2] Modified HTTP headers and related code to use a dictionary-based scheme. --- libavformat/http.c | 70 +++++++++++++++++++++++++++++++++++++++------------ libavformat/http.h | 8 +++--- libavformat/rtsp.c | 37 +++++++++++++++------------ 3 files changed, 77 insertions(+), 38 deletions(-) diff --git a/libavformat/http.c b/libavformat/http.c index 199dc99..0582aed 100644 --- a/libavformat/http.c +++ b/libavformat/http.c @@ -60,7 +60,7 @@ typedef struct { char location[URL_SIZE]; HTTPAuthState auth_state; int init; - unsigned char headers[BUFFER_SIZE]; + DictContext *headers; int is_chunked; } HTTPContext; @@ -68,10 +68,16 @@ static int http_connect(URLContext *h, const char *path, const char *hoststr, const char *auth, int *new_location); static int http_write(URLContext *h, const uint8_t *buf, int size); -void ff_http_set_headers(URLContext *h, const char *headers) +static int dict_add(DictContext *hc, const char *key, const char *value); +static void dict_remove(DictContext *hc, const char *key); + +void ff_http_set_header(URLContext *h, const char *header, const char *value) { HTTPContext *s = h->priv_data; - snprintf(s->headers, sizeof(s->headers), "%s", headers); + if(value) + dict_add(s->headers, header, value); + else + dict_remove(s->headers, header); } void ff_http_set_chunked_transfer_encoding(URLContext *h, int is_chunked) @@ -229,6 +235,7 @@ static int http_open_cnx(URLContext *h) static int http_open(URLContext *h, const char *uri, int flags) { HTTPContext *s; + DictContext *d; h->is_streamed = 1; @@ -242,10 +249,18 @@ static int http_open(URLContext *h, const char *uri, int flags) s->is_chunked = 1; s->off = 0; s->init = 0; - *s->headers = '\0'; memset(&s->auth_state, 0, sizeof(s->auth_state)); av_strlcpy(s->location, uri, URL_SIZE); + // add in default headers + dict_init(&d); + dict_add(d, "Accept", "*/*"); + dict_add(d, "Range", ""); + dict_add(d, "Host", ""); + dict_add(d, "Connection", "close"); + dict_add(d, "User-Agent", LIBAVFORMAT_IDENT); + s->headers = d; + return 0; } static int http_getc(HTTPContext *s) @@ -351,12 +366,42 @@ static int process_line(URLContext *h, char *line, int line_count, return 1; } +static void make_headers(URLContext *h, char *buf, int buf_size, const char *hoststr) +{ + HTTPContext *s = h->priv_data; + DictContext *headers = s->headers; + int i; + + for (i = 0; i < TABLESIZE; i++) { + pair *p = headers->table[i]; + while(p && buf_size > 0) { + int len; + + if(!strcmp("Range", p->key)) { + snprintf(p->value, sizeof(p->value), + "bytes=%"PRId64"-\r\n", s->off); + } + if(!strcmp("Host", p->key)) + strncpy(p->value, hoststr, sizeof(p->value)); + + len = snprintf(buf, buf_size, + "%s: %s\r\n", p->key, p->value); + + buf += len; + buf_size -= len; + p = p->next; + + } + } +} + static int http_connect(URLContext *h, const char *path, const char *hoststr, const char *auth, int *new_location) { HTTPContext *s = h->priv_data; int post, err; char line[1024]; + char headers[1024]; char *authstr = NULL; int64_t off = s->off; @@ -365,29 +410,19 @@ static int http_connect(URLContext *h, const char *path, const char *hoststr, post = h->flags & URL_WRONLY; authstr = ff_http_auth_create_response(&s->auth_state, auth, path, post ? "POST" : "GET"); - if (!strlen(s->headers)) { - snprintf(s->headers, sizeof(s->headers), - "Accept: */*\r\n" - "Range: bytes=%"PRId64"-\r\n" - "Host: %s\r\n" - "Connection: close\r\n" - "\r\n", - s->off, - hoststr); - } + + make_headers(h, headers, sizeof(headers), hoststr); snprintf(s->buffer, sizeof(s->buffer), "%s %s HTTP/1.1\r\n" - "User-Agent: %s\r\n" "%s" "%s" "%s" "\r\n", post ? "POST" : "GET", path, - LIBAVFORMAT_IDENT, post && s->is_chunked ? "Transfer-Encoding: chunked\r\n" : "", - s->headers, + headers, authstr ? authstr : ""); av_freep(&authstr); @@ -535,6 +570,7 @@ static int http_close(URLContext *h) } url_close(s->hd); + dict_free(s->headers); av_free(s); return ret; } diff --git a/libavformat/http.h b/libavformat/http.h index e754c7e..9057c61 100644 --- a/libavformat/http.h +++ b/libavformat/http.h @@ -22,7 +22,7 @@ #define AVFORMAT_HTTP_H /** - * Sets custom HTTP headers. Note this overwrites all default options. + * Sets custom HTTP headers. * * The following headers will always be set, no matter what: * -HTTP verb, path, HTTP version @@ -30,8 +30,8 @@ * -Transfer-Encoding (if applicable) * - Authorization (if applicable) * - * The following headers are set by default, and will be overwritten if - * custom headers are set. Be sure to re-specify them if needed. + * The following headers are set by default. Passing in a null value + * will remove the header, if it exists. * -Accept * -Range * -Host @@ -40,7 +40,7 @@ * @param h URL context for this HTTP connection * @param is_chunked 0 to disable chunking, nonzero otherwise. */ -void ff_http_set_headers(URLContext *h, const char *headers); +void ff_http_set_header(URLContext *h, const char *header, const char *value); /** * Enables or disables chunked transfer encoding. (default is enabled) diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c index 7b3abbe..584c8e4 100644 --- a/libavformat/rtsp.c +++ b/libavformat/rtsp.c @@ -1566,7 +1566,6 @@ redirect: /* set up initial handshake for tunneling */ char httpname[1024]; char sessioncookie[17]; - char headers[1024]; ff_url_join(httpname, sizeof(httpname), "http", NULL, host, port, "%s", path); snprintf(sessioncookie, sizeof(sessioncookie), "%08x%08x", @@ -1579,13 +1578,14 @@ redirect: } /* generate GET headers */ - snprintf(headers, sizeof(headers), - "x-sessioncookie: %s\r\n" - "Accept: application/x-rtsp-tunnelled\r\n" - "Pragma: no-cache\r\n" - "Cache-Control: no-cache\r\n", - sessioncookie); - ff_http_set_headers(rtsp_hd, headers); + ff_http_set_header(rtsp_hd, "Range", NULL); + ff_http_set_header(rtsp_hd, "Host", NULL); + ff_http_set_header(rtsp_hd, "Connection", NULL); + + ff_http_set_header(rtsp_hd, "x-sessioncookie", sessioncookie); + ff_http_set_header(rtsp_hd, "Accept", "application/x-rtsp-tunnelled"); + ff_http_set_header(rtsp_hd, "Pragma", "no-cache"); + ff_http_set_header(rtsp_hd, "Cache-Control", "no-cache"); /* complete the connection */ if (url_read(rtsp_hd, NULL, 0)) { @@ -1600,15 +1600,18 @@ redirect: } /* generate POST headers */ - snprintf(headers, sizeof(headers), - "x-sessioncookie: %s\r\n" - "Content-Type: application/x-rtsp-tunnelled\r\n" - "Pragma: no-cache\r\n" - "Cache-Control: no-cache\r\n" - "Content-Length: 32767\r\n" - "Expires: Sun, 9 Jan 1972 00:00:00 GMT\r\n", - sessioncookie); - ff_http_set_headers(rtsp_hd_out, headers); + ff_http_set_header(rtsp_hd_out, "Accept", NULL); + ff_http_set_header(rtsp_hd_out, "Range", NULL); + ff_http_set_header(rtsp_hd_out, "Host", NULL); + ff_http_set_header(rtsp_hd_out, "Connection", NULL); + + ff_http_set_header(rtsp_hd_out, "x-sessioncookie", sessioncookie); + ff_http_set_header(rtsp_hd_out, "Accept", "application/x-rtsp-tunnelled"); + ff_http_set_header(rtsp_hd_out, "Pragma", "no-cache"); + ff_http_set_header(rtsp_hd_out, "Cache-Control", "no-cache"); + ff_http_set_header(rtsp_hd_out, "Content-Length", "32767"); + ff_http_set_header(rtsp_hd_out, "Expires", "Sun, 9 Jan 1972 00:00:00 GMT"); + ff_http_set_chunked_transfer_encoding(rtsp_hd_out, 0); } else { -- 1.7.0.4
_______________________________________________ FFmpeg-soc mailing list [email protected] https://lists.mplayerhq.hu/mailman/listinfo/ffmpeg-soc
