Reorder functions to more closely reflect the flow of control
[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
143         http->content_length = strtoul ( value, &endp, 10 );
144         if ( *endp != '\0' ) {
145                 DBGC ( http, "HTTP %p invalid Content-Length \"%s\"\n",
146                        http, value );
147                 return -EIO;
148         }
149
150         return 0;
151 }
152
153 /**
154  * An HTTP header handler
155  *
156  */
157 struct http_header_handler {
158         /** Name (e.g. "Content-Length") */
159         const char *header;
160         /** Handle received header
161          *
162          * @v http      HTTP request
163          * @v value     HTTP header value
164          * @ret rc      Return status code
165          */
166         int ( * rx ) ( struct http_request *http, const char *value );
167 };
168
169 /** List of HTTP header handlers */
170 struct http_header_handler http_header_handlers[] = {
171         {
172                 .header = "Content-Length",
173                 .rx = http_rx_content_length,
174         },
175         { NULL, NULL }
176 };
177
178 /**
179  * Handle HTTP header
180  *
181  * @v http              HTTP request
182  * @v header            HTTP header
183  */
184 static void http_rx_header ( struct http_request *http, char *header ) {
185         struct http_header_handler *handler;
186         char *separator;
187         char *value;
188         int rc = -EIO;
189
190         /* An empty header line marks the transition to the data phase */
191         if ( ! header[0] ) {
192                 DBGC ( http, "HTTP %p start of data\n", http );
193                 empty_line_buffer ( &http->linebuf );
194                 http->rx_state = HTTP_RX_DATA;
195                 return;
196         }
197
198         DBGC ( http, "HTTP %p header \"%s\"\n", http, header );
199
200         /* Split header at the ": " */
201         separator = strstr ( header, ": " );
202         if ( ! separator )
203                 goto err;
204         *separator = '\0';
205         value = ( separator + 2 );
206
207         /* Hand off to header handler, if one exists */
208         for ( handler = http_header_handlers ; handler->header ; handler++ ) {
209                 if ( strcasecmp ( header, handler->header ) == 0 ) {
210                         if ( ( rc = handler->rx ( http, value ) ) != 0 )
211                                 goto err;
212                         break;
213                 }
214         }
215         return;
216
217  err:
218         DBGC ( http, "HTTP %p bad header\n", http );
219         http_done ( http, rc );
220         return;
221 }
222
223 /**
224  * Handle new data arriving via HTTP connection in the data phase
225  *
226  * @v http              HTTP request
227  * @v data              New data
228  * @v len               Length of new data
229  */
230 static void http_rx_data ( struct http_request *http,
231                            const char *data, size_t len ) {
232         int rc;
233
234         /* Fill data buffer */
235         if ( ( rc = fill_buffer ( http->buffer, data,
236                                   http->buffer->fill, len ) ) != 0 ) {
237                 DBGC ( http, "HTTP %p failed to fill data buffer: %s\n",
238                        http, strerror ( rc ) );
239                 http_done ( http, rc );
240                 return;
241         }
242
243         /* If we have reached the content-length, stop now */
244         if ( http->content_length &&
245              ( http->buffer->fill >= http->content_length ) ) {
246                 http_done ( http, 0 );
247         }
248 }
249
250 /**
251  * Handle new data arriving via HTTP connection
252  *
253  * @v http              HTTP request
254  * @v data              New data
255  * @v len               Length of new data
256  */
257 static void http_newdata ( struct tcp_application *app,
258                            void *data, size_t len ) {
259         struct http_request *http = tcp_to_http ( app );
260         const char *buf = data;
261         char *line;
262         int rc;
263
264         while ( len ) {
265                 if ( http->rx_state == HTTP_RX_DEAD ) {
266                         /* Do no further processing */
267                         return;
268                 } else if ( http->rx_state == HTTP_RX_DATA ) {
269                         /* Once we're into the data phase, just fill
270                          * the data buffer
271                          */
272                         http_rx_data ( http, buf, len );
273                         return;
274                 } else {
275                         /* In the other phases, buffer and process a
276                          * line at a time
277                          */
278                         if ( ( rc = line_buffer ( &http->linebuf, &buf,
279                                                   &len ) ) != 0 ) {
280                                 DBGC ( http, "HTTP %p could not buffer line: "
281                                        "%s\n", http, strerror ( rc ) );
282                                 http_done ( http, rc );
283                                 return;
284                         }
285                         if ( ( line = buffered_line ( &http->linebuf ) ) ) {
286                                 switch ( http->rx_state ) {
287                                 case HTTP_RX_RESPONSE:
288                                         http_rx_response ( http, line );
289                                         break;
290                                 case HTTP_RX_HEADER:
291                                         http_rx_header ( http, line );
292                                         break;
293                                 default:
294                                         assert ( 0 );
295                                         break;
296                                 }
297                         }
298                 }
299         }
300 }
301
302 /**
303  * Send HTTP data
304  *
305  * @v app               TCP application
306  * @v buf               Temporary data buffer
307  * @v len               Length of temporary data buffer
308  */
309 static void http_senddata ( struct tcp_application *app,
310                             void *buf, size_t len ) {
311         struct http_request *http = tcp_to_http ( app );
312         const char *path = http->uri->path;
313         const char *host = http->uri->host;
314
315         if ( ! path )
316                 path = "/";
317
318         len = snprintf ( buf, len,
319                          "GET %s HTTP/1.1\r\n"
320                          "User-Agent: gPXE/" VERSION "\r\n"
321                          "Host: %s\r\n"
322                          "\r\n", path, host );
323
324         tcp_send ( app, ( buf + http->tx_offset ), ( len - http->tx_offset ) );
325 }
326
327 /**
328  * HTTP data acknowledged
329  *
330  * @v app               TCP application
331  * @v len               Length of acknowledged data
332  */
333 static void http_acked ( struct tcp_application *app, size_t len ) {
334         struct http_request *http = tcp_to_http ( app );
335
336         http->tx_offset += len;
337 }
338
339 /**
340  * HTTP connection closed by network stack
341  *
342  * @v app               TCP application
343  */
344 static void http_closed ( struct tcp_application *app, int rc ) {
345         struct http_request *http = tcp_to_http ( app );
346
347         DBGC ( http, "HTTP %p connection closed: %s\n",
348                http, strerror ( rc ) );
349         
350         http_done ( http, rc );
351 }
352
353 /** HTTP TCP operations */
354 static struct tcp_operations http_tcp_operations = {
355         .closed         = http_closed,
356         .acked          = http_acked,
357         .newdata        = http_newdata,
358         .senddata       = http_senddata,
359 };
360
361 /**
362  * Initiate a HTTP connection
363  *
364  * @v uri               Uniform Resource Identifier
365  * @v buffer            Buffer into which to download file
366  * @v parent            Parent asynchronous operation
367  * @ret rc              Return status code
368  */
369 int http_get ( struct uri *uri, struct buffer *buffer, struct async *parent ) {
370         struct http_request *http = NULL;
371         int rc;
372
373         /* Allocate and populate HTTP structure */
374         http = malloc ( sizeof ( *http ) );
375         if ( ! http )
376                 return -ENOMEM;
377         memset ( http, 0, sizeof ( *http ) );
378         http->uri = uri;
379         http->buffer = buffer;
380         async_init ( &http->async, &http_async_operations, parent );
381
382
383 #warning "Quick name resolution hack"
384         extern int dns_resolv ( const char *name,
385                                 struct sockaddr *sa,
386                                 struct async *parent );
387                 
388         if ( ( rc = dns_resolv ( uri->host, &http->server,
389                                  &http->async ) ) != 0 )
390                 goto err;
391
392
393         return 0;
394
395  err:
396         DBGC ( http, "HTTP %p could not create request: %s\n", 
397                http, strerror ( rc ) );
398         async_uninit ( &http->async );
399         free ( http );
400         return rc;
401 }
402
403 /**
404  * Handle name resolution completion
405  *
406  * @v async             HTTP asynchronous operation
407  * @v signal            SIGCHLD
408  */
409 static void http_sigchld ( struct async *async, enum signal signal __unused ) {
410         struct http_request *http =
411                 container_of ( async, struct http_request, async );
412         struct sockaddr_tcpip *st = ( struct sockaddr_tcpip * ) &http->server;
413         int rc;
414
415         /* If name resolution failed, abort now */
416         async_wait ( async, &rc, 1 );
417         if ( rc != 0 ) {
418                 http_done ( http, rc );
419                 return;
420         }
421
422         /* Otherwise, start the HTTP connection */
423         http->tcp.tcp_op = &http_tcp_operations;
424         st->st_port = htons ( uri_port ( http->uri, HTTP_PORT ) );
425         if ( ( rc = tcp_connect ( &http->tcp, st, 0 ) ) != 0 ) {
426                 DBGC ( http, "HTTP %p could not open TCP connection: %s\n",
427                        http, strerror ( rc ) );
428                 http_done ( http, rc );
429                 return;
430         }
431 }
432
433 /**
434  * Free HTTP connection
435  *
436  * @v async             Asynchronous operation
437  */
438 static void http_reap ( struct async *async ) {
439         free ( container_of ( async, struct http_request, async ) );
440 }
441
442 /** HTTP asynchronous operations */
443 static struct async_operations http_async_operations = {
444         .reap = http_reap,
445         .signal = {
446                 [SIGCHLD] = http_sigchld,
447         },
448 };
449
450 /** HTTP download protocol */
451 struct download_protocol http_download_protocol __download_protocol = {
452         .name = "http",
453         .start_download = http_get,
454 };