| @@ -31,6 +31,7 @@ · 30 unmodified lines |
| 31 | 31 | #include "libavutil/avstring.h" |
| 32 | 32 | #include "libavutil/bprint.h" |
| 33 | 33 | #include "libavutil/getenv_utf8.h" |
| 34 | +#include "libavutil/macros.h" |
| 34 | 35 | #include "libavutil/mem.h" |
| 35 | 36 | #include "libavutil/opt.h" |
| 36 | 37 | #include "libavutil/time.h" |
| @@ -138,6 +139,8 @@ typedef struct HTTPContext { · 101 unmodified lines |
| 138 | 139 | char *new_location; |
| 139 | 140 | AVDictionary *redirect_cache; |
| 140 | 141 | uint64_t filesize_from_content_range; |
| 142 | + int respect_retry_after; |
| 143 | + unsigned int retry_after; |
| 141 | 144 | int reconnect_max_retries; |
| 142 | 145 | int reconnect_delay_total_max; |
| 143 | 146 | } HTTPContext; |
| @@ -180,6 +183,7 @@ static const AVOption options[] = { · 36 unmodified lines |
| 180 | 183 | { "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 }, |
| 181 | 184 | { "reconnect_max_retries", "the max number of times to retry a connection", OFFSET(reconnect_max_retries), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, D }, |
| 182 | 185 | { "reconnect_delay_total_max", "max total reconnect delay in seconds after which to give up", OFFSET(reconnect_delay_total_max), AV_OPT_TYPE_INT, { .i64 = 256 }, 0, UINT_MAX/1000/1000, D }, |
| 186 | + { "respect_retry_after", "respect the Retry-After header when retrying connections", OFFSET(respect_retry_after), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, D }, |
| 183 | 187 | { "listen", "listen on HTTP", OFFSET(listen), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 2, D | E }, |
| 184 | 188 | { "resource", "The resource requested by a client", OFFSET(resource), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E }, |
| 185 | 189 | { "reply_code", "The http status code to return to a client", OFFSET(reply_code), AV_OPT_TYPE_INT, { .i64 = 200}, INT_MIN, 599, E}, |
| @@ -393,6 +397,14 @@ redo: · 207 unmodified lines |
| 393 | 397 | reconnect_delay_total > s->reconnect_delay_total_max) |
| 394 | 398 | goto fail; |
| 395 | 399 | |
| 400 | + /* Both fields here are in seconds. */ |
| 401 | + if (s->respect_retry_after && s->retry_after > 0) { |
| 402 | + reconnect_delay = s->retry_after; |
| 403 | + if (reconnect_delay > s->reconnect_delay_max) |
| 404 | + goto fail; |
| 405 | + s->retry_after = 0; |
| 406 | + } |
| 407 | + |
| 396 | 408 | av_log(h, AV_LOG_WARNING, "Will reconnect at %"PRIu64" in %d second(s).\n", off, reconnect_delay); |
| 397 | 409 | ret = ff_network_sleep_interruptible(1000U * 1000 * reconnect_delay, &h->interrupt_callback); |
| 398 | 410 | if (ret != AVERROR(ETIMEDOUT)) |
| @@ -1241,6 +1253,18 @@ static int process_line(URLContext *h, char *line, int line_count, int *parsed_h · 842 unmodified lines |
| 1241 | 1253 | parse_expires(s, p); |
| 1242 | 1254 | } else if (!av_strcasecmp(tag, "Cache-Control")) { |
| 1243 | 1255 | parse_cache_control(s, p); |
| 1256 | + } else if (!av_strcasecmp(tag, "Retry-After")) { |
| 1257 | + /* The header can be either an integer that represents seconds, or a date. */ |
| 1258 | + struct tm tm; |
| 1259 | + int date_ret = parse_http_date(p, &tm); |
| 1260 | + if (!date_ret) { |
| 1261 | + time_t retry = av_timegm(&tm); |
| 1262 | + int64_t now = av_gettime() / 1000000; |
| 1263 | + int64_t diff = ((int64_t) retry) - now; |
| 1264 | + s->retry_after = (unsigned int) FFMAX(0, diff); |
| 1265 | + } else { |
| 1266 | + s->retry_after = strtoul(p, NULL, 10); |
| 1267 | + } |
| 1244 | 1268 | } |
| 1245 | 1269 | } |
| 1246 | 1270 | return 1; |