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>
44 #include <gpxe/http.h>
46 /** HTTP receive state */
59 /** Reference count */
61 /** Data transfer interface */
62 struct xfer_interface xfer;
64 /** URI being fetched */
66 /** Transport layer interface */
67 struct xfer_interface socket;
70 struct process process;
72 /** HTTP response code */
73 unsigned int response;
74 /** HTTP Content-Length */
75 size_t content_length;
76 /** Received length */
79 enum http_rx_state rx_state;
80 /** Line buffer for received header lines */
81 struct line_buffer linebuf;
87 * @v refcnt Reference counter
89 static void http_free ( struct refcnt *refcnt ) {
90 struct http_request *http =
91 container_of ( refcnt, struct http_request, refcnt );
93 uri_put ( http->uri );
94 empty_line_buffer ( &http->linebuf );
99 * Mark HTTP request as complete
101 * @v http HTTP request
102 * @v rc Return status code
104 static void http_done ( struct http_request *http, int rc ) {
106 /* Prevent further processing of any current packet */
107 http->rx_state = HTTP_RX_DEAD;
109 /* If we had a Content-Length, and the received content length
110 * isn't correct, flag an error
112 if ( http->content_length &&
113 ( http->content_length != http->rx_len ) ) {
114 DBGC ( http, "HTTP %p incorrect length %zd, should be %zd\n",
115 http, http->rx_len, http->content_length );
120 process_del ( &http->process );
122 /* Close all data transfer interfaces */
123 xfer_nullify ( &http->socket );
124 xfer_close ( &http->socket, rc );
125 xfer_nullify ( &http->xfer );
126 xfer_close ( &http->xfer, rc );
130 * Convert HTTP response code to return status code
132 * @v response HTTP response code
133 * @ret rc Return status code
135 static int http_response_to_rc ( unsigned int response ) {
136 switch ( response ) {
149 * Handle HTTP response
151 * @v http HTTP request
152 * @v response HTTP response
153 * @ret rc Return status code
155 static int http_rx_response ( struct http_request *http, char *response ) {
159 DBGC ( http, "HTTP %p response \"%s\"\n", http, response );
161 /* Check response starts with "HTTP/" */
162 if ( strncmp ( response, "HTTP/", 5 ) != 0 )
165 /* Locate and check response code */
166 spc = strchr ( response, ' ' );
169 http->response = strtoul ( spc, NULL, 10 );
170 if ( ( rc = http_response_to_rc ( http->response ) ) != 0 )
173 /* Move to received headers */
174 http->rx_state = HTTP_RX_HEADER;
179 * Handle HTTP Content-Length header
181 * @v http HTTP request
182 * @v value HTTP header value
183 * @ret rc Return status code
185 static int http_rx_content_length ( struct http_request *http,
186 const char *value ) {
189 http->content_length = strtoul ( value, &endp, 10 );
190 if ( *endp != '\0' ) {
191 DBGC ( http, "HTTP %p invalid Content-Length \"%s\"\n",
196 /* Use seek() to notify recipient of filesize */
197 xfer_seek ( &http->xfer, http->content_length, SEEK_SET );
198 xfer_seek ( &http->xfer, 0, SEEK_SET );
203 /** An HTTP header handler */
204 struct http_header_handler {
205 /** Name (e.g. "Content-Length") */
207 /** Handle received header
209 * @v http HTTP request
210 * @v value HTTP header value
211 * @ret rc Return status code
213 * If an error is returned, the download will be aborted.
215 int ( * rx ) ( struct http_request *http, const char *value );
218 /** List of HTTP header handlers */
219 struct http_header_handler http_header_handlers[] = {
221 .header = "Content-Length",
222 .rx = http_rx_content_length,
230 * @v http HTTP request
231 * @v header HTTP header
232 * @ret rc Return status code
234 static int http_rx_header ( struct http_request *http, char *header ) {
235 struct http_header_handler *handler;
240 /* An empty header line marks the transition to the data phase */
242 DBGC ( http, "HTTP %p start of data\n", http );
243 empty_line_buffer ( &http->linebuf );
244 http->rx_state = HTTP_RX_DATA;
248 DBGC ( http, "HTTP %p header \"%s\"\n", http, header );
250 /* Split header at the ": " */
251 separator = strstr ( header, ": " );
253 DBGC ( http, "HTTP %p malformed header\n", http );
257 value = ( separator + 2 );
259 /* Hand off to header handler, if one exists */
260 for ( handler = http_header_handlers ; handler->header ; handler++ ) {
261 if ( strcasecmp ( header, handler->header ) == 0 ) {
262 if ( ( rc = handler->rx ( http, value ) ) != 0 )
270 /** An HTTP line-based data handler */
271 struct http_line_handler {
274 * @v http HTTP request
275 * @v line Line to handle
276 * @ret rc Return status code
278 int ( * rx ) ( struct http_request *http, char *line );
281 /** List of HTTP line-based data handlers */
282 struct http_line_handler http_line_handlers[] = {
283 [HTTP_RX_RESPONSE] = { .rx = http_rx_response },
284 [HTTP_RX_HEADER] = { .rx = http_rx_header },
288 * Handle new data arriving via HTTP connection in the data phase
290 * @v http HTTP request
291 * @v iobuf I/O buffer
292 * @ret rc Return status code
294 static int http_rx_data ( struct http_request *http,
295 struct io_buffer *iobuf ) {
298 /* Update received length */
299 http->rx_len += iob_len ( iobuf );
301 /* Hand off data buffer */
302 if ( ( rc = xfer_deliver_iob ( &http->xfer, iobuf ) ) != 0 )
305 /* If we have reached the content-length, stop now */
306 if ( http->content_length &&
307 ( http->rx_len >= http->content_length ) ) {
308 http_done ( http, 0 );
315 * Handle new data arriving via HTTP connection
317 * @v socket Transport layer interface
318 * @v iobuf I/O buffer
319 * @v meta Data transfer metadata, or NULL
320 * @ret rc Return status code
322 static int http_socket_deliver_iob ( struct xfer_interface *socket,
323 struct io_buffer *iobuf,
324 struct xfer_metadata *meta __unused ) {
325 struct http_request *http =
326 container_of ( socket, struct http_request, socket );
327 struct http_line_handler *lh;
332 while ( iob_len ( iobuf ) ) {
333 switch ( http->rx_state ) {
335 /* Do no further processing */
338 /* Once we're into the data phase, just fill
341 rc = http_rx_data ( http, iobuf );
344 case HTTP_RX_RESPONSE:
346 /* In the other phases, buffer and process a
349 len = line_buffer ( &http->linebuf, iobuf->data,
353 DBGC ( http, "HTTP %p could not buffer line: "
354 "%s\n", http, strerror ( rc ) );
357 iob_pull ( iobuf, len );
358 line = buffered_line ( &http->linebuf );
360 lh = &http_line_handlers[http->rx_state];
361 if ( ( rc = lh->rx ( http, line ) ) != 0 )
373 http_done ( http, rc );
383 static void http_step ( struct process *process ) {
384 struct http_request *http =
385 container_of ( process, struct http_request, process );
386 const char *path = http->uri->path;
387 const char *host = http->uri->host;
388 const char *query = http->uri->query;
391 if ( xfer_ready ( &http->socket ) == 0 ) {
392 process_del ( &http->process );
393 if ( ( rc = xfer_printf ( &http->socket,
394 "GET %s%s%s HTTP/1.1\r\n"
395 "User-Agent: gPXE/" VERSION "\r\n"
398 ( path ? path : "/" ),
399 ( query ? "?" : "" ),
400 ( query ? query : "" ),
402 http_done ( http, rc );
408 * HTTP connection closed by network stack
410 * @v socket Transport layer interface
411 * @v rc Reason for close
413 static void http_socket_close ( struct xfer_interface *socket, int rc ) {
414 struct http_request *http =
415 container_of ( socket, struct http_request, socket );
417 DBGC ( http, "HTTP %p socket closed: %s\n",
418 http, strerror ( rc ) );
420 http_done ( http, rc );
423 /** HTTP socket operations */
424 static struct xfer_interface_operations http_socket_operations = {
425 .close = http_socket_close,
426 .vredirect = xfer_vopen,
427 .request = ignore_xfer_request,
428 .seek = ignore_xfer_seek,
429 .alloc_iob = default_xfer_alloc_iob,
430 .deliver_iob = http_socket_deliver_iob,
431 .deliver_raw = xfer_deliver_as_iob,
435 * Close HTTP data transfer interface
437 * @v xfer Data transfer interface
438 * @v rc Reason for close
440 static void http_xfer_close ( struct xfer_interface *xfer, int rc ) {
441 struct http_request *http =
442 container_of ( xfer, struct http_request, xfer );
444 DBGC ( http, "HTTP %p interface closed: %s\n",
445 http, strerror ( rc ) );
447 http_done ( http, rc );
450 /** HTTP data transfer interface operations */
451 static struct xfer_interface_operations http_xfer_operations = {
452 .close = http_xfer_close,
453 .vredirect = ignore_xfer_vredirect,
454 .request = ignore_xfer_request,
455 .seek = ignore_xfer_seek,
456 .alloc_iob = default_xfer_alloc_iob,
457 .deliver_iob = xfer_deliver_as_raw,
458 .deliver_raw = ignore_xfer_deliver_raw,
462 * Initiate an HTTP connection
464 * @v xfer Data transfer interface
465 * @v uri Uniform Resource Identifier
466 * @ret rc Return status code
468 int http_open ( struct xfer_interface *xfer, struct uri *uri ) {
469 struct http_request *http;
470 struct sockaddr_tcpip server;
477 /* Allocate and populate HTTP structure */
478 http = malloc ( sizeof ( *http ) );
481 memset ( http, 0, sizeof ( *http ) );
482 http->refcnt.free = http_free;
483 xfer_init ( &http->xfer, &http_xfer_operations, &http->refcnt );
484 http->uri = uri_get ( uri );
485 xfer_init ( &http->socket, &http_socket_operations, &http->refcnt );
486 process_init ( &http->process, http_step, &http->refcnt );
489 memset ( &server, 0, sizeof ( server ) );
490 server.st_port = htons ( uri_port ( http->uri, HTTP_PORT ) );
491 if ( ( rc = xfer_open_named_socket ( &http->socket, SOCK_STREAM,
492 ( struct sockaddr * ) &server,
493 uri->host, NULL ) ) != 0 )
497 if ( strcmp ( http->uri->scheme, "https" ) == 0 ) {
498 st->st_port = htons ( uri_port ( http->uri, HTTPS_PORT ) );
499 if ( ( rc = add_tls ( &http->stream ) ) != 0 )
504 /* Attach to parent interface, mortalise self, and return */
505 xfer_plug_plug ( &http->xfer, xfer );
506 ref_put ( &http->refcnt );
510 DBGC ( http, "HTTP %p could not create request: %s\n",
511 http, strerror ( rc ) );
512 http_done ( http, rc );
513 ref_put ( &http->refcnt );
517 /** HTTP URI opener */
518 struct uri_opener http_uri_opener __uri_opener = {
523 /** HTTPS URI opener */
524 struct uri_opener https_uri_opener __uri_opener = {