aboutsummaryrefslogtreecommitdiffstats
path: root/libavformat/http.c
diff options
context:
space:
mode:
authorerankor <eran.kornblau@kaltura.com>2020-12-03 10:42:52 +0200
committerMarton Balint <cus@passwd.hu>2021-01-03 16:47:29 +0100
commitfde5593c7ce5288ae1333e9dc3ca4f95caa69444 (patch)
tree86d733b30e5584396049f2b3c65612dbf0134671 /libavformat/http.c
parentf26b5acfc03375c96e593197a8145ada1b63b1fd (diff)
downloadffmpeg-fde5593c7ce5288ae1333e9dc3ca4f95caa69444.tar.gz
avformat/http: support retry on connection error
Add 2 new options: - reconnect_on_http_error - a list of http status codes that should be retried. the list can contain explicit status codes / the strings 4xx/5xx. - reconnect_on_network_error - reconnects on arbitrary errors during connect, e.g. ECONNRESET/ETIMEDOUT the retry employs the same exponential backoff logic as the existing reconnect/reconnect_at_eof flags. related tickets: https://trac.ffmpeg.org/ticket/6066 https://trac.ffmpeg.org/ticket/7768 Signed-off-by: Marton Balint <cus@passwd.hu>
Diffstat (limited to 'libavformat/http.c')
-rw-r--r--libavformat/http.c60
1 files changed, 58 insertions, 2 deletions
diff --git a/libavformat/http.c b/libavformat/http.c
index 2d24c00e18..76f0a67c61 100644
--- a/libavformat/http.c
+++ b/libavformat/http.c
@@ -119,8 +119,10 @@ typedef struct HTTPContext {
char *method;
int reconnect;
int reconnect_at_eof;
+ int reconnect_on_network_error;
int reconnect_streamed;
int reconnect_delay_max;
+ char *reconnect_on_http_error;
int listen;
char *resource;
int reply_code;
@@ -164,6 +166,8 @@ static const AVOption options[] = {
{ "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_BOOL, { .i64 = 0 }, 0, 1, D },
{ "reconnect_at_eof", "auto reconnect at EOF", OFFSET(reconnect_at_eof), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, D },
+ { "reconnect_on_network_error", "auto reconnect in case of tcp/tls error during connect", OFFSET(reconnect_on_network_error), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, D },
+ { "reconnect_on_http_error", "list of http status codes to reconnect on", OFFSET(reconnect_on_http_error), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D },
{ "reconnect_streamed", "auto reconnect streamed / non seekable streams", OFFSET(reconnect_streamed), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, D },
{ "reconnect_delay_max", "max reconnect delay in seconds after which to give up", OFFSET(reconnect_delay_max), AV_OPT_TYPE_INT, { .i64 = 120 }, 0, UINT_MAX/1000/1000, D },
{ "listen", "listen on HTTP", OFFSET(listen), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 2, D | E },
@@ -258,21 +262,73 @@ static int http_open_cnx_internal(URLContext *h, AVDictionary **options)
return location_changed;
}
+static int http_should_reconnect(HTTPContext *s, int err)
+{
+ const char *status_group;
+ char http_code[4];
+
+ switch (err) {
+ case AVERROR_HTTP_BAD_REQUEST:
+ case AVERROR_HTTP_UNAUTHORIZED:
+ case AVERROR_HTTP_FORBIDDEN:
+ case AVERROR_HTTP_NOT_FOUND:
+ case AVERROR_HTTP_OTHER_4XX:
+ status_group = "4xx";
+ break;
+
+ case AVERROR_HTTP_SERVER_ERROR:
+ status_group = "5xx";
+ break;
+
+ default:
+ return s->reconnect_on_network_error;
+ }
+
+ if (!s->reconnect_on_http_error)
+ return 0;
+
+ if (av_match_list(status_group, s->reconnect_on_http_error, ',') > 0)
+ return 1;
+
+ snprintf(http_code, sizeof(http_code), "%d", s->http_code);
+
+ return av_match_list(http_code, s->reconnect_on_http_error, ',') > 0;
+}
+
/* return non zero if error */
static int http_open_cnx(URLContext *h, AVDictionary **options)
{
HTTPAuthType cur_auth_type, cur_proxy_auth_type;
HTTPContext *s = h->priv_data;
int location_changed, attempts = 0, redirects = 0;
+ int reconnect_delay = 0;
+ uint64_t off;
+
redo:
av_dict_copy(options, s->chained_options, 0);
cur_auth_type = s->auth_state.auth_type;
cur_proxy_auth_type = s->auth_state.auth_type;
+ off = s->off;
location_changed = http_open_cnx_internal(h, options);
- if (location_changed < 0)
- goto fail;
+ if (location_changed < 0) {
+ if (!http_should_reconnect(s, location_changed) ||
+ reconnect_delay > s->reconnect_delay_max)
+ goto fail;
+
+ av_log(h, AV_LOG_WARNING, "Will reconnect at %"PRIu64" in %d second(s).\n", off, reconnect_delay);
+ location_changed = ff_network_sleep_interruptible(1000U * 1000 * reconnect_delay, &h->interrupt_callback);
+ if (location_changed != AVERROR(ETIMEDOUT))
+ goto fail;
+ reconnect_delay = 1 + 2 * reconnect_delay;
+
+ /* restore the offset (http_connect resets it) */
+ s->off = off;
+
+ ffurl_closep(&s->hd);
+ goto redo;
+ }
attempts++;
if (s->http_code == 401) {