Keep HTTP progress updated, and display a progress indicator during the
[people/xl0/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 <stdio.h>
29 #include <string.h>
30 #include <strings.h>
31 #include <byteswap.h>
32 #include <errno.h>
33 #include <assert.h>
34 #include <gpxe/async.h>
35 #include <gpxe/uri.h>
36 #include <gpxe/buffer.h>
37 #include <gpxe/download.h>
38 #include <gpxe/resolv.h>
39 #include <gpxe/http.h>
40
41 static struct async_operations http_async_operations;
42
43 static inline struct http_request *
44 tcp_to_http ( struct tcp_application *app ) {
45         return container_of ( app, struct http_request, tcp );
46 }
47
48 /**
49  * Mark HTTP request as complete
50  *
51  * @v http              HTTP request
52  * @v rc                Return status code
53  *
54  */
55 static void http_done ( struct http_request *http, int rc ) {
56
57         /* Close TCP connection */
58         tcp_close ( &http->tcp );
59
60         /* Prevent further processing of any current packet */
61         http->rx_state = HTTP_RX_DEAD;
62
63         /* Free up any dynamically allocated storage */
64         empty_line_buffer ( &http->linebuf );
65
66         /* If we had a Content-Length, and the received content length
67          * isn't correct, flag an error
68          */
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 );
73                 rc = -EIO;
74         }
75
76         /* Mark async operation as complete */
77         async_done ( &http->async, rc );
78 }
79
80 /**
81  * Convert HTTP response code to return status code
82  *
83  * @v response          HTTP response code
84  * @ret rc              Return status code
85  */
86 static int http_response_to_rc ( unsigned int response ) {
87         switch ( response ) {
88         case 200:
89                 return 0;
90         case 404:
91                 return -ENOENT;
92         case 403:
93                 return -EPERM;
94         default:
95                 return -EIO;
96         }
97 }
98
99 /**
100  * Handle HTTP response
101  *
102  * @v http              HTTP request
103  * @v response          HTTP response
104  */
105 static void http_rx_response ( struct http_request *http, char *response ) {
106         char *spc;
107         int rc = -EIO;
108
109         DBGC ( http, "HTTP %p response \"%s\"\n", http, response );
110
111         /* Check response starts with "HTTP/" */
112         if ( strncmp ( response, "HTTP/", 5 ) != 0 )
113                 goto err;
114
115         /* Locate and check response code */
116         spc = strchr ( response, ' ' );
117         if ( ! spc )
118                 goto err;
119         http->response = strtoul ( spc, NULL, 10 );
120         if ( ( rc = http_response_to_rc ( http->response ) ) != 0 )
121                 goto err;
122
123         /* Move to received headers */
124         http->rx_state = HTTP_RX_HEADER;
125         return;
126
127  err:
128         DBGC ( http, "HTTP %p bad response\n", http );
129         http_done ( http, rc );
130         return;
131 }
132
133 /**
134  * Handle HTTP Content-Length header
135  *
136  * @v http              HTTP request
137  * @v value             HTTP header value
138  * @ret rc              Return status code
139  */
140 static int http_rx_content_length ( struct http_request *http,
141                                     const char *value ) {
142         char *endp;
143         int rc;
144
145         http->content_length = strtoul ( value, &endp, 10 );
146         if ( *endp != '\0' ) {
147                 DBGC ( http, "HTTP %p invalid Content-Length \"%s\"\n",
148                        http, value );
149                 return -EIO;
150         }
151
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 ) );
158                 return rc;
159         }
160
161         return 0;
162 }
163
164 /**
165  * An HTTP header handler
166  *
167  */
168 struct http_header_handler {
169         /** Name (e.g. "Content-Length") */
170         const char *header;
171         /** Handle received header
172          *
173          * @v http      HTTP request
174          * @v value     HTTP header value
175          * @ret rc      Return status code
176          *
177          * If an error is returned, the download will be aborted.
178          */
179         int ( * rx ) ( struct http_request *http, const char *value );
180 };
181
182 /** List of HTTP header handlers */
183 struct http_header_handler http_header_handlers[] = {
184         {
185                 .header = "Content-Length",
186                 .rx = http_rx_content_length,
187         },
188         { NULL, NULL }
189 };
190
191 /**
192  * Handle HTTP header
193  *
194  * @v http              HTTP request
195  * @v header            HTTP header
196  */
197 static void http_rx_header ( struct http_request *http, char *header ) {
198         struct http_header_handler *handler;
199         char *separator;
200         char *value;
201         int rc = -EIO;
202
203         /* An empty header line marks the transition to the data phase */
204         if ( ! header[0] ) {
205                 DBGC ( http, "HTTP %p start of data\n", http );
206                 empty_line_buffer ( &http->linebuf );
207                 http->rx_state = HTTP_RX_DATA;
208                 return;
209         }
210
211         DBGC ( http, "HTTP %p header \"%s\"\n", http, header );
212
213         /* Split header at the ": " */
214         separator = strstr ( header, ": " );
215         if ( ! separator )
216                 goto err;
217         *separator = '\0';
218         value = ( separator + 2 );
219
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 )
224                                 goto err;
225                         break;
226                 }
227         }
228         return;
229
230  err:
231         DBGC ( http, "HTTP %p bad header\n", http );
232         http_done ( http, rc );
233         return;
234 }
235
236 /**
237  * Handle new data arriving via HTTP connection in the data phase
238  *
239  * @v http              HTTP request
240  * @v data              New data
241  * @v len               Length of new data
242  */
243 static void http_rx_data ( struct http_request *http,
244                            const char *data, size_t len ) {
245         int rc;
246
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 );
253                 return;
254         }
255
256         /* Update progress */
257         http->async.completed = http->buffer->fill;
258         http->async.total = http->content_length;
259
260         /* If we have reached the content-length, stop now */
261         if ( http->content_length &&
262              ( http->buffer->fill >= http->content_length ) ) {
263                 http_done ( http, 0 );
264         }
265 }
266
267 /**
268  * Handle new data arriving via HTTP connection
269  *
270  * @v http              HTTP request
271  * @v data              New data
272  * @v len               Length of new data
273  */
274 static void http_newdata ( struct tcp_application *app,
275                            void *data, size_t len ) {
276         struct http_request *http = tcp_to_http ( app );
277         const char *buf = data;
278         char *line;
279         int rc;
280
281         while ( len ) {
282                 if ( http->rx_state == HTTP_RX_DEAD ) {
283                         /* Do no further processing */
284                         return;
285                 } else if ( http->rx_state == HTTP_RX_DATA ) {
286                         /* Once we're into the data phase, just fill
287                          * the data buffer
288                          */
289                         http_rx_data ( http, buf, len );
290                         return;
291                 } else {
292                         /* In the other phases, buffer and process a
293                          * line at a time
294                          */
295                         if ( ( rc = line_buffer ( &http->linebuf, &buf,
296                                                   &len ) ) != 0 ) {
297                                 DBGC ( http, "HTTP %p could not buffer line: "
298                                        "%s\n", http, strerror ( rc ) );
299                                 http_done ( http, rc );
300                                 return;
301                         }
302                         if ( ( line = buffered_line ( &http->linebuf ) ) ) {
303                                 switch ( http->rx_state ) {
304                                 case HTTP_RX_RESPONSE:
305                                         http_rx_response ( http, line );
306                                         break;
307                                 case HTTP_RX_HEADER:
308                                         http_rx_header ( http, line );
309                                         break;
310                                 default:
311                                         assert ( 0 );
312                                         break;
313                                 }
314                         }
315                 }
316         }
317 }
318
319 /**
320  * Send HTTP data
321  *
322  * @v app               TCP application
323  * @v buf               Temporary data buffer
324  * @v len               Length of temporary data buffer
325  */
326 static void http_senddata ( struct tcp_application *app,
327                             void *buf, size_t len ) {
328         struct http_request *http = tcp_to_http ( app );
329         const char *path = http->uri->path;
330         const char *host = http->uri->host;
331
332         if ( ! path )
333                 path = "/";
334
335         len = snprintf ( buf, len,
336                          "GET %s HTTP/1.1\r\n"
337                          "User-Agent: gPXE/" VERSION "\r\n"
338                          "Host: %s\r\n"
339                          "\r\n", path, host );
340
341         tcp_send ( app, ( buf + http->tx_offset ), ( len - http->tx_offset ) );
342 }
343
344 /**
345  * HTTP data acknowledged
346  *
347  * @v app               TCP application
348  * @v len               Length of acknowledged data
349  */
350 static void http_acked ( struct tcp_application *app, size_t len ) {
351         struct http_request *http = tcp_to_http ( app );
352
353         http->tx_offset += len;
354 }
355
356 /**
357  * HTTP connection closed by network stack
358  *
359  * @v app               TCP application
360  */
361 static void http_closed ( struct tcp_application *app, int rc ) {
362         struct http_request *http = tcp_to_http ( app );
363
364         DBGC ( http, "HTTP %p connection closed: %s\n",
365                http, strerror ( rc ) );
366         
367         http_done ( http, rc );
368 }
369
370 /** HTTP TCP operations */
371 static struct tcp_operations http_tcp_operations = {
372         .closed         = http_closed,
373         .acked          = http_acked,
374         .newdata        = http_newdata,
375         .senddata       = http_senddata,
376 };
377
378 /**
379  * Initiate a HTTP connection
380  *
381  * @v uri               Uniform Resource Identifier
382  * @v buffer            Buffer into which to download file
383  * @v parent            Parent asynchronous operation
384  * @ret rc              Return status code
385  */
386 int http_get ( struct uri *uri, struct buffer *buffer, struct async *parent ) {
387         struct http_request *http = NULL;
388         int rc;
389
390         /* Allocate and populate HTTP structure */
391         http = malloc ( sizeof ( *http ) );
392         if ( ! http )
393                 return -ENOMEM;
394         memset ( http, 0, sizeof ( *http ) );
395         http->uri = uri;
396         http->buffer = buffer;
397         async_init ( &http->async, &http_async_operations, parent );
398
399         /* Start name resolution.  The download proper will start when
400          * name resolution completes.
401          */
402         if ( ( rc = resolv ( uri->host, &http->server, &http->async ) ) != 0 )
403                 goto err;
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 };