diff options
author | Michael Brown <mcb30@ipxe.org> | 2014-03-07 17:19:36 +0000 |
---|---|---|
committer | Michael Brown <mcb30@ipxe.org> | 2014-03-07 17:32:26 +0000 |
commit | 42bf3b9aa949f5b53eeb164a4a405822a7038a0e (patch) | |
tree | 252788d68ddc8b679ea3739f4fcf8ab50855854a /src/net | |
parent | 0d657b8e944c1d126fe2dfc07d808818a31bc270 (diff) | |
download | ipxe-42bf3b9aa949f5b53eeb164a4a405822a7038a0e.zip ipxe-42bf3b9aa949f5b53eeb164a4a405822a7038a0e.tar.gz ipxe-42bf3b9aa949f5b53eeb164a4a405822a7038a0e.tar.bz2 |
[http] Automatically retry request on a 503 Service Unavailable
A web server may return a 503 Service Unavailable response along with
a Retry-After header to direct the client to retry the request at a
later time.
The Retry-After header may be a number of seconds, or a full HTTP
timestamp (e.g. "Fri, 7 Mar 2014 17:22:14 GMT"). We have no
reasonable way of parsing a full HTTP timestamp; if the server chooses
to use this format then we simply retry after a fixed 5-second delay.
As per RFC 2616, in the absence of a Retry-After header we treat a
status code of 503 Service Unavailable as being equivalent to 500
Internal Server Error, and immediately fail the request.
Requested-by: Suresh Sundriyal <ssundriy@vmware.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/net')
-rw-r--r-- | src/net/tcp/httpcore.c | 46 |
1 files changed, 45 insertions, 1 deletions
diff --git a/src/net/tcp/httpcore.c b/src/net/tcp/httpcore.c index 3b2aacd..aa15b11 100644 --- a/src/net/tcp/httpcore.c +++ b/src/net/tcp/httpcore.c @@ -43,6 +43,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/tcpip.h> #include <ipxe/process.h> #include <ipxe/retry.h> +#include <ipxe/timer.h> #include <ipxe/linebuf.h> #include <ipxe/base64.h> #include <ipxe/base16.h> @@ -88,6 +89,9 @@ FILE_LICENCE ( GPL2_OR_LATER ); /** Block size used for HTTP block device request */ #define HTTP_BLKSIZE 512 +/** Retry delay used when we cannot understand the Retry-After header */ +#define HTTP_RETRY_SECONDS 5 + /** HTTP flags */ enum http_flags { /** Request is waiting to be transmitted */ @@ -185,6 +189,8 @@ struct http_request { /** Request retry timer */ struct retry_timer timer; + /** Retry delay (in timer ticks) */ + unsigned long retry_delay; }; /** @@ -342,7 +348,8 @@ static void http_done ( struct http_request *http ) { } /* Start request retry timer */ - start_timer_nodelay ( &http->timer ); + start_timer_fixed ( &http->timer, http->retry_delay ); + http->retry_delay = 0; } /** @@ -664,6 +671,39 @@ static int http_rx_www_authenticate ( struct http_request *http, char *value ) { return 0; } +/** + * Handle HTTP Retry-After header + * + * @v http HTTP request + * @v value HTTP header value + * @ret rc Return status code + */ +static int http_rx_retry_after ( struct http_request *http, char *value ) { + unsigned long seconds; + char *endp; + + DBGC ( http, "HTTP %p retry requested (%s)\n", http, value ); + + /* If we received a 503 Service Unavailable response, then + * retry after the specified number of seconds. If the value + * is not a simple number of seconds (e.g. a full HTTP date), + * then retry after a fixed delay, since we don't have code + * able to parse full HTTP dates. + */ + if ( http->code == 503 ) { + seconds = strtoul ( value, &endp, 10 ); + if ( *endp != '\0' ) { + seconds = HTTP_RETRY_SECONDS; + DBGC ( http, "HTTP %p cannot understand \"%s\"; " + "using %ld seconds\n", http, value, seconds ); + } + http->flags |= HTTP_TRY_AGAIN; + http->retry_delay = ( seconds * TICKS_PER_SEC ); + } + + return 0; +} + /** An HTTP header handler */ struct http_header_handler { /** Name (e.g. "Content-Length") */ @@ -701,6 +741,10 @@ static struct http_header_handler http_header_handlers[] = { .header = "WWW-Authenticate", .rx = http_rx_www_authenticate, }, + { + .header = "Retry-After", + .rx = http_rx_retry_after, + }, { NULL, NULL } }; |