From aa74401af8a40862c8e148222481921f8013f6ed Mon Sep 17 00:00:00 2001 From: Stephan Holljes Date: Thu, 4 Jun 2015 01:16:59 +0200 Subject: [PATCH 1/6] lavf/http: Document method option. Signed-off-by: Stephan Holljes --- doc/protocols.texi | 10 ++++++++++ libavformat/http.c | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/doc/protocols.texi b/doc/protocols.texi index f822d81223..453dbcf6bf 100644 --- a/doc/protocols.texi +++ b/doc/protocols.texi @@ -278,6 +278,16 @@ Set initial byte offset. @item end_offset Try to limit the request to bytes preceding this offset. +@item method +When used as a client option it sets the HTTP method for the request. + +When used as a server option it sets the HTTP method that is going to be +expected from the client(s). +If the expected and the received HTTP method do not match the client will +be given a Bad Request response. +When unset the HTTP method is not checked for now. This will be replaced by +autodetection in the future. + @item listen If set to 1 enables experimental HTTP server. This can be used to send data when used as an output option, or read data from a client with HTTP POST when used as diff --git a/libavformat/http.c b/libavformat/http.c index 4f6716a75b..546741ad47 100644 --- a/libavformat/http.c +++ b/libavformat/http.c @@ -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 } From bbcee92b6d59adca054351c6e9e56bcb77ebda11 Mon Sep 17 00:00:00 2001 From: Stephan Holljes Date: Thu, 4 Jun 2015 01:17:51 +0200 Subject: [PATCH 2/6] lavf/http: Process HTTP header before sending response. Signed-off-by: Stephan Holljes --- libavformat/http.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libavformat/http.c b/libavformat/http.c index 546741ad47..e51f524560 100644 --- a/libavformat/http.c +++ b/libavformat/http.c @@ -318,10 +318,10 @@ 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: From 8cfaa76a5e77a8cf9768a5315646647610ff49c7 Mon Sep 17 00:00:00 2001 From: Stephan Holljes Date: Thu, 4 Jun 2015 01:20:28 +0200 Subject: [PATCH 3/6] lavf/http: Rudimentary error handling for HTTP requests received from clients. Signed-off-by: Stephan Holljes --- libavformat/http.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/libavformat/http.c b/libavformat/http.c index e51f524560..a5b3e2942d 100644 --- a/libavformat/http.c +++ b/libavformat/http.c @@ -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; @@ -325,6 +342,7 @@ static int http_listen(URLContext *h, const char *uri, int flags, return 0; fail: + handle_http_errors(h, ret); av_dict_free(&s->chained_options); return ret; } From a7e7c68b0e264f5db15bbbf9fb3cd557cc58e927 Mon Sep 17 00:00:00 2001 From: Stephan Holljes Date: Thu, 4 Jun 2015 01:21:02 +0200 Subject: [PATCH 4/6] lavf/http: Properly process HTTP header on listen. Signed-off-by: Stephan Holljes --- libavformat/http.c | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/libavformat/http.c b/libavformat/http.c index a5b3e2942d..53bdb985f0 100644 --- a/libavformat/http.c +++ b/libavformat/http.c @@ -563,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 */ @@ -574,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)) @@ -584,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++; From 290b23755673eaa8a6f32025734e8f9916e75f6f Mon Sep 17 00:00:00 2001 From: Stephan Holljes Date: Thu, 4 Jun 2015 01:21:26 +0200 Subject: [PATCH 5/6] lavf/http: Indent else-clause. Signed-off-by: Stephan Holljes --- libavformat/http.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/libavformat/http.c b/libavformat/http.c index 53bdb985f0..1bf9166c27 100644 --- a/libavformat/http.c +++ b/libavformat/http.c @@ -611,17 +611,16 @@ static int process_line(URLContext *h, char *line, int line_count, } 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)) - p++; - s->http_code = strtol(p, &end, 10); + while (!av_isspace(*p) && *p != '\0') + p++; + while (av_isspace(*p)) + p++; + s->http_code = strtol(p, &end, 10); - av_log(h, AV_LOG_TRACE, "http_code=%d\n", s->http_code); + av_log(h, AV_LOG_TRACE, "http_code=%d\n", s->http_code); - if ((ret = check_http_code(h, s->http_code, end)) < 0) - return ret; + if ((ret = check_http_code(h, s->http_code, end)) < 0) + return ret; } } else { while (*p != '\0' && *p != ':') From 44d19212008ee48255f96d82df899e3413a982c2 Mon Sep 17 00:00:00 2001 From: Stephan Holljes Date: Fri, 5 Jun 2015 00:27:07 +0200 Subject: [PATCH 6/6] lavf/http: Add simple autodetection for client HTTP method, based on AVIO_FLAG_READ. Signed-off-by: Stephan Holljes --- libavformat/http.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/libavformat/http.c b/libavformat/http.c index 1bf9166c27..2db2dea51e 100644 --- a/libavformat/http.c +++ b/libavformat/http.c @@ -563,6 +563,7 @@ static int process_line(URLContext *h, char *line, int line_count, int *new_location) { HTTPContext *s = h->priv_data; + const char *auto_method = h->flags & AVIO_FLAG_READ ? "POST" : "GET"; char *tag, *p, *end, *method, *resource, *version; int ret; @@ -587,6 +588,14 @@ static int process_line(URLContext *h, char *line, int line_count, s->method, method); return ff_http_averror(400, AVERROR(EIO)); } + } else { + // use autodetected HTTP method to expect + av_log(h, AV_LOG_TRACE, "Autodetected %s HTTP method\n", auto_method); + if (av_strcasecmp(auto_method, method)) { + av_log(h, AV_LOG_ERROR, "Received and autodetected HTTP method did not match " + "(%s autodetected %s received)\n", auto_method, method); + return ff_http_averror(400, AVERROR(EIO)); + } } // HTTP resource