#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <byteswap.h>
#include <errno.h>
#include <assert.h>
#include <gpxe/uri.h>
#include <gpxe/refcnt.h>
#include <gpxe/iobuf.h>
#include <gpxe/xfer.h>
#include <gpxe/open.h>
#include <gpxe/socket.h>
#include <gpxe/tcpip.h>
#include <gpxe/process.h>
#include <gpxe/linebuf.h>
#include <gpxe/features.h>
#include <gpxe/base64.h>
#include <gpxe/http.h>
Go to the source code of this file.
Data Structures | |
| struct | http_request |
| An HTTP request. More... | |
| struct | http_header_handler |
| An HTTP header handler. More... | |
| struct | http_line_handler |
| An HTTP line-based data handler. More... | |
Enumerations | |
| enum | http_rx_state { HTTP_RX_RESPONSE = 0, HTTP_RX_HEADER, HTTP_RX_DATA, HTTP_RX_DEAD } |
| HTTP receive state. More... | |
Functions | |
| FILE_LICENCE (GPL2_OR_LATER) | |
| FEATURE (FEATURE_PROTOCOL,"HTTP", DHCP_EB_FEATURE_HTTP, 1) | |
| static void | http_free (struct refcnt *refcnt) |
| Free HTTP request. | |
| static void | http_done (struct http_request *http, int rc) |
| Mark HTTP request as complete. | |
| static int | http_response_to_rc (unsigned int response) |
| Convert HTTP response code to return status code. | |
| static int | http_rx_response (struct http_request *http, char *response) |
| Handle HTTP response. | |
| static int | http_rx_location (struct http_request *http, const char *value) |
| Handle HTTP Location header. | |
| static int | http_rx_content_length (struct http_request *http, const char *value) |
| Handle HTTP Content-Length header. | |
| static int | http_rx_header (struct http_request *http, char *header) |
| Handle HTTP header. | |
| static int | http_rx_data (struct http_request *http, struct io_buffer *iobuf) |
| Handle new data arriving via HTTP connection in the data phase. | |
| static int | http_socket_deliver_iob (struct xfer_interface *socket, struct io_buffer *iobuf, struct xfer_metadata *meta __unused) |
| Handle new data arriving via HTTP connection. | |
| static void | http_step (struct process *process) |
| HTTP process. | |
| static void | http_socket_close (struct xfer_interface *socket, int rc) |
| HTTP connection closed by network stack. | |
| static void | http_xfer_close (struct xfer_interface *xfer, int rc) |
| Close HTTP data transfer interface. | |
| int | http_open_filter (struct xfer_interface *xfer, struct uri *uri, unsigned int default_port, int(*filter)(struct xfer_interface *xfer, struct xfer_interface **next)) |
| Initiate an HTTP connection, with optional filter. | |
| static int | http_open (struct xfer_interface *xfer, struct uri *uri) |
| Initiate an HTTP connection. | |
Variables | |
| static struct http_header_handler | http_header_handlers [] |
| List of HTTP header handlers. | |
| static struct http_line_handler | http_line_handlers [] |
| List of HTTP line-based data handlers. | |
| static struct xfer_interface_operations | http_socket_operations |
| HTTP socket operations. | |
| static struct xfer_interface_operations | http_xfer_operations |
| HTTP data transfer interface operations. | |
| struct uri_opener http_uri_opener | __uri_opener |
| HTTP URI opener. | |
Definition in file http.c.
| enum http_rx_state |
HTTP receive state.
Definition at line 52 of file http.c.
00052 { 00053 HTTP_RX_RESPONSE = 0, 00054 HTTP_RX_HEADER, 00055 HTTP_RX_DATA, 00056 HTTP_RX_DEAD, 00057 };
| FILE_LICENCE | ( | GPL2_OR_LATER | ) |
| FEATURE | ( | FEATURE_PROTOCOL | , | |
| "HTTP" | , | |||
| DHCP_EB_FEATURE_HTTP | , | |||
| 1 | ||||
| ) |
| static void http_free | ( | struct refcnt * | refcnt | ) | [static] |
Free HTTP request.
| refcnt | Reference counter |
Definition at line 94 of file http.c.
References container_of, empty_line_buffer(), free(), http_request::linebuf, and http_request::uri.
Referenced by http_open_filter().
00094 { 00095 struct http_request *http = 00096 container_of ( refcnt, struct http_request, refcnt ); 00097 00098 uri_put ( http->uri ); 00099 empty_line_buffer ( &http->linebuf ); 00100 free ( http ); 00101 };
| static void http_done | ( | struct http_request * | http, | |
| int | rc | |||
| ) | [static] |
Mark HTTP request as complete.
| http | HTTP request | |
| rc | Return status code |
Definition at line 109 of file http.c.
References http_request::content_length, DBGC, EIO, HTTP_RX_DEAD, http_request::process, process_del(), http_request::rx_len, http_request::rx_state, http_request::socket, http_request::xfer, and xfer_close().
Referenced by http_open_filter(), http_rx_data(), http_socket_close(), http_socket_deliver_iob(), http_step(), and http_xfer_close().
00109 { 00110 00111 /* Prevent further processing of any current packet */ 00112 http->rx_state = HTTP_RX_DEAD; 00113 00114 /* If we had a Content-Length, and the received content length 00115 * isn't correct, flag an error 00116 */ 00117 if ( http->content_length && 00118 ( http->content_length != http->rx_len ) ) { 00119 DBGC ( http, "HTTP %p incorrect length %zd, should be %zd\n", 00120 http, http->rx_len, http->content_length ); 00121 rc = -EIO; 00122 } 00123 00124 /* Remove process */ 00125 process_del ( &http->process ); 00126 00127 /* Close all data transfer interfaces */ 00128 xfer_nullify ( &http->socket ); 00129 xfer_close ( &http->socket, rc ); 00130 xfer_nullify ( &http->xfer ); 00131 xfer_close ( &http->xfer, rc ); 00132 }
| static int http_response_to_rc | ( | unsigned int | response | ) | [static] |
Convert HTTP response code to return status code.
| response | HTTP response code |
| rc | Return status code |
Definition at line 140 of file http.c.
References EACCES, EIO, ENOENT, and EPERM.
Referenced by http_rx_response().
00140 { 00141 switch ( response ) { 00142 case 200: 00143 case 301: 00144 case 302: 00145 return 0; 00146 case 404: 00147 return -ENOENT; 00148 case 403: 00149 return -EPERM; 00150 case 401: 00151 return -EACCES; 00152 default: 00153 return -EIO; 00154 } 00155 }
| static int http_rx_response | ( | struct http_request * | http, | |
| char * | response | |||
| ) | [static] |
Handle HTTP response.
| http | HTTP request | |
| response | HTTP response |
| rc | Return status code |
Definition at line 164 of file http.c.
References DBGC, EIO, http_response_to_rc(), HTTP_RX_HEADER, NULL, http_request::response, http_request::rx_state, strchr(), strncmp(), and strtoul().
00164 { 00165 char *spc; 00166 int rc; 00167 00168 DBGC ( http, "HTTP %p response \"%s\"\n", http, response ); 00169 00170 /* Check response starts with "HTTP/" */ 00171 if ( strncmp ( response, "HTTP/", 5 ) != 0 ) 00172 return -EIO; 00173 00174 /* Locate and check response code */ 00175 spc = strchr ( response, ' ' ); 00176 if ( ! spc ) 00177 return -EIO; 00178 http->response = strtoul ( spc, NULL, 10 ); 00179 if ( ( rc = http_response_to_rc ( http->response ) ) != 0 ) 00180 return rc; 00181 00182 /* Move to received headers */ 00183 http->rx_state = HTTP_RX_HEADER; 00184 return 0; 00185 }
| static int http_rx_location | ( | struct http_request * | http, | |
| const char * | value | |||
| ) | [static] |
Handle HTTP Location header.
| http | HTTP request | |
| value | HTTP header value |
| rc | Return status code |
Definition at line 194 of file http.c.
References DBGC, LOCATION_URI_STRING, strerror(), http_request::xfer, and xfer_redirect().
00194 { 00195 int rc; 00196 00197 /* Redirect to new location */ 00198 DBGC ( http, "HTTP %p redirecting to %s\n", http, value ); 00199 if ( ( rc = xfer_redirect ( &http->xfer, LOCATION_URI_STRING, 00200 value ) ) != 0 ) { 00201 DBGC ( http, "HTTP %p could not redirect: %s\n", 00202 http, strerror ( rc ) ); 00203 return rc; 00204 } 00205 00206 return 0; 00207 }
| static int http_rx_content_length | ( | struct http_request * | http, | |
| const char * | value | |||
| ) | [static] |
Handle HTTP Content-Length header.
| http | HTTP request | |
| value | HTTP header value |
| rc | Return status code |
Definition at line 216 of file http.c.
References http_request::content_length, DBGC, EIO, SEEK_SET, strtoul(), http_request::xfer, and xfer_seek().
00217 { 00218 char *endp; 00219 00220 http->content_length = strtoul ( value, &endp, 10 ); 00221 if ( *endp != '\0' ) { 00222 DBGC ( http, "HTTP %p invalid Content-Length \"%s\"\n", 00223 http, value ); 00224 return -EIO; 00225 } 00226 00227 /* Use seek() to notify recipient of filesize */ 00228 xfer_seek ( &http->xfer, http->content_length, SEEK_SET ); 00229 xfer_seek ( &http->xfer, 0, SEEK_SET ); 00230 00231 return 0; 00232 }
| static int http_rx_header | ( | struct http_request * | http, | |
| char * | header | |||
| ) | [static] |
Handle HTTP header.
| http | HTTP request | |
| header | HTTP header |
| rc | Return status code |
Definition at line 269 of file http.c.
References DBGC, EIO, empty_line_buffer(), http_header_handler::header, HTTP_RX_DATA, http_request::linebuf, http_header_handler::rx, http_request::rx_state, strcasecmp(), and strstr().
00269 { 00270 struct http_header_handler *handler; 00271 char *separator; 00272 char *value; 00273 int rc; 00274 00275 /* An empty header line marks the transition to the data phase */ 00276 if ( ! header[0] ) { 00277 DBGC ( http, "HTTP %p start of data\n", http ); 00278 empty_line_buffer ( &http->linebuf ); 00279 http->rx_state = HTTP_RX_DATA; 00280 return 0; 00281 } 00282 00283 DBGC ( http, "HTTP %p header \"%s\"\n", http, header ); 00284 00285 /* Split header at the ": " */ 00286 separator = strstr ( header, ": " ); 00287 if ( ! separator ) { 00288 DBGC ( http, "HTTP %p malformed header\n", http ); 00289 return -EIO; 00290 } 00291 *separator = '\0'; 00292 value = ( separator + 2 ); 00293 00294 /* Hand off to header handler, if one exists */ 00295 for ( handler = http_header_handlers ; handler->header ; handler++ ) { 00296 if ( strcasecmp ( header, handler->header ) == 0 ) { 00297 if ( ( rc = handler->rx ( http, value ) ) != 0 ) 00298 return rc; 00299 break; 00300 } 00301 } 00302 return 0; 00303 }
| static int http_rx_data | ( | struct http_request * | http, | |
| struct io_buffer * | iobuf | |||
| ) | [static] |
Handle new data arriving via HTTP connection in the data phase.
| http | HTTP request | |
| iobuf | I/O buffer |
| rc | Return status code |
Definition at line 329 of file http.c.
References http_request::content_length, http_done(), iob_len(), http_request::rx_len, http_request::xfer, and xfer_deliver_iob().
Referenced by http_socket_deliver_iob().
00330 { 00331 int rc; 00332 00333 /* Update received length */ 00334 http->rx_len += iob_len ( iobuf ); 00335 00336 /* Hand off data buffer */ 00337 if ( ( rc = xfer_deliver_iob ( &http->xfer, iobuf ) ) != 0 ) 00338 return rc; 00339 00340 /* If we have reached the content-length, stop now */ 00341 if ( http->content_length && 00342 ( http->rx_len >= http->content_length ) ) { 00343 http_done ( http, 0 ); 00344 } 00345 00346 return 0; 00347 }
| static int http_socket_deliver_iob | ( | struct xfer_interface * | socket, | |
| struct io_buffer * | iobuf, | |||
| struct xfer_metadata *meta | __unused | |||
| ) | [static] |
Handle new data arriving via HTTP connection.
| socket | Transport layer interface | |
| iobuf | I/O buffer | |
| meta | Data transfer metadata |
| rc | Return status code |
Definition at line 357 of file http.c.
References assert, buffered_line(), container_of, io_buffer::data, DBGC, free_iob(), http_done(), http_rx_data(), HTTP_RX_DATA, HTTP_RX_DEAD, HTTP_RX_HEADER, HTTP_RX_RESPONSE, iob_disown, iob_len(), iob_pull, line_buffer(), http_request::linebuf, http_line_handler::rx, http_request::rx_state, and strerror().
00359 { 00360 struct http_request *http = 00361 container_of ( socket, struct http_request, socket ); 00362 struct http_line_handler *lh; 00363 char *line; 00364 ssize_t len; 00365 int rc = 0; 00366 00367 while ( iob_len ( iobuf ) ) { 00368 switch ( http->rx_state ) { 00369 case HTTP_RX_DEAD: 00370 /* Do no further processing */ 00371 goto done; 00372 case HTTP_RX_DATA: 00373 /* Once we're into the data phase, just fill 00374 * the data buffer 00375 */ 00376 rc = http_rx_data ( http, iob_disown ( iobuf ) ); 00377 goto done; 00378 case HTTP_RX_RESPONSE: 00379 case HTTP_RX_HEADER: 00380 /* In the other phases, buffer and process a 00381 * line at a time 00382 */ 00383 len = line_buffer ( &http->linebuf, iobuf->data, 00384 iob_len ( iobuf ) ); 00385 if ( len < 0 ) { 00386 rc = len; 00387 DBGC ( http, "HTTP %p could not buffer line: " 00388 "%s\n", http, strerror ( rc ) ); 00389 goto done; 00390 } 00391 iob_pull ( iobuf, len ); 00392 line = buffered_line ( &http->linebuf ); 00393 if ( line ) { 00394 lh = &http_line_handlers[http->rx_state]; 00395 if ( ( rc = lh->rx ( http, line ) ) != 0 ) 00396 goto done; 00397 } 00398 break; 00399 default: 00400 assert ( 0 ); 00401 break; 00402 } 00403 } 00404 00405 done: 00406 if ( rc ) 00407 http_done ( http, rc ); 00408 free_iob ( iobuf ); 00409 return rc; 00410 }
| static void http_step | ( | struct process * | process | ) | [static] |
HTTP process.
| process | Process |
Definition at line 417 of file http.c.
References base64_encode(), base64_encoded_len(), container_of, uri::host, http_done(), NULL, uri::password, uri::path, http_request::process, process_del(), snprintf(), http_request::socket, strlen(), unparse_uri(), http_request::uri, URI_PATH_BIT, URI_QUERY_BIT, uri::user, xfer_printf(), and xfer_window().
Referenced by http_open_filter().
00417 { 00418 struct http_request *http = 00419 container_of ( process, struct http_request, process ); 00420 const char *host = http->uri->host; 00421 const char *user = http->uri->user; 00422 const char *password = 00423 ( http->uri->password ? http->uri->password : "" ); 00424 size_t user_pw_len = ( user ? ( strlen ( user ) + 1 /* ":" */ + 00425 strlen ( password ) ) : 0 ); 00426 size_t user_pw_base64_len = base64_encoded_len ( user_pw_len ); 00427 char user_pw[ user_pw_len + 1 /* NUL */ ]; 00428 char user_pw_base64[ user_pw_base64_len + 1 /* NUL */ ]; 00429 int rc; 00430 int request_len = unparse_uri ( NULL, 0, http->uri, 00431 URI_PATH_BIT | URI_QUERY_BIT ); 00432 00433 if ( xfer_window ( &http->socket ) ) { 00434 char request[request_len + 1]; 00435 00436 /* Construct path?query request */ 00437 unparse_uri ( request, sizeof ( request ), http->uri, 00438 URI_PATH_BIT | URI_QUERY_BIT ); 00439 00440 /* We want to execute only once */ 00441 process_del ( &http->process ); 00442 00443 /* Construct authorisation, if applicable */ 00444 if ( user ) { 00445 /* Make "user:password" string from decoded fields */ 00446 snprintf ( user_pw, sizeof ( user_pw ), "%s:%s", 00447 user, password ); 00448 00449 /* Base64-encode the "user:password" string */ 00450 base64_encode ( user_pw, user_pw_base64 ); 00451 } 00452 00453 /* Send GET request */ 00454 if ( ( rc = xfer_printf ( &http->socket, 00455 "GET %s%s HTTP/1.0\r\n" 00456 "User-Agent: gPXE/" VERSION "\r\n" 00457 "%s%s%s" 00458 "Host: %s\r\n" 00459 "\r\n", 00460 http->uri->path ? "" : "/", 00461 request, 00462 ( user ? 00463 "Authorization: Basic " : "" ), 00464 ( user ? user_pw_base64 : "" ), 00465 ( user ? "\r\n" : "" ), 00466 host ) ) != 0 ) { 00467 http_done ( http, rc ); 00468 } 00469 } 00470 }
| static void http_socket_close | ( | struct xfer_interface * | socket, | |
| int | rc | |||
| ) | [static] |
HTTP connection closed by network stack.
| socket | Transport layer interface | |
| rc | Reason for close |
Definition at line 478 of file http.c.
References container_of, DBGC, http_done(), and strerror().
00478 { 00479 struct http_request *http = 00480 container_of ( socket, struct http_request, socket ); 00481 00482 DBGC ( http, "HTTP %p socket closed: %s\n", 00483 http, strerror ( rc ) ); 00484 00485 http_done ( http, rc ); 00486 }
| static void http_xfer_close | ( | struct xfer_interface * | xfer, | |
| int | rc | |||
| ) | [static] |
Close HTTP data transfer interface.
| xfer | Data transfer interface | |
| rc | Reason for close |
Definition at line 504 of file http.c.
References container_of, DBGC, http_done(), and strerror().
00504 { 00505 struct http_request *http = 00506 container_of ( xfer, struct http_request, xfer ); 00507 00508 DBGC ( http, "HTTP %p interface closed: %s\n", 00509 http, strerror ( rc ) ); 00510 00511 http_done ( http, rc ); 00512 }
| int http_open_filter | ( | struct xfer_interface * | xfer, | |
| struct uri * | uri, | |||
| unsigned int | default_port, | |||
| int(*)(struct xfer_interface *xfer, struct xfer_interface **next) | filter | |||
| ) |
Initiate an HTTP connection, with optional filter.
| xfer | Data transfer interface | |
| uri | Uniform Resource Identifier | |
| default_port | Default port number | |
| filter | Filter to apply to socket, or NULL |
| rc | Return status code |
Definition at line 533 of file http.c.
References DBGC, EINVAL, ENOMEM, filter(), refcnt::free, uri::host, htons, http_done(), http_free(), http_step(), memset(), NULL, http_request::process, process_init(), ref_put(), http_request::refcnt, SOCK_STREAM, http_request::socket, sockaddr_tcpip::st_port, strerror(), http_request::uri, uri_port(), http_request::xfer, xfer_init(), xfer_open_named_socket(), and zalloc().
Referenced by http_open(), and https_open().
00536 { 00537 struct http_request *http; 00538 struct sockaddr_tcpip server; 00539 struct xfer_interface *socket; 00540 int rc; 00541 00542 /* Sanity checks */ 00543 if ( ! uri->host ) 00544 return -EINVAL; 00545 00546 /* Allocate and populate HTTP structure */ 00547 http = zalloc ( sizeof ( *http ) ); 00548 if ( ! http ) 00549 return -ENOMEM; 00550 http->refcnt.free = http_free; 00551 xfer_init ( &http->xfer, &http_xfer_operations, &http->refcnt ); 00552 http->uri = uri_get ( uri ); 00553 xfer_init ( &http->socket, &http_socket_operations, &http->refcnt ); 00554 process_init ( &http->process, http_step, &http->refcnt ); 00555 00556 /* Open socket */ 00557 memset ( &server, 0, sizeof ( server ) ); 00558 server.st_port = htons ( uri_port ( http->uri, default_port ) ); 00559 socket = &http->socket; 00560 if ( filter ) { 00561 if ( ( rc = filter ( socket, &socket ) ) != 0 ) 00562 goto err; 00563 } 00564 if ( ( rc = xfer_open_named_socket ( socket, SOCK_STREAM, 00565 ( struct sockaddr * ) &server, 00566 uri->host, NULL ) ) != 0 ) 00567 goto err; 00568 00569 /* Attach to parent interface, mortalise self, and return */ 00570 xfer_plug_plug ( &http->xfer, xfer ); 00571 ref_put ( &http->refcnt ); 00572 return 0; 00573 00574 err: 00575 DBGC ( http, "HTTP %p could not create request: %s\n", 00576 http, strerror ( rc ) ); 00577 http_done ( http, rc ); 00578 ref_put ( &http->refcnt ); 00579 return rc; 00580 }
| static int http_open | ( | struct xfer_interface * | xfer, | |
| struct uri * | uri | |||
| ) | [static] |
Initiate an HTTP connection.
| rc | Return status code |
Definition at line 589 of file http.c.
References http_open_filter(), HTTP_PORT, and NULL.
00589 { 00590 return http_open_filter ( xfer, uri, HTTP_PORT, NULL ); 00591 }
struct http_header_handler http_header_handlers[] [static] |
Initial value:
{
{
.header = "Location",
.rx = http_rx_location,
},
{
.header = "Content-Length",
.rx = http_rx_content_length,
},
{ NULL, NULL }
}
struct http_line_handler http_line_handlers[] [static] |
Initial value:
{
[HTTP_RX_RESPONSE] = { .rx = http_rx_response },
[HTTP_RX_HEADER] = { .rx = http_rx_header },
}
struct xfer_interface_operations http_socket_operations [static] |
Initial value:
{
.close = http_socket_close,
.vredirect = xfer_vreopen,
.window = unlimited_xfer_window,
.alloc_iob = default_xfer_alloc_iob,
.deliver_iob = http_socket_deliver_iob,
.deliver_raw = xfer_deliver_as_iob,
}
struct xfer_interface_operations http_xfer_operations [static] |
Initial value:
{
.close = http_xfer_close,
.vredirect = ignore_xfer_vredirect,
.window = unlimited_xfer_window,
.alloc_iob = default_xfer_alloc_iob,
.deliver_iob = xfer_deliver_as_raw,
.deliver_raw = ignore_xfer_deliver_raw,
}
| struct uri_opener http_uri_opener __uri_opener |
1.5.7.1