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

Reply via email to