summaryrefslogtreecommitdiffstats
path: root/src/net/tcp/httpcore.c
diff options
context:
space:
mode:
authorMichael Brown2012-05-22 13:32:28 +0200
committerMichael Brown2012-05-23 00:43:36 +0200
commit5f2226aa3664d20b72ba622a4425d040390eaf2e (patch)
tree670a904e91da42919422cba8d5ba3c84d3cc6833 /src/net/tcp/httpcore.c
parent[http] Split construction of Authorization header out of http_step() (diff)
downloadipxe-5f2226aa3664d20b72ba622a4425d040390eaf2e.tar.gz
ipxe-5f2226aa3664d20b72ba622a4425d040390eaf2e.tar.xz
ipxe-5f2226aa3664d20b72ba622a4425d040390eaf2e.zip
[http] Provide credentials only when requested by server
Provide HTTP Basic authentication credentials only in response to a 401 Unauthorized response from the server. Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/net/tcp/httpcore.c')
-rw-r--r--src/net/tcp/httpcore.c144
1 files changed, 128 insertions, 16 deletions
diff --git a/src/net/tcp/httpcore.c b/src/net/tcp/httpcore.c
index 52989514..1eebf683 100644
--- a/src/net/tcp/httpcore.c
+++ b/src/net/tcp/httpcore.c
@@ -95,6 +95,10 @@ enum http_flags {
HTTP_CLIENT_KEEPALIVE = 0x0004,
/** Server will keep connection alive */
HTTP_SERVER_KEEPALIVE = 0x0008,
+ /** Discard the current request and try again */
+ HTTP_TRY_AGAIN = 0x0010,
+ /** Provide Basic authentication details */
+ HTTP_BASIC_AUTH = 0x0020,
};
/** HTTP receive state */
@@ -257,10 +261,11 @@ static void http_done ( struct http_request *http ) {
assert ( http->chunk_remaining == 0 );
/* Close partial transfer interface */
- intf_restart ( &http->partial, 0 );
+ if ( ! ( http->flags & HTTP_TRY_AGAIN ) )
+ intf_restart ( &http->partial, 0 );
/* Close everything unless we want to keep the connection alive */
- if ( ! ( http->flags & HTTP_CLIENT_KEEPALIVE ) ) {
+ if ( ! ( http->flags & ( HTTP_CLIENT_KEEPALIVE | HTTP_TRY_AGAIN ) ) ) {
http_close ( http, 0 );
return;
}
@@ -277,6 +282,14 @@ static void http_done ( struct http_request *http ) {
}
}
http->flags &= ~HTTP_SERVER_KEEPALIVE;
+
+ /* Retry the request if applicable */
+ if ( http->flags & HTTP_TRY_AGAIN ) {
+ http->flags &= ~HTTP_TRY_AGAIN;
+ http->flags |= HTTP_TX_PENDING;
+ http->rx_state = HTTP_RX_RESPONSE;
+ process_add ( &http->process );
+ }
}
/**
@@ -338,7 +351,7 @@ static int http_rx_response ( struct http_request *http, char *response ) {
* @v value HTTP header value
* @ret rc Return status code
*/
-static int http_rx_location ( struct http_request *http, const char *value ) {
+static int http_rx_location ( struct http_request *http, char *value ) {
int rc;
/* Redirect to new location */
@@ -360,8 +373,7 @@ static int http_rx_location ( struct http_request *http, const char *value ) {
* @v value HTTP header value
* @ret rc Return status code
*/
-static int http_rx_content_length ( struct http_request *http,
- const char *value ) {
+static int http_rx_content_length ( struct http_request *http, char *value ) {
struct block_device_capacity capacity;
size_t content_len;
char *endp;
@@ -385,6 +397,10 @@ static int http_rx_content_length ( struct http_request *http,
if ( ! ( http->flags & HTTP_HEAD_ONLY ) )
http->remaining = content_len;
+ /* Do nothing more if we are retrying the request */
+ if ( http->flags & HTTP_TRY_AGAIN )
+ return 0;
+
/* Use seek() to notify recipient of filesize */
xfer_seek ( &http->xfer, http->remaining );
xfer_seek ( &http->xfer, 0 );
@@ -406,8 +422,7 @@ static int http_rx_content_length ( struct http_request *http,
* @v value HTTP header value
* @ret rc Return status code
*/
-static int http_rx_transfer_encoding ( struct http_request *http,
- const char *value ) {
+static int http_rx_transfer_encoding ( struct http_request *http, char *value ){
if ( strcasecmp ( value, "chunked" ) == 0 ) {
/* Mark connection as using chunked transfer encoding */
@@ -424,7 +439,7 @@ static int http_rx_transfer_encoding ( struct http_request *http,
* @v value HTTP header value
* @ret rc Return status code
*/
-static int http_rx_connection ( struct http_request *http, const char *value ) {
+static int http_rx_connection ( struct http_request *http, char *value ) {
if ( strcasecmp ( value, "keep-alive" ) == 0 ) {
/* Mark connection as being kept alive by the server */
@@ -434,6 +449,88 @@ static int http_rx_connection ( struct http_request *http, const char *value ) {
return 0;
}
+/**
+ * Handle WWW-Authenticate Basic header
+ *
+ * @v http HTTP request
+ * @v params Parameters
+ * @ret rc Return status code
+ */
+static int http_rx_basic_auth ( struct http_request *http, char *params ) {
+
+ DBGC ( http, "HTTP %p Basic authentication required (%s)\n",
+ http, params );
+
+ /* If we received a 401 Unauthorized response, then retry
+ * using Basic authentication
+ */
+ if ( ( http->code == 401 ) &&
+ ( ! ( http->flags & HTTP_BASIC_AUTH ) ) &&
+ ( http->uri->user != NULL ) ) {
+ http->flags |= ( HTTP_TRY_AGAIN | HTTP_BASIC_AUTH );
+ }
+
+ return 0;
+}
+
+/** An HTTP WWW-Authenticate header handler */
+struct http_auth_header_handler {
+ /** Scheme (e.g. "Basic") */
+ const char *scheme;
+ /** Handle received parameters
+ *
+ * @v http HTTP request
+ * @v params Parameters
+ * @ret rc Return status code
+ */
+ int ( * rx ) ( struct http_request *http, char *params );
+};
+
+/** List of HTTP WWW-Authenticate header handlers */
+static struct http_auth_header_handler http_auth_header_handlers[] = {
+ {
+ .scheme = "Basic",
+ .rx = http_rx_basic_auth,
+ },
+ { NULL, NULL },
+};
+
+/**
+ * Handle HTTP WWW-Authenticate header
+ *
+ * @v http HTTP request
+ * @v value HTTP header value
+ * @ret rc Return status code
+ */
+static int http_rx_www_authenticate ( struct http_request *http, char *value ) {
+ struct http_auth_header_handler *handler;
+ char *separator;
+ char *scheme;
+ char *params;
+ int rc;
+
+ /* Extract scheme */
+ separator = strchr ( value, ' ' );
+ if ( ! separator ) {
+ DBGC ( http, "HTTP %p malformed WWW-Authenticate header\n",
+ http );
+ return -EINVAL_HEADER;
+ }
+ *separator = '\0';
+ scheme = value;
+ params = ( separator + 1 );
+
+ /* Hand off to header handler, if one exists */
+ for ( handler = http_auth_header_handlers; handler->scheme; handler++ ){
+ if ( strcasecmp ( scheme, handler->scheme ) == 0 ) {
+ if ( ( rc = handler->rx ( http, params ) ) != 0 )
+ return rc;
+ break;
+ }
+ }
+ return 0;
+}
+
/** An HTTP header handler */
struct http_header_handler {
/** Name (e.g. "Content-Length") */
@@ -446,7 +543,7 @@ struct http_header_handler {
*
* If an error is returned, the download will be aborted.
*/
- int ( * rx ) ( struct http_request *http, const char *value );
+ int ( * rx ) ( struct http_request *http, char *value );
};
/** List of HTTP header handlers */
@@ -467,6 +564,10 @@ static struct http_header_handler http_header_handlers[] = {
.header = "Connection",
.rx = http_rx_connection,
},
+ {
+ .header = "WWW-Authenticate",
+ .rx = http_rx_www_authenticate,
+ },
{ NULL, NULL }
};
@@ -488,8 +589,10 @@ static int http_rx_header ( struct http_request *http, char *header ) {
empty_line_buffer ( &http->linebuf );
/* Handle response code */
- if ( ( rc = http_response_to_rc ( http->code ) ) != 0 )
- return rc;
+ if ( ! ( http->flags & HTTP_TRY_AGAIN ) ) {
+ if ( ( rc = http_response_to_rc ( http->code ) ) != 0 )
+ return rc;
+ }
/* Move to next state */
if ( ( http->rx_state == HTTP_RX_HEADER ) &&
@@ -497,6 +600,10 @@ static int http_rx_header ( struct http_request *http, char *header ) {
DBGC ( http, "HTTP %p start of data\n", http );
http->rx_state = ( http->chunked ?
HTTP_RX_CHUNK_LEN : HTTP_RX_DATA );
+ if ( ( http->partial_len != 0 ) &&
+ ( ! ( http->flags & HTTP_TRY_AGAIN ) ) ) {
+ http->remaining = http->partial_len;
+ }
return 0;
} else {
DBGC ( http, "HTTP %p end of trailer\n", http );
@@ -560,8 +667,11 @@ static int http_rx_chunk_len ( struct http_request *http, char *length ) {
/* Use seek() to notify recipient of new filesize */
DBGC ( http, "HTTP %p start of chunk of length %zd\n",
http, http->chunk_remaining );
- xfer_seek ( &http->xfer, ( http->rx_len + http->chunk_remaining ) );
- xfer_seek ( &http->xfer, http->rx_len );
+ if ( ! ( http->flags & HTTP_TRY_AGAIN ) ) {
+ xfer_seek ( &http->xfer,
+ ( http->rx_len + http->chunk_remaining ) );
+ xfer_seek ( &http->xfer, http->rx_len );
+ }
/* Start receiving data */
http->rx_state = HTTP_RX_DATA;
@@ -630,7 +740,10 @@ static int http_socket_deliver ( struct http_request *http,
( http->remaining < data_len ) ) {
data_len = http->remaining;
}
- if ( http->rx_buffer != UNULL ) {
+ if ( http->flags & HTTP_TRY_AGAIN ) {
+ /* Discard all received data */
+ iob_pull ( iobuf, data_len );
+ } else if ( http->rx_buffer != UNULL ) {
/* Copy to partial transfer buffer */
copy_to_user ( http->rx_buffer, http->rx_len,
iobuf->data, data_len );
@@ -830,7 +943,7 @@ static void http_step ( struct http_request *http ) {
}
/* Construct authorisation, if applicable */
- if ( http->uri->user ) {
+ if ( http->flags & HTTP_BASIC_AUTH ) {
auth = http_basic_auth ( http );
if ( ! auth ) {
rc = -ENOMEM;
@@ -907,7 +1020,6 @@ static int http_partial_read ( struct http_request *http,
http->rx_buffer = buffer;
http->partial_start = offset;
http->partial_len = len;
- http->remaining = len;
/* Schedule request */
http->rx_state = HTTP_RX_RESPONSE;