int http_code;
/* Used if "Transfer-Encoding: chunked" otherwise -1. */
uint64_t chunksize;
+ int chunkend;
uint64_t off, end_off, filesize;
char *location;
HTTPAuthState auth_state;
char *http_proxy;
char *headers;
char *mime_type;
+ char *http_version;
char *user_agent;
#if FF_API_HTTP_USER_AGENT
char *user_agent_deprecated;
int reconnect;
int reconnect_at_eof;
int reconnect_streamed;
- int reconnect_delay;
int reconnect_delay_max;
int listen;
char *resource;
{ "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 },
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)
{
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;
av_free(s->location);
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;
}
}
}
+ av_dict_free(&new_params);
// duplicate the cookie name (dict will dupe the value)
if (!(eql = strchr(p, '='))) return AVERROR(EINVAL);
}
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))
int len;
if (s->chunksize != UINT64_MAX) {
+ if (s->chunkend) {
+ return AVERROR_EOF;
+ }
if (!s->chunksize) {
char line[32];
int err;
s->chunksize = strtoull(line, NULL, 16);
av_log(h, AV_LOG_TRACE,
- "Chunked encoding data size: %"PRIu64"'\n",
+ "Chunked encoding data size: %"PRIu64"\n",
s->chunksize);
- if (!s->chunksize) {
+ if (!s->chunksize && s->multiple_requests) {
+ http_get_line(s, line, sizeof(line)); // read empty chunk
+ s->chunkend = 1;
+ return 0;
+ }
+ else if (!s->chunksize) {
av_log(h, AV_LOG_DEBUG, "Last chunk received, closing conn\n");
ffurl_closep(&s->hd);
return 0;
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;
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) {
uint64_t target = h->is_streamed ? 0 : s->off;
- if (s->reconnect_delay > s->reconnect_delay_max)
+ if (read_ret == AVERROR_EXIT)
+ break;
+
+ if (h->is_streamed && !s->reconnect_streamed)
+ break;
+
+ if (!(s->reconnect && s->filesize > 0 && s->off < s->filesize) &&
+ !(s->reconnect_at_eof && read_ret == AVERROR_EOF))
+ break;
+
+ 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;
}