[http] Use a retry timer to trigger retried requests

Use a retry timer to allow for the possibility of deferring a retried
request.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
pull/17/head
Michael Brown 2014-03-07 16:45:45 +00:00
parent 08f9170ba4
commit 0d657b8e94
1 changed files with 44 additions and 15 deletions

View File

@ -42,6 +42,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/socket.h> #include <ipxe/socket.h>
#include <ipxe/tcpip.h> #include <ipxe/tcpip.h>
#include <ipxe/process.h> #include <ipxe/process.h>
#include <ipxe/retry.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>
@ -103,6 +104,8 @@ enum http_flags {
HTTP_BASIC_AUTH = 0x0020, HTTP_BASIC_AUTH = 0x0020,
/** Provide Digest authentication details */ /** Provide Digest authentication details */
HTTP_DIGEST_AUTH = 0x0040, HTTP_DIGEST_AUTH = 0x0040,
/** Socket must be reopened */
HTTP_REOPEN_SOCKET = 0x0080,
}; };
/** HTTP receive state */ /** HTTP receive state */
@ -179,6 +182,9 @@ struct http_request {
char *auth_nonce; char *auth_nonce;
/** Authentication opaque string (if any) */ /** Authentication opaque string (if any) */
char *auth_opaque; char *auth_opaque;
/** Request retry timer */
struct retry_timer timer;
}; };
/** /**
@ -249,13 +255,43 @@ static int http_socket_open ( struct http_request *http ) {
return 0; return 0;
} }
/**
* Retry HTTP request
*
* @v timer Retry timer
* @v fail Failure indicator
*/
static void http_retry ( struct retry_timer *timer, int fail __unused ) {
struct http_request *http =
container_of ( timer, struct http_request, timer );
int rc;
/* Reopen socket if required */
if ( http->flags & HTTP_REOPEN_SOCKET ) {
http->flags &= ~HTTP_REOPEN_SOCKET;
DBGC ( http, "HTTP %p reopening connection\n", http );
if ( ( rc = http_socket_open ( http ) ) != 0 ) {
http_close ( http, rc );
return;
}
}
/* Retry the request if applicable */
if ( http->flags & HTTP_TRY_AGAIN ) {
http->flags &= ~HTTP_TRY_AGAIN;
DBGC ( http, "HTTP %p retrying request\n", http );
http->flags |= HTTP_TX_PENDING;
http->rx_state = HTTP_RX_RESPONSE;
process_add ( &http->process );
}
}
/** /**
* Mark HTTP request as completed successfully * Mark HTTP request as completed successfully
* *
* @v http HTTP request * @v http HTTP request
*/ */
static void http_done ( struct http_request *http ) { static void http_done ( struct http_request *http ) {
int rc;
/* If we are not at an appropriate stage of the protocol /* If we are not at an appropriate stage of the protocol
* (including being in the middle of a chunked transfer), * (including being in the middle of a chunked transfer),
@ -296,25 +332,17 @@ static void http_done ( struct http_request *http ) {
} }
/* If the server is not intending to keep the connection /* If the server is not intending to keep the connection
* alive, then reopen the socket. * alive, then close the socket and mark it as requiring
* reopening.
*/ */
if ( ! ( http->flags & HTTP_SERVER_KEEPALIVE ) ) { if ( ! ( http->flags & HTTP_SERVER_KEEPALIVE ) ) {
DBGC ( http, "HTTP %p reopening connection\n", http );
intf_restart ( &http->socket, 0 ); intf_restart ( &http->socket, 0 );
if ( ( rc = http_socket_open ( http ) ) != 0 ) { http->flags &= ~HTTP_SERVER_KEEPALIVE;
http_close ( http, rc ); http->flags |= HTTP_REOPEN_SOCKET;
return;
}
} }
http->flags &= ~HTTP_SERVER_KEEPALIVE;
/* Retry the request if applicable */ /* Start request retry timer */
if ( http->flags & HTTP_TRY_AGAIN ) { start_timer_nodelay ( &http->timer );
http->flags &= ~HTTP_TRY_AGAIN;
http->flags |= HTTP_TX_PENDING;
http->rx_state = HTTP_RX_RESPONSE;
process_add ( &http->process );
}
} }
/** /**
@ -1467,6 +1495,7 @@ int http_open_filter ( struct interface *xfer, struct uri *uri,
http->filter = filter; http->filter = filter;
intf_init ( &http->socket, &http_socket_desc, &http->refcnt ); intf_init ( &http->socket, &http_socket_desc, &http->refcnt );
process_init ( &http->process, &http_process_desc, &http->refcnt ); process_init ( &http->process, &http_process_desc, &http->refcnt );
timer_init ( &http->timer, http_retry, &http->refcnt );
http->flags = HTTP_TX_PENDING; http->flags = HTTP_TX_PENDING;
/* Open socket */ /* Open socket */