summaryrefslogtreecommitdiffstats
path: root/src/net/tcp
diff options
context:
space:
mode:
authorMichael Brown2014-03-07 18:19:36 +0100
committerMichael Brown2014-03-07 18:32:26 +0100
commit42bf3b9aa949f5b53eeb164a4a405822a7038a0e (patch)
tree252788d68ddc8b679ea3739f4fcf8ab50855854a /src/net/tcp
parent[http] Use a retry timer to trigger retried requests (diff)
downloadipxe-42bf3b9aa949f5b53eeb164a4a405822a7038a0e.tar.gz
ipxe-42bf3b9aa949f5b53eeb164a4a405822a7038a0e.tar.xz
ipxe-42bf3b9aa949f5b53eeb164a4a405822a7038a0e.zip
[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/tcp')
-rw-r--r--src/net/tcp/httpcore.c46
1 files changed, 45 insertions, 1 deletions
diff --git a/src/net/tcp/httpcore.c b/src/net/tcp/httpcore.c
index 3b2aacde..aa15b112 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 }
};