mirror of https://github.com/ipxe/ipxe.git
[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>pull/17/head
parent
0d657b8e94
commit
42bf3b9aa9
|
@ -43,6 +43,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
|
||||||
#include <ipxe/tcpip.h>
|
#include <ipxe/tcpip.h>
|
||||||
#include <ipxe/process.h>
|
#include <ipxe/process.h>
|
||||||
#include <ipxe/retry.h>
|
#include <ipxe/retry.h>
|
||||||
|
#include <ipxe/timer.h>
|
||||||
#include <ipxe/linebuf.h>
|
#include <ipxe/linebuf.h>
|
||||||
#include <ipxe/base64.h>
|
#include <ipxe/base64.h>
|
||||||
#include <ipxe/base16.h>
|
#include <ipxe/base16.h>
|
||||||
|
@ -88,6 +89,9 @@ FILE_LICENCE ( GPL2_OR_LATER );
|
||||||
/** Block size used for HTTP block device request */
|
/** Block size used for HTTP block device request */
|
||||||
#define HTTP_BLKSIZE 512
|
#define HTTP_BLKSIZE 512
|
||||||
|
|
||||||
|
/** Retry delay used when we cannot understand the Retry-After header */
|
||||||
|
#define HTTP_RETRY_SECONDS 5
|
||||||
|
|
||||||
/** HTTP flags */
|
/** HTTP flags */
|
||||||
enum http_flags {
|
enum http_flags {
|
||||||
/** Request is waiting to be transmitted */
|
/** Request is waiting to be transmitted */
|
||||||
|
@ -185,6 +189,8 @@ struct http_request {
|
||||||
|
|
||||||
/** Request retry timer */
|
/** Request retry timer */
|
||||||
struct retry_timer 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 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;
|
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 */
|
/** An HTTP header handler */
|
||||||
struct http_header_handler {
|
struct http_header_handler {
|
||||||
/** Name (e.g. "Content-Length") */
|
/** Name (e.g. "Content-Length") */
|
||||||
|
@ -701,6 +741,10 @@ static struct http_header_handler http_header_handlers[] = {
|
||||||
.header = "WWW-Authenticate",
|
.header = "WWW-Authenticate",
|
||||||
.rx = http_rx_www_authenticate,
|
.rx = http_rx_www_authenticate,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.header = "Retry-After",
|
||||||
|
.rx = http_rx_retry_after,
|
||||||
|
},
|
||||||
{ NULL, NULL }
|
{ NULL, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue