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)
34 #include <gpxe/async.h>
36 #include <gpxe/buffer.h>
37 #include <gpxe/download.h>
38 #include <gpxe/resolv.h>
39 #include <gpxe/http.h>
41 static struct async_operations http_async_operations;
43 static inline struct http_request *
44 tcp_to_http ( struct tcp_application *app ) {
45 return container_of ( app, struct http_request, tcp );
49 * Mark HTTP request as complete
51 * @v http HTTP request
52 * @v rc Return status code
55 static void http_done ( struct http_request *http, int rc ) {
57 /* Close TCP connection */
58 tcp_close ( &http->tcp );
60 /* Prevent further processing of any current packet */
61 http->rx_state = HTTP_RX_DEAD;
63 /* Free up any dynamically allocated storage */
64 empty_line_buffer ( &http->linebuf );
66 /* If we had a Content-Length, and the received content length
67 * isn't correct, flag an error
69 if ( http->content_length &&
70 ( http->content_length != http->buffer->fill ) ) {
71 DBGC ( http, "HTTP %p incorrect length %zd, should be %zd\n",
72 http, http->buffer->fill, http->content_length );
76 /* Mark async operation as complete */
77 async_done ( &http->async, rc );
81 * Convert HTTP response code to return status code
83 * @v response HTTP response code
84 * @ret rc Return status code
86 static int http_response_to_rc ( unsigned int response ) {
100 * Handle HTTP response
102 * @v http HTTP request
103 * @v response HTTP response
105 static void http_rx_response ( struct http_request *http, char *response ) {
109 DBGC ( http, "HTTP %p response \"%s\"\n", http, response );
111 /* Check response starts with "HTTP/" */
112 if ( strncmp ( response, "HTTP/", 5 ) != 0 )
115 /* Locate and check response code */
116 spc = strchr ( response, ' ' );
119 http->response = strtoul ( spc, NULL, 10 );
120 if ( ( rc = http_response_to_rc ( http->response ) ) != 0 )
123 /* Move to received headers */
124 http->rx_state = HTTP_RX_HEADER;
128 DBGC ( http, "HTTP %p bad response\n", http );
129 http_done ( http, rc );
134 * Handle HTTP Content-Length header
136 * @v http HTTP request
137 * @v value HTTP header value
138 * @ret rc Return status code
140 static int http_rx_content_length ( struct http_request *http,
141 const char *value ) {
145 http->content_length = strtoul ( value, &endp, 10 );
146 if ( *endp != '\0' ) {
147 DBGC ( http, "HTTP %p invalid Content-Length \"%s\"\n",
152 /* Try to presize the receive buffer */
153 if ( ( rc = expand_buffer ( http->buffer,
154 http->content_length ) ) != 0 ) {
155 /* May as well abandon the download now; it will fail */
156 DBGC ( http, "HTTP %p could not presize buffer: %s\n",
157 http, strerror ( rc ) );
165 * An HTTP header handler
168 struct http_header_handler {
169 /** Name (e.g. "Content-Length") */
171 /** Handle received header
173 * @v http HTTP request
174 * @v value HTTP header value
175 * @ret rc Return status code
177 * If an error is returned, the download will be aborted.
179 int ( * rx ) ( struct http_request *http, const char *value );
182 /** List of HTTP header handlers */
183 struct http_header_handler http_header_handlers[] = {
185 .header = "Content-Length",
186 .rx = http_rx_content_length,
194 * @v http HTTP request
195 * @v header HTTP header
197 static void http_rx_header ( struct http_request *http, char *header ) {
198 struct http_header_handler *handler;
203 /* An empty header line marks the transition to the data phase */
205 DBGC ( http, "HTTP %p start of data\n", http );
206 empty_line_buffer ( &http->linebuf );
207 http->rx_state = HTTP_RX_DATA;
211 DBGC ( http, "HTTP %p header \"%s\"\n", http, header );
213 /* Split header at the ": " */
214 separator = strstr ( header, ": " );
218 value = ( separator + 2 );
220 /* Hand off to header handler, if one exists */
221 for ( handler = http_header_handlers ; handler->header ; handler++ ) {
222 if ( strcasecmp ( header, handler->header ) == 0 ) {
223 if ( ( rc = handler->rx ( http, value ) ) != 0 )
231 DBGC ( http, "HTTP %p bad header\n", http );
232 http_done ( http, rc );
237 * Handle new data arriving via HTTP connection in the data phase
239 * @v http HTTP request
241 * @v len Length of new data
243 static void http_rx_data ( struct http_request *http,
244 const char *data, size_t len ) {
247 /* Fill data buffer */
248 if ( ( rc = fill_buffer ( http->buffer, data,
249 http->buffer->fill, len ) ) != 0 ) {
250 DBGC ( http, "HTTP %p failed to fill data buffer: %s\n",
251 http, strerror ( rc ) );
252 http_done ( http, rc );
256 /* If we have reached the content-length, stop now */
257 if ( http->content_length &&
258 ( http->buffer->fill >= http->content_length ) ) {
259 http_done ( http, 0 );
264 * Handle new data arriving via HTTP connection
266 * @v http HTTP request
268 * @v len Length of new data
270 static void http_newdata ( struct tcp_application *app,
271 void *data, size_t len ) {
272 struct http_request *http = tcp_to_http ( app );
273 const char *buf = data;
278 if ( http->rx_state == HTTP_RX_DEAD ) {
279 /* Do no further processing */
281 } else if ( http->rx_state == HTTP_RX_DATA ) {
282 /* Once we're into the data phase, just fill
285 http_rx_data ( http, buf, len );
288 /* In the other phases, buffer and process a
291 if ( ( rc = line_buffer ( &http->linebuf, &buf,
293 DBGC ( http, "HTTP %p could not buffer line: "
294 "%s\n", http, strerror ( rc ) );
295 http_done ( http, rc );
298 if ( ( line = buffered_line ( &http->linebuf ) ) ) {
299 switch ( http->rx_state ) {
300 case HTTP_RX_RESPONSE:
301 http_rx_response ( http, line );
304 http_rx_header ( http, line );
318 * @v app TCP application
319 * @v buf Temporary data buffer
320 * @v len Length of temporary data buffer
322 static void http_senddata ( struct tcp_application *app,
323 void *buf, size_t len ) {
324 struct http_request *http = tcp_to_http ( app );
325 const char *path = http->uri->path;
326 const char *host = http->uri->host;
331 len = snprintf ( buf, len,
332 "GET %s HTTP/1.1\r\n"
333 "User-Agent: gPXE/" VERSION "\r\n"
335 "\r\n", path, host );
337 tcp_send ( app, ( buf + http->tx_offset ), ( len - http->tx_offset ) );
341 * HTTP data acknowledged
343 * @v app TCP application
344 * @v len Length of acknowledged data
346 static void http_acked ( struct tcp_application *app, size_t len ) {
347 struct http_request *http = tcp_to_http ( app );
349 http->tx_offset += len;
353 * HTTP connection closed by network stack
355 * @v app TCP application
357 static void http_closed ( struct tcp_application *app, int rc ) {
358 struct http_request *http = tcp_to_http ( app );
360 DBGC ( http, "HTTP %p connection closed: %s\n",
361 http, strerror ( rc ) );
363 http_done ( http, rc );
366 /** HTTP TCP operations */
367 static struct tcp_operations http_tcp_operations = {
368 .closed = http_closed,
370 .newdata = http_newdata,
371 .senddata = http_senddata,
375 * Initiate a HTTP connection
377 * @v uri Uniform Resource Identifier
378 * @v buffer Buffer into which to download file
379 * @v parent Parent asynchronous operation
380 * @ret rc Return status code
382 int http_get ( struct uri *uri, struct buffer *buffer, struct async *parent ) {
383 struct http_request *http = NULL;
386 /* Allocate and populate HTTP structure */
387 http = malloc ( sizeof ( *http ) );
390 memset ( http, 0, sizeof ( *http ) );
392 http->buffer = buffer;
393 async_init ( &http->async, &http_async_operations, parent );
395 /* Start name resolution. The download proper will start when
396 * name resolution completes.
398 if ( ( rc = resolv ( uri->host, &http->server, &http->async ) ) != 0 )
404 DBGC ( http, "HTTP %p could not create request: %s\n",
405 http, strerror ( rc ) );
406 async_uninit ( &http->async );
412 * Handle name resolution completion
414 * @v async HTTP asynchronous operation
417 static void http_sigchld ( struct async *async, enum signal signal __unused ) {
418 struct http_request *http =
419 container_of ( async, struct http_request, async );
420 struct sockaddr_tcpip *st = ( struct sockaddr_tcpip * ) &http->server;
423 /* If name resolution failed, abort now */
424 async_wait ( async, &rc, 1 );
426 http_done ( http, rc );
430 /* Otherwise, start the HTTP connection */
431 http->tcp.tcp_op = &http_tcp_operations;
432 st->st_port = htons ( uri_port ( http->uri, HTTP_PORT ) );
433 if ( ( rc = tcp_connect ( &http->tcp, st, 0 ) ) != 0 ) {
434 DBGC ( http, "HTTP %p could not open TCP connection: %s\n",
435 http, strerror ( rc ) );
436 http_done ( http, rc );
442 * Free HTTP connection
444 * @v async Asynchronous operation
446 static void http_reap ( struct async *async ) {
447 free ( container_of ( async, struct http_request, async ) );
450 /** HTTP asynchronous operations */
451 static struct async_operations http_async_operations = {
454 [SIGCHLD] = http_sigchld,
458 /** HTTP download protocol */
459 struct download_protocol http_download_protocol __download_protocol = {
461 .start_download = http_get,