Commit 7897ed0d authored by Michael Niedermayer's avatar Michael Niedermayer

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: 's avatarMichael Niedermayer <michaelni@gmx.at>
parents c5a08956 44d19212
......@@ -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
......
......@@ -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++;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment