2 * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 * Hyper Text Transfer Protocol (HTTP)
35 #include <gpxe/refcnt.h>
36 #include <gpxe/iobuf.h>
37 #include <gpxe/xfer.h>
38 #include <gpxe/open.h>
39 #include <gpxe/socket.h>
40 #include <gpxe/tcpip.h>
41 #include <gpxe/process.h>
42 #include <gpxe/linebuf.h>
43 #include <gpxe/features.h>
44 #include <gpxe/base64.h>
45 #include <gpxe/http.h>
47 FEATURE ( FEATURE_PROTOCOL, "HTTP", DHCP_EB_FEATURE_HTTP, 1 );
49 /** HTTP receive state */
62 /** Reference count */
64 /** Data transfer interface */
65 struct xfer_interface xfer;
67 /** URI being fetched */
69 /** Transport layer interface */
70 struct xfer_interface socket;
73 struct process process;
75 /** HTTP response code */
76 unsigned int response;
77 /** HTTP Content-Length */
78 size_t content_length;
79 /** Received length */
82 enum http_rx_state rx_state;
83 /** Line buffer for received header lines */
84 struct line_buffer linebuf;
90 * @v refcnt Reference counter
92 static void http_free ( struct refcnt *refcnt ) {
93 struct http_request *http =
94 container_of ( refcnt, struct http_request, refcnt );
96 uri_put ( http->uri );
97 empty_line_buffer ( &http->linebuf );
102 * Mark HTTP request as complete
104 * @v http HTTP request
105 * @v rc Return status code
107 static void http_done ( struct http_request *http, int rc ) {
109 /* Prevent further processing of any current packet */
110 http->rx_state = HTTP_RX_DEAD;
112 /* If we had a Content-Length, and the received content length
113 * isn't correct, flag an error
115 if ( http->content_length &&
116 ( http->content_length != http->rx_len ) ) {
117 DBGC ( http, "HTTP %p incorrect length %zd, should be %zd\n",
118 http, http->rx_len, http->content_length );
123 process_del ( &http->process );
125 /* Close all data transfer interfaces */
126 xfer_nullify ( &http->socket );
127 xfer_close ( &http->socket, rc );
128 xfer_nullify ( &http->xfer );
129 xfer_close ( &http->xfer, rc );
133 * Convert HTTP response code to return status code
135 * @v response HTTP response code
136 * @ret rc Return status code
138 static int http_response_to_rc ( unsigned int response ) {
139 switch ( response ) {
154 * Handle HTTP response
156 * @v http HTTP request
157 * @v response HTTP response
158 * @ret rc Return status code
160 static int http_rx_response ( struct http_request *http, char *response ) {
164 DBGC ( http, "HTTP %p response \"%s\"\n", http, response );
166 /* Check response starts with "HTTP/" */
167 if ( strncmp ( response, "HTTP/", 5 ) != 0 )
170 /* Locate and check response code */
171 spc = strchr ( response, ' ' );
174 http->response = strtoul ( spc, NULL, 10 );
175 if ( ( rc = http_response_to_rc ( http->response ) ) != 0 )
178 /* Move to received headers */
179 http->rx_state = HTTP_RX_HEADER;
184 * Handle HTTP Content-Length header
186 * @v http HTTP request
187 * @v value HTTP header value
188 * @ret rc Return status code
190 static int http_rx_content_length ( struct http_request *http,
191 const char *value ) {
194 http->content_length = strtoul ( value, &endp, 10 );
195 if ( *endp != '\0' ) {
196 DBGC ( http, "HTTP %p invalid Content-Length \"%s\"\n",
201 /* Use seek() to notify recipient of filesize */
202 xfer_seek ( &http->xfer, http->content_length, SEEK_SET );
203 xfer_seek ( &http->xfer, 0, SEEK_SET );
208 /** An HTTP header handler */
209 struct http_header_handler {
210 /** Name (e.g. "Content-Length") */
212 /** Handle received header
214 * @v http HTTP request
215 * @v value HTTP header value
216 * @ret rc Return status code
218 * If an error is returned, the download will be aborted.
220 int ( * rx ) ( struct http_request *http, const char *value );
223 /** List of HTTP header handlers */
224 static struct http_header_handler http_header_handlers[] = {
226 .header = "Content-Length",
227 .rx = http_rx_content_length,
235 * @v http HTTP request
236 * @v header HTTP header
237 * @ret rc Return status code
239 static int http_rx_header ( struct http_request *http, char *header ) {
240 struct http_header_handler *handler;
245 /* An empty header line marks the transition to the data phase */
247 DBGC ( http, "HTTP %p start of data\n", http );
248 empty_line_buffer ( &http->linebuf );
249 http->rx_state = HTTP_RX_DATA;
253 DBGC ( http, "HTTP %p header \"%s\"\n", http, header );
255 /* Split header at the ": " */
256 separator = strstr ( header, ": " );
258 DBGC ( http, "HTTP %p malformed header\n", http );
262 value = ( separator + 2 );
264 /* Hand off to header handler, if one exists */
265 for ( handler = http_header_handlers ; handler->header ; handler++ ) {
266 if ( strcasecmp ( header, handler->header ) == 0 ) {
267 if ( ( rc = handler->rx ( http, value ) ) != 0 )
275 /** An HTTP line-based data handler */
276 struct http_line_handler {
279 * @v http HTTP request
280 * @v line Line to handle
281 * @ret rc Return status code
283 int ( * rx ) ( struct http_request *http, char *line );
286 /** List of HTTP line-based data handlers */
287 static struct http_line_handler http_line_handlers[] = {
288 [HTTP_RX_RESPONSE] = { .rx = http_rx_response },
289 [HTTP_RX_HEADER] = { .rx = http_rx_header },
293 * Handle new data arriving via HTTP connection in the data phase
295 * @v http HTTP request
296 * @v iobuf I/O buffer
297 * @ret rc Return status code
299 static int http_rx_data ( struct http_request *http,
300 struct io_buffer *iobuf ) {
303 /* Update received length */
304 http->rx_len += iob_len ( iobuf );
306 /* Hand off data buffer */
307 if ( ( rc = xfer_deliver_iob ( &http->xfer, iobuf ) ) != 0 )
310 /* If we have reached the content-length, stop now */
311 if ( http->content_length &&
312 ( http->rx_len >= http->content_length ) ) {
313 http_done ( http, 0 );
320 * Handle new data arriving via HTTP connection
322 * @v socket Transport layer interface
323 * @v iobuf I/O buffer
324 * @v meta Data transfer metadata
325 * @ret rc Return status code
327 static int http_socket_deliver_iob ( struct xfer_interface *socket,
328 struct io_buffer *iobuf,
329 struct xfer_metadata *meta __unused ) {
330 struct http_request *http =
331 container_of ( socket, struct http_request, socket );
332 struct http_line_handler *lh;
337 while ( iob_len ( iobuf ) ) {
338 switch ( http->rx_state ) {
340 /* Do no further processing */
343 /* Once we're into the data phase, just fill
346 rc = http_rx_data ( http, iob_disown ( iobuf ) );
348 case HTTP_RX_RESPONSE:
350 /* In the other phases, buffer and process a
353 len = line_buffer ( &http->linebuf, iobuf->data,
357 DBGC ( http, "HTTP %p could not buffer line: "
358 "%s\n", http, strerror ( rc ) );
361 iob_pull ( iobuf, len );
362 line = buffered_line ( &http->linebuf );
364 lh = &http_line_handlers[http->rx_state];
365 if ( ( rc = lh->rx ( http, line ) ) != 0 )
377 http_done ( http, rc );
387 static void http_step ( struct process *process ) {
388 struct http_request *http =
389 container_of ( process, struct http_request, process );
390 const char *path = http->uri->path;
391 const char *host = http->uri->host;
392 const char *query = http->uri->query;
393 const char *user = http->uri->user;
394 const char *password =
395 ( http->uri->password ? http->uri->password : "" );
396 size_t user_pw_len = ( user ? ( strlen ( user ) + 1 /* ":" */ +
397 strlen ( password ) ) : 0 );
398 size_t user_pw_base64_len = base64_encoded_len ( user_pw_len );
399 char user_pw[ user_pw_len + 1 /* NUL */ ];
400 char user_pw_base64[ user_pw_base64_len + 1 /* NUL */ ];
403 if ( xfer_window ( &http->socket ) ) {
405 /* We want to execute only once */
406 process_del ( &http->process );
408 /* Construct authorisation, if applicable */
411 ssize_t remaining = sizeof ( user_pw );
414 /* URI-decode the username and password */
415 len = uri_decode ( user, buf, remaining );
418 *(remaining--, buf++) = ':';
419 len = uri_decode ( password, buf, remaining );
422 assert ( remaining >= 0 );
424 /* Base64-encode the "user:password" string */
425 base64_encode ( user_pw, user_pw_base64 );
428 /* Send GET request */
429 if ( ( rc = xfer_printf ( &http->socket,
430 "GET %s%s%s HTTP/1.0\r\n"
431 "User-Agent: gPXE/" VERSION "\r\n"
435 ( path ? path : "/" ),
436 ( query ? "?" : "" ),
437 ( query ? query : "" ),
439 "Authorization: Basic " : "" ),
440 ( user ? user_pw_base64 : "" ),
441 ( user ? "\r\n" : "" ),
443 http_done ( http, rc );
449 * HTTP connection closed by network stack
451 * @v socket Transport layer interface
452 * @v rc Reason for close
454 static void http_socket_close ( struct xfer_interface *socket, int rc ) {
455 struct http_request *http =
456 container_of ( socket, struct http_request, socket );
458 DBGC ( http, "HTTP %p socket closed: %s\n",
459 http, strerror ( rc ) );
461 http_done ( http, rc );
464 /** HTTP socket operations */
465 static struct xfer_interface_operations http_socket_operations = {
466 .close = http_socket_close,
467 .vredirect = xfer_vreopen,
468 .window = unlimited_xfer_window,
469 .alloc_iob = default_xfer_alloc_iob,
470 .deliver_iob = http_socket_deliver_iob,
471 .deliver_raw = xfer_deliver_as_iob,
475 * Close HTTP data transfer interface
477 * @v xfer Data transfer interface
478 * @v rc Reason for close
480 static void http_xfer_close ( struct xfer_interface *xfer, int rc ) {
481 struct http_request *http =
482 container_of ( xfer, struct http_request, xfer );
484 DBGC ( http, "HTTP %p interface closed: %s\n",
485 http, strerror ( rc ) );
487 http_done ( http, rc );
490 /** HTTP data transfer interface operations */
491 static struct xfer_interface_operations http_xfer_operations = {
492 .close = http_xfer_close,
493 .vredirect = ignore_xfer_vredirect,
494 .window = unlimited_xfer_window,
495 .alloc_iob = default_xfer_alloc_iob,
496 .deliver_iob = xfer_deliver_as_raw,
497 .deliver_raw = ignore_xfer_deliver_raw,
501 * Initiate an HTTP connection, with optional filter
503 * @v xfer Data transfer interface
504 * @v uri Uniform Resource Identifier
505 * @v default_port Default port number
506 * @v filter Filter to apply to socket, or NULL
507 * @ret rc Return status code
509 int http_open_filter ( struct xfer_interface *xfer, struct uri *uri,
510 unsigned int default_port,
511 int ( * filter ) ( struct xfer_interface *xfer,
512 struct xfer_interface **next ) ) {
513 struct http_request *http;
514 struct sockaddr_tcpip server;
515 struct xfer_interface *socket;
522 /* Allocate and populate HTTP structure */
523 http = zalloc ( sizeof ( *http ) );
526 http->refcnt.free = http_free;
527 xfer_init ( &http->xfer, &http_xfer_operations, &http->refcnt );
528 http->uri = uri_get ( uri );
529 xfer_init ( &http->socket, &http_socket_operations, &http->refcnt );
530 process_init ( &http->process, http_step, &http->refcnt );
533 memset ( &server, 0, sizeof ( server ) );
534 server.st_port = htons ( uri_port ( http->uri, default_port ) );
535 socket = &http->socket;
537 if ( ( rc = filter ( socket, &socket ) ) != 0 )
540 if ( ( rc = xfer_open_named_socket ( socket, SOCK_STREAM,
541 ( struct sockaddr * ) &server,
542 uri->host, NULL ) ) != 0 )
545 /* Attach to parent interface, mortalise self, and return */
546 xfer_plug_plug ( &http->xfer, xfer );
547 ref_put ( &http->refcnt );
551 DBGC ( http, "HTTP %p could not create request: %s\n",
552 http, strerror ( rc ) );
553 http_done ( http, rc );
554 ref_put ( &http->refcnt );
559 * Initiate an HTTP connection
561 * @v xfer Data transfer interface
562 * @v uri Uniform Resource Identifier
563 * @ret rc Return status code
565 static int http_open ( struct xfer_interface *xfer, struct uri *uri ) {
566 return http_open_filter ( xfer, uri, HTTP_PORT, NULL );
569 /** HTTP URI opener */
570 struct uri_opener http_uri_opener __uri_opener = {