X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Fhttp.c;h=510b23375a301f7a78869ea8af8b45dd68d25edb;hb=9147c0975533c83564b1ba47f22952b291b80282;hp=056d5f6583766076db99645069f34ee861d1e0b8;hpb=4251e25272d6a51433805956ea26425d7ccd74cb;p=ffmpeg diff --git a/libavformat/http.c b/libavformat/http.c index 056d5f65837..510b23375a3 100644 --- a/libavformat/http.c +++ b/libavformat/http.c @@ -74,6 +74,7 @@ typedef struct HTTPContext { char *http_proxy; char *headers; char *mime_type; + char *http_version; char *user_agent; #if FF_API_HTTP_USER_AGENT char *user_agent_deprecated; @@ -116,7 +117,6 @@ typedef struct HTTPContext { int reconnect; int reconnect_at_eof; int reconnect_streamed; - int reconnect_delay; int reconnect_delay_max; int listen; char *resource; @@ -144,6 +144,7 @@ static const AVOption options[] = { { "multiple_requests", "use persistent connections", OFFSET(multiple_requests), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, D | E }, { "post_data", "set custom HTTP post data", OFFSET(post_data), AV_OPT_TYPE_BINARY, .flags = D | E }, { "mime_type", "export the MIME type", OFFSET(mime_type), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, AV_OPT_FLAG_EXPORT | AV_OPT_FLAG_READONLY }, + { "http_version", "export the http response version", OFFSET(http_version), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, AV_OPT_FLAG_EXPORT | AV_OPT_FLAG_READONLY }, { "cookies", "set cookies to be sent in applicable future requests, use newline delimited Set-Cookie HTTP field value syntax", OFFSET(cookies), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D }, { "icy", "request ICY metadata", OFFSET(icy), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, D }, { "icy_metadata_headers", "return ICY metadata headers", OFFSET(icy_metadata_headers), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, AV_OPT_FLAG_EXPORT }, @@ -171,6 +172,7 @@ static int http_connect(URLContext *h, const char *path, const char *local_path, const char *hoststr, const char *auth, const char *proxyauth, int *new_location); static int http_read_header(URLContext *h, int *new_location); +static int http_shutdown(URLContext *h, int flags); void ff_http_init_auth_state(URLContext *dest, const URLContext *src) { @@ -305,7 +307,38 @@ int ff_http_do_new_request(URLContext *h, const char *uri) HTTPContext *s = h->priv_data; AVDictionary *options = NULL; int ret; + char hostname1[1024], hostname2[1024], proto1[10], proto2[10]; + int port1, port2; + if (!h->prot || + !(!strcmp(h->prot->name, "http") || + !strcmp(h->prot->name, "https"))) + return AVERROR(EINVAL); + + av_url_split(proto1, sizeof(proto1), NULL, 0, + hostname1, sizeof(hostname1), &port1, + NULL, 0, s->location); + av_url_split(proto2, sizeof(proto2), NULL, 0, + hostname2, sizeof(hostname2), &port2, + NULL, 0, uri); + if (port1 != port2 || strncmp(hostname1, hostname2, sizeof(hostname2)) != 0) { + av_log(h, AV_LOG_ERROR, "Cannot reuse HTTP connection for different host: %s:%d != %s:%d\n", + hostname1, port1, + hostname2, port2 + ); + return AVERROR(EINVAL); + } + + if (!s->end_chunked_post) { + ret = http_shutdown(h, h->flags); + if (ret < 0) + return ret; + } + + if (s->willclose) + return AVERROR_EOF; + + s->end_chunked_post = 0; s->chunkend = 0; s->off = 0; s->icy_data_read = 0; @@ -314,6 +347,7 @@ int ff_http_do_new_request(URLContext *h, const char *uri) if (!s->location) return AVERROR(ENOMEM); + av_log(s, AV_LOG_INFO, "Opening \'%s\' for %s\n", uri, h->flags & AVIO_FLAG_WRITE ? "writing" : "reading"); ret = http_open_cnx(h, &options); av_dict_free(&options); return ret; @@ -790,6 +824,7 @@ static int parse_cookie(HTTPContext *s, const char *p, AVDictionary **cookies) } } } + av_dict_free(&new_params); // duplicate the cookie name (dict will dupe the value) if (!(eql = strchr(p, '='))) return AVERROR(EINVAL); @@ -889,6 +924,14 @@ static int process_line(URLContext *h, char *line, int line_count, } av_log(h, AV_LOG_TRACE, "HTTP version string: %s\n", version); } else { + if (av_strncasecmp(p, "HTTP/1.0", 8) == 0) + s->willclose = 1; + while (*p != '/' && *p != '\0') + p++; + while (*p == '/') + p++; + av_freep(&s->http_version); + s->http_version = av_strndup(p, 3); while (!av_isspace(*p) && *p != '\0') p++; while (av_isspace(*p)) @@ -1390,6 +1433,7 @@ static int http_read_stream(URLContext *h, uint8_t *buf, int size) HTTPContext *s = h->priv_data; int err, new_location, read_ret; int64_t seek_ret; + int reconnect_delay = 0; if (!s->hd) return AVERROR_EOF; @@ -1405,25 +1449,29 @@ static int http_read_stream(URLContext *h, uint8_t *buf, int size) return http_buf_read_compressed(h, buf, size); #endif /* CONFIG_ZLIB */ read_ret = http_buf_read(h, buf, size); - if ( (read_ret < 0 && s->reconnect && (!h->is_streamed || s->reconnect_streamed) && s->filesize > 0 && s->off < s->filesize) - || (read_ret == 0 && s->reconnect_at_eof && (!h->is_streamed || s->reconnect_streamed))) { + while ((read_ret < 0 && s->reconnect && (!h->is_streamed || s->reconnect_streamed) && s->filesize > 0 && s->off < s->filesize) + || (read_ret == AVERROR_EOF && s->reconnect_at_eof && (!h->is_streamed || s->reconnect_streamed))) { uint64_t target = h->is_streamed ? 0 : s->off; - if (s->reconnect_delay > s->reconnect_delay_max) + if (read_ret == AVERROR_EXIT) + return read_ret; + + if (reconnect_delay > s->reconnect_delay_max) return AVERROR(EIO); - av_log(h, AV_LOG_INFO, "Will reconnect at %"PRIu64" error=%s.\n", s->off, av_err2str(read_ret)); - av_usleep(1000U*1000*s->reconnect_delay); - s->reconnect_delay = 1 + 2*s->reconnect_delay; + av_log(h, AV_LOG_WARNING, "Will reconnect at %"PRIu64" in %d second(s), error=%s.\n", s->off, reconnect_delay, av_err2str(read_ret)); + err = ff_network_sleep_interruptible(1000U*1000*reconnect_delay, &h->interrupt_callback); + if (err != AVERROR(ETIMEDOUT)) + return err; + reconnect_delay = 1 + 2*reconnect_delay; seek_ret = http_seek_internal(h, target, SEEK_SET, 1); - if (seek_ret != target) { + if (seek_ret >= 0 && seek_ret != target) { av_log(h, AV_LOG_ERROR, "Failed to reconnect at %"PRIu64".\n", target); return read_ret; } read_ret = http_buf_read(h, buf, size); - } else - s->reconnect_delay = 0; + } return read_ret; }