Message ID | 20240415162741.110374-1-derek.buitenhuis@gmail.com |
---|---|
Headers | show |
Series | HTTP rate limiting and retry improvements | expand |
On 4/15/2024 1:27 PM, Derek Buitenhuis wrote: > 429 and 503 codes can, and often do (e.g. all Google Cloud > Storage URLs can), return a Retry-After header with the error, > indicating how long to wait, in seconds, before retrying again. > If it is not respected by, for example, using our default backoff > stratetgy instead, chances of success are very unlikely. > > This adds an AVOption to respect that header. > > Signed-off-by: Derek Buitenhuis <derek.buitenhuis@gmail.com> > --- > libavformat/http.c | 12 ++++++++++++ > libavformat/version.h | 2 +- > 2 files changed, 13 insertions(+), 1 deletion(-) > > diff --git a/libavformat/http.c b/libavformat/http.c > index e7603037f4..8f092f108d 100644 > --- a/libavformat/http.c > +++ b/libavformat/http.c > @@ -138,6 +138,8 @@ typedef struct HTTPContext { > char *new_location; > AVDictionary *redirect_cache; > uint64_t filesize_from_content_range; > + int respect_retry_after; > + int retry_after; > } HTTPContext; > > #define OFFSET(x) offsetof(HTTPContext, x) > @@ -176,6 +178,7 @@ static const AVOption options[] = { > { "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 }, > + { "respect_retry_after", "respect the Retry-After header when retrying connections", OFFSET(respect_retry_after), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, D }, > { "listen", "listen on HTTP", OFFSET(listen), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 2, D | E }, > { "resource", "The resource requested by a client", OFFSET(resource), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E }, > { "reply_code", "The http status code to return to a client", OFFSET(reply_code), AV_OPT_TYPE_INT, { .i64 = 200}, INT_MIN, 599, E}, > @@ -386,6 +389,13 @@ redo: > reconnect_delay > s->reconnect_delay_max) > goto fail; > > + if (s->respect_retry_after && s->retry_after > 0) { > + reconnect_delay = s->retry_after; > + if (reconnect_delay > s->reconnect_delay_max) > + goto fail; > + s->retry_after = 0; > + } > + > av_log(h, AV_LOG_WARNING, "Will reconnect at %"PRIu64" in %d second(s).\n", off, reconnect_delay); > ret = ff_network_sleep_interruptible(1000U * 1000 * reconnect_delay, &h->interrupt_callback); > if (ret != AVERROR(ETIMEDOUT)) > @@ -1231,6 +1241,8 @@ static int process_line(URLContext *h, char *line, int line_count, int *parsed_h > parse_expires(s, p); > } else if (!av_strcasecmp(tag, "Cache-Control")) { > parse_cache_control(s, p); > + } else if (!av_strcasecmp(tag, "Retry-After")) { > + s->retry_after = strtoull(p, NULL, 10); Why strtoull for an int? If the value can't be negative, then make it unsigned and use strtoul instead. > } > } > return 1; > diff --git a/libavformat/version.h b/libavformat/version.h > index 7ff1483912..ee91990360 100644 > --- a/libavformat/version.h > +++ b/libavformat/version.h > @@ -32,7 +32,7 @@ > #include "version_major.h" > > #define LIBAVFORMAT_VERSION_MINOR 3 > -#define LIBAVFORMAT_VERSION_MICRO 100 > +#define LIBAVFORMAT_VERSION_MICRO 101 > > #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ > LIBAVFORMAT_VERSION_MINOR, \