Presize the download buffer when we see the Content-Length header;
[people/sha0/gpxe.git] / src / net / tcp / http.c
1 /*
2  * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
3  *
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.
8  *
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.
13  *
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.
17  */
18
19 /**
20  * @file
21  *
22  * Hyper Text Transfer Protocol (HTTP)
23  *
24  */
25
26 #include <stddef.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <strings.h>
30 #include <byteswap.h>
31 #include <errno.h>
32 #include <assert.h>
33 #include <vsprintf.h>
34 #include <gpxe/async.h>
35 #include <gpxe/uri.h>
36 #include <gpxe/buffer.h>
37 #include <gpxe/download.h>
38 #include <gpxe/http.h>
39
40 static struct async_operations http_async_operations;
41
42 static inline struct http_request *
43 tcp_to_http ( struct tcp_application *app ) {
44         return container_of ( app, struct http_request, tcp );
45 }
46
47 /**
48  * Mark HTTP request as complete
49  *
50  * @v http              HTTP request
51  * @v rc                Return status code
52  *
53  */
54 static void http_done ( struct http_request *http, int rc ) {
55
56         /* Close TCP connection */
57         tcp_close ( &http->tcp );
58
59         /* Prevent further processing of any current packet */
60         http->rx_state = HTTP_RX_DEAD;
61
62         /* Free up any dynamically allocated storage */
63         empty_line_buffer ( &http->linebuf );
64
65         /* If we had a Content-Length, and the received content length
66          * isn't correct, flag an error
67          */
68         if ( http->content_length &&
69              ( http->content_length != http->buffer->fill ) ) {
70                 DBGC ( http, "HTTP %p incorrect length %zd, should be %zd\n",
71                        http, http->buffer->fill, http->content_length );
72                 rc = -EIO;
73         }
74
75         /* Mark async operation as complete */
76         async_done ( &http->async, rc );
77 }
78
79 /**
80  * Convert HTTP response code to return status code
81  *
82  * @v response          HTTP response code
83  * @ret rc              Return status code
84  */
85 static int http_response_to_rc ( unsigned int response ) {
86         switch ( response ) {
87         case 200:
88                 return 0;
89         case 404:
90                 return -ENOENT;
91         case 403:
92                 return -EPERM;
93         default:
94                 return -EIO;
95         }
96 }
97
98 /**
99  * Handle HTTP response
100  *
101  * @v http              HTTP request
102  * @v response          HTTP response
103  */
104 static void http_rx_response ( struct http_request *http, char *response ) {
105         char *spc;
106         int rc = -EIO;
107
108         DBGC ( http, "HTTP %p response \"%s\"\n", http, response );
109
110         /* Check response starts with "HTTP/" */
111         if ( strncmp ( response, "HTTP/", 5 ) != 0 )
112                 goto err;
113
114         /* Locate and check response code */
115         spc = strchr ( response, ' ' );
116         if ( ! spc )
117                 goto err;
118         http->response = strtoul ( spc, NULL, 10 );
119         if ( ( rc = http_response_to_rc ( http->response ) ) != 0 )
120                 goto err;
121
122         /* Move to received headers */
123         http->rx_state = HTTP_RX_HEADER;
124         return;
125
126  err:
127         DBGC ( http, "HTTP %p bad response\n", http );
128         http_done ( http, rc );
129         return;
130 }
131
132 /**
133  * Handle HTTP Content-Length header
134  *
135  * @v http              HTTP request
136  * @v value             HTTP header value
137  * @ret rc              Return status code
138  */
139 static int http_rx_content_length ( struct http_request *http,
140                                     const char *value ) {
141         char *endp;
142         int rc;
143
144         http->content_length = strtoul ( value, &endp, 10 );
145         if ( *endp != '\0' ) {
146                 DBGC ( http, "HTTP %p invalid Content-Length \"%s\"\n",
147                        http, value );
148                 return -EIO;
149         }
150
151         /* Try to presize the receive buffer */
152         if ( ( rc = expand_buffer ( http->buffer,
153                                     http->content_length ) ) != 0 ) {
154                 /* May as well abandon the download now; it will fail */
155                 DBGC ( http, "HTTP %p could not presize buffer: %s\n",
156                        http, strerror ( rc ) );
157                 return rc;
158         }
159
160         return 0;
161 }
162
163 /**
164  * An HTTP header handler
165  *
166  */
167 struct http_header_handler {
168         /** Name (e.g. "Content-Length") */
169         const char *header;
170         /** Handle received header
171          *
172          * @v http      HTTP request
173          * @v value     HTTP header value
174          * @ret rc      Return status code
175          *
176          * If an error is returned, the download will be aborted.
177          */
178         int ( * rx ) ( struct http_request *http, const char *value );
179 };
180
181 /** List of HTTP header handlers */
182 struct http_header_handler http_header_handlers[] = {
183         {
184                 .header = "Content-Length",
185                 .rx = http_rx_content_length,
186         },
187         { NULL, NULL }
188 };
189
190 /**
191  * Handle HTTP header
192  *
193  * @v http              HTTP request
194  * @v header            HTTP header
195  */
196 static void http_rx_header ( struct http_request *http, char *header ) {
197         struct http_header_handler *handler;
198         char *separator;
199         char *value;
200         int rc = -EIO;
201
202         /* An empty header line marks the transition to the data phase */
203         if ( ! header[0] ) {
204                 DBGC ( http, "HTTP %p start of data\n", http );
205                 empty_line_buffer ( &http->linebuf );
206                 http->rx_state = HTTP_RX_DATA;
207                 return;
208         }
209
210         DBGC ( http, "HTTP %p header \"%s\"\n", http, header );
211
212         /* Split header at the ": " */
213         separator = strstr ( header, ": " );
214         if ( ! separator )
215                 goto err;
216         *separator = '\0';
217         value = ( separator + 2 );
218
219         /* Hand off to header handler, if one exists */
220         for ( handler = http_header_handlers ; handler->header ; handler++ ) {
221                 if ( strcasecmp ( header, handler->header ) == 0 ) {
222                         if ( ( rc = handler->rx ( http, value ) ) != 0 )
223                                 goto err;
224                         break;
225                 }
226         }
227         return;
228
229  err:
230         DBGC ( http, "HTTP %p bad header\n", http );
231         http_done ( http, rc );
232         return;
233 }
234
235 /**
236  * Handle new data arriving via HTTP connection in the data phase
237  *
238  * @v http              HTTP request
239  * @v data              New data
240  * @v len               Length of new data
241  */
242 static void http_rx_data ( struct http_request *http,
243                            const char *data, size_t len ) {
244         int rc;
245
246         /* Fill data buffer */
247         if ( ( rc = fill_buffer ( http->buffer, data,
248                                   http->buffer->fill, len ) ) != 0 ) {
249                 DBGC ( http, "HTTP %p failed to fill data buffer: %s\n",
250                        http, strerror ( rc ) );
251                 http_done ( http, rc );
252                 return;
253         }
254
255         /* If we have reached the content-length, stop now */
256         if ( http->content_length &&
257              ( http->buffer->fill >= http->content_length ) ) {
258                 http_done ( http, 0 );
259         }
260 }
261
262 /**
263  * Handle new data arriving via HTTP connection
264  *
265  * @v http              HTTP request
266  * @v data              New data
267  * @v len               Length of new data
268  */
269 static void http_newdata ( struct tcp_application *app,
270                            void *data, size_t len ) {
271         struct http_request *http = tcp_to_http ( app );
272         const char *buf = data;
273         char *line;
274         int rc;
275
276         while ( len ) {
277                 if ( http->rx_state == HTTP_RX_DEAD ) {
278                         /* Do no further processing */
279                         return;
280                 } else if ( http->rx_state == HTTP_RX_DATA ) {
281                         /* Once we're into the data phase, just fill
282                          * the data buffer
283                          */
284                         http_rx_data ( http, buf, len );
285                         return;
286                 } else {
287                         /* In the other phases, buffer and process a
288                          * line at a time
289                          */
290                         if ( ( rc = line_buffer ( &http->linebuf, &buf,
291                                                   &len ) ) != 0 ) {
292                                 DBGC ( http, "HTTP %p could not buffer line: "
293                                        "%s\n", http, strerror ( rc ) );
294                                 http_done ( http, rc );
295                                 return;
296                         }
297                         if ( ( line = buffered_line ( &http->linebuf ) ) ) {
298                                 switch ( http->rx_state ) {
299                                 case HTTP_RX_RESPONSE:
300                                         http_rx_response ( http, line );
301                                         break;
302                                 case HTTP_RX_HEADER:
303                                         http_rx_header ( http, line );
304                                         break;
305                                 default:
306                                         assert ( 0 );
307                                         break;
308                                 }
309                         }
310                 }
311         }
312 }
313
314 /**
315  * Send HTTP data
316  *
317  * @v app               TCP application
318  * @v buf               Temporary data buffer
319  * @v len               Length of temporary data buffer
320  */
321 static void http_senddata ( struct tcp_application *app,
322                             void *buf, size_t len ) {
323         struct http_request *http = tcp_to_http ( app );
324         const char *path = http->uri->path;
325         const char *host = http->uri->host;
326
327         if ( ! path )
328                 path = "/";
329
330         len = snprintf ( buf, len,
331                          "GET %s HTTP/1.1\r\n"
332                          "User-Agent: gPXE/" VERSION "\r\n"
333                          "Host: %s\r\n"
334                          "\r\n", path, host );
335
336         tcp_send ( app, ( buf + http->tx_offset ), ( len - http->tx_offset ) );
337 }
338
339 /**
340  * HTTP data acknowledged
341  *
342  * @v app               TCP application
343  * @v len               Length of acknowledged data
344  */
345 static void http_acked ( struct tcp_application *app, size_t len ) {
346         struct http_request *http = tcp_to_http ( app );
347
348         http->tx_offset += len;
349 }
350
351 /**
352  * HTTP connection closed by network stack
353  *
354  * @v app               TCP application
355  */
356 static void http_closed ( struct tcp_application *app, int rc ) {
357         struct http_request *http = tcp_to_http ( app );
358
359         DBGC ( http, "HTTP %p connection closed: %s\n",
360                http, strerror ( rc ) );
361         
362         http_done ( http, rc );
363 }
364
365 /** HTTP TCP operations */
366 static struct tcp_operations http_tcp_operations = {
367         .closed         = http_closed,
368         .acked          = http_acked,
369         .newdata        = http_newdata,
370         .senddata       = http_senddata,
371 };
372
373 /**
374  * Initiate a HTTP connection
375  *
376  * @v uri               Uniform Resource Identifier
377  * @v buffer            Buffer into which to download file
378  * @v parent            Parent asynchronous operation
379  * @ret rc              Return status code
380  */
381 int http_get ( struct uri *uri, struct buffer *buffer, struct async *parent ) {
382         struct http_request *http = NULL;
383         int rc;
384
385         /* Allocate and populate HTTP structure */
386         http = malloc ( sizeof ( *http ) );
387         if ( ! http )
388                 return -ENOMEM;
389         memset ( http, 0, sizeof ( *http ) );
390         http->uri = uri;
391         http->buffer = buffer;
392         async_init ( &http->async, &http_async_operations, parent );
393
394
395 #warning "Quick name resolution hack"
396         extern int dns_resolv ( const char *name,
397                                 struct sockaddr *sa,
398                                 struct async *parent );
399                 
400         if ( ( rc = dns_resolv ( uri->host, &http->server,
401                                  &http->async ) ) != 0 )
402                 goto err;
403
404
405         return 0;
406
407  err:
408         DBGC ( http, "HTTP %p could not create request: %s\n", 
409                http, strerror ( rc ) );
410         async_uninit ( &http->async );
411         free ( http );
412         return rc;
413 }
414
415 /**
416  * Handle name resolution completion
417  *
418  * @v async             HTTP asynchronous operation
419  * @v signal            SIGCHLD
420  */
421 static void http_sigchld ( struct async *async, enum signal signal __unused ) {
422         struct http_request *http =
423                 container_of ( async, struct http_request, async );
424         struct sockaddr_tcpip *st = ( struct sockaddr_tcpip * ) &http->server;
425         int rc;
426
427         /* If name resolution failed, abort now */
428         async_wait ( async, &rc, 1 );
429         if ( rc != 0 ) {
430                 http_done ( http, rc );
431                 return;
432         }
433
434         /* Otherwise, start the HTTP connection */
435         http->tcp.tcp_op = &http_tcp_operations;
436         st->st_port = htons ( uri_port ( http->uri, HTTP_PORT ) );
437         if ( ( rc = tcp_connect ( &http->tcp, st, 0 ) ) != 0 ) {
438                 DBGC ( http, "HTTP %p could not open TCP connection: %s\n",
439                        http, strerror ( rc ) );
440                 http_done ( http, rc );
441                 return;
442         }
443 }
444
445 /**
446  * Free HTTP connection
447  *
448  * @v async             Asynchronous operation
449  */
450 static void http_reap ( struct async *async ) {
451         free ( container_of ( async, struct http_request, async ) );
452 }
453
454 /** HTTP asynchronous operations */
455 static struct async_operations http_async_operations = {
456         .reap = http_reap,
457         .signal = {
458                 [SIGCHLD] = http_sigchld,
459         },
460 };
461
462 /** HTTP download protocol */
463 struct download_protocol http_download_protocol __download_protocol = {
464         .name = "http",
465         .start_download = http_get,
466 };