aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Niedermayer <michaelni@gmx.at>2015-06-06 12:34:00 +0200
committerMichael Niedermayer <michaelni@gmx.at>2015-06-06 12:34:05 +0200
commit7897ed0d3cb918e05ee0fa7b02095de09bb41087 (patch)
tree83d585969039e4ede59275b3abb694975291d64f
parentc5a08956a3b11d9adf82dc1a97f6fa599803d8fd (diff)
parent44d19212008ee48255f96d82df899e3413a982c2 (diff)
downloadffmpeg-7897ed0d3cb918e05ee0fa7b02095de09bb41087.tar.gz
Merge remote-tracking branch 'cigaes/master'
* cigaes/master: lavf/http: Add simple autodetection for client HTTP method, based on AVIO_FLAG_READ. lavf/http: Indent else-clause. lavf/http: Properly process HTTP header on listen. lavf/http: Rudimentary error handling for HTTP requests received from clients. lavf/http: Process HTTP header before sending response. lavf/http: Document method option. Merged-by: Michael Niedermayer <michaelni@gmx.at>
-rw-r--r--doc/protocols.texi10
-rw-r--r--libavformat/http.c89
2 files changed, 87 insertions, 12 deletions
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..2db2dea51e 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 }
@@ -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,8 @@ static int process_line(URLContext *h, char *line, int line_count,
int *new_location)
{
HTTPContext *s = h->priv_data;
- char *tag, *p, *end;
+ const char *auto_method = h->flags & AVIO_FLAG_READ ? "POST" : "GET";
+ char *tag, *p, *end, *method, *resource, *version;
int ret;
/* end of header */
@@ -556,16 +575,62 @@ static int process_line(URLContext *h, char *line, int line_count,
p = line;
if (line_count == 0) {
- while (!av_isspace(*p) && *p != '\0')
- p++;
- while (av_isspace(*p))
- p++;
- s->http_code = strtol(p, &end, 10);
+ 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));
+ }
+ } 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
+ 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 {
+ 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 != ':')
p++;