]> git.sesse.net Git - ffmpeg/blobdiff - libavformat/http.c
lavf/http: Properly process HTTP header on listen.
[ffmpeg] / libavformat / http.c
index 4f6716a75bd75c0ae1c65810525407e55abd0210..53bdb985f0598791991afbc18cb97b981638a7ac 100644 (file)
@@ -126,7 +126,7 @@ static const AVOption options[] = {
     { "location", "The actual location of the data received", OFFSET(location), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D | E },
     { "offset", "initial byte offset", OFFSET(off), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, INT64_MAX, D },
     { "end_offset", "try to limit the request to bytes preceding this offset", OFFSET(end_off), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, INT64_MAX, D },
-    { "method", "Override the HTTP method", OFFSET(method), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E },
+    { "method", "Override the HTTP method or set the expected HTTP method from a client", OFFSET(method), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D | E },
     { "reconnect", "auto reconnect after disconnect before EOF", OFFSET(reconnect), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, D },
     { "listen", "listen on HTTP", OFFSET(listen), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, D | E },
     { NULL }
@@ -299,6 +299,23 @@ int ff_http_averror(int status_code, int default_averror)
         return default_averror;
 }
 
+static void handle_http_errors(URLContext *h, int error)
+{
+    static const char bad_request[] = "HTTP/1.1 400 Bad Request\r\nContent-Type: text/plain\r\n\r\n400 Bad Request\r\n";
+    static const char internal_server_error[] = "HTTP/1.1 500 Internal server error\r\nContent-Type: text/plain\r\n\r\n500 Internal server error\r\n";
+    HTTPContext *s = h->priv_data;
+    if (h->is_connected) {
+        switch(error) {
+            case AVERROR_HTTP_BAD_REQUEST:
+                ffurl_write(s->hd, bad_request, strlen(bad_request));
+                break;
+            default:
+                av_log(h, AV_LOG_ERROR, "Unhandled HTTP error.\n");
+                ffurl_write(s->hd, internal_server_error, strlen(internal_server_error));
+        }
+    }
+}
+
 static int http_listen(URLContext *h, const char *uri, int flags,
                        AVDictionary **options) {
     HTTPContext *s = h->priv_data;
@@ -318,13 +335,14 @@ static int http_listen(URLContext *h, const char *uri, int flags,
     if ((ret = ffurl_open(&s->hd, lower_url, AVIO_FLAG_READ_WRITE,
                           &h->interrupt_callback, options)) < 0)
         goto fail;
-    if ((ret = ffurl_write(s->hd, header, strlen(header))) < 0)
-        goto fail;
     if ((ret = http_read_header(h, &new_location)) < 0)
          goto fail;
+    if ((ret = ffurl_write(s->hd, header, strlen(header))) < 0)
+         goto fail;
     return 0;
 
 fail:
+    handle_http_errors(h, ret);
     av_dict_free(&s->chained_options);
     return ret;
 }
@@ -545,7 +563,7 @@ static int process_line(URLContext *h, char *line, int line_count,
                         int *new_location)
 {
     HTTPContext *s = h->priv_data;
-    char *tag, *p, *end;
+    char *tag, *p, *end, *method, *resource, *version;
     int ret;
 
     /* end of header */
@@ -556,6 +574,44 @@ static int process_line(URLContext *h, char *line, int line_count,
 
     p = line;
     if (line_count == 0) {
+        if (s->listen) {
+            // HTTP method
+            method = p;
+            while (!av_isspace(*p))
+                p++;
+            *(p++) = '\0';
+            av_log(h, AV_LOG_TRACE, "Received method: %s\n", method);
+            if (s->method) {
+                if (av_strcasecmp(s->method, method)) {
+                    av_log(h, AV_LOG_ERROR, "Received and expected HTTP method do not match. (%s expected, %s received)\n",
+                           s->method, method);
+                    return ff_http_averror(400, AVERROR(EIO));
+                }
+            }
+
+            // HTTP resource
+            while (av_isspace(*p))
+                p++;
+            resource = p;
+            while (!av_isspace(*p))
+                p++;
+            *(p++) = '\0';
+            av_log(h, AV_LOG_TRACE, "Requested resource: %s\n", resource);
+
+            // HTTP version
+            while (av_isspace(*p))
+                p++;
+            version = p;
+            while (!av_isspace(*p))
+                p++;
+            *p = '\0';
+            if (av_strncasecmp(version, "HTTP/", 5)) {
+                av_log(h, AV_LOG_ERROR, "Malformed HTTP version string.\n");
+                return ff_http_averror(400, AVERROR(EIO));
+            }
+            av_log(h, AV_LOG_TRACE, "HTTP version string: %s\n", version);
+        } else {
+        /* TODO: reindent */
         while (!av_isspace(*p) && *p != '\0')
             p++;
         while (av_isspace(*p))
@@ -566,6 +622,7 @@ static int process_line(URLContext *h, char *line, int line_count,
 
         if ((ret = check_http_code(h, s->http_code, end)) < 0)
             return ret;
+        }
     } else {
         while (*p != '\0' && *p != ':')
             p++;