char *http_proxy;
char *headers;
char *mime_type;
+ char *http_version;
char *user_agent;
+ char *referer;
#if FF_API_HTTP_USER_AGENT
char *user_agent_deprecated;
#endif
int reconnect;
int reconnect_at_eof;
int reconnect_streamed;
- int reconnect_delay;
int reconnect_delay_max;
int listen;
char *resource;
{ "headers", "set custom HTTP headers, can override built in default headers", OFFSET(headers), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D | E },
{ "content_type", "set a specific content type for the POST messages", OFFSET(content_type), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D | E },
{ "user_agent", "override User-Agent header", OFFSET(user_agent), AV_OPT_TYPE_STRING, { .str = DEFAULT_USER_AGENT }, 0, 0, D },
+ { "referer", "override referer header", OFFSET(referer), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D },
#if FF_API_HTTP_USER_AGENT
{ "user-agent", "override User-Agent header", OFFSET(user_agent_deprecated), AV_OPT_TYPE_STRING, { .str = DEFAULT_USER_AGENT }, 0, 0, D },
#endif
{ "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 },
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);
return AVERROR(EINVAL);
}
- ret = http_shutdown(h, h->flags);
- if (ret < 0)
- return ret;
+ 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;
{
char *param, *next_param, *cstr, *back;
+ if (!set_cookie[0])
+ return 0;
+
if (!(cstr = av_strdup(set_cookie)))
return AVERROR(EINVAL);
back = &cstr[strlen(cstr)-1];
while (strchr(WHITESPACES, *back)) {
*back='\0';
+ if (back == cstr)
+ break;
back--;
}
// if the cookie has already expired ignore it
if (av_timegm(&new_tm) < av_gettime() / 1000000) {
av_dict_free(&new_params);
- return -1;
+ return 0;
}
// only replace an older cookie with the same name
}
}
}
+ 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))
if (!has_header(s->headers, "\r\nUser-Agent: "))
len += av_strlcatf(headers + len, sizeof(headers) - len,
"User-Agent: %s\r\n", s->user_agent);
+ if (s->referer) {
+ /* set default headers if needed */
+ if (!has_header(s->headers, "\r\nReferer: "))
+ len += av_strlcatf(headers + len, sizeof(headers) - len,
+ "Referer: %s\r\n", s->referer);
+ }
if (!has_header(s->headers, "\r\nAccept: "))
len += av_strlcpy(headers + len, "Accept: */*\r\n",
sizeof(headers) - len);
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;
}