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