|  | @@ -1,187 +1,346 @@
 | 
		
	
		
			
			|  | 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 | +
 | 
		
	
		
			
			| 1 | 26 |  #include <stddef.h>
 | 
		
	
		
			
			| 2 | 27 |  #include <stdlib.h>
 | 
		
	
		
			
			| 3 | 28 |  #include <string.h>
 | 
		
	
		
			
			|  | 29 | +#include <strings.h>
 | 
		
	
		
			
			| 4 | 30 |  #include <vsprintf.h>
 | 
		
	
		
			
			| 5 | 31 |  #include <assert.h>
 | 
		
	
		
			
			| 6 | 32 |  #include <gpxe/async.h>
 | 
		
	
		
			
			|  | 33 | +#include <gpxe/buffer.h>
 | 
		
	
		
			
			| 7 | 34 |  #include <gpxe/http.h>
 | 
		
	
		
			
			| 8 | 35 |  
 | 
		
	
		
			
			| 9 |  | -/** @file
 | 
		
	
		
			
			| 10 |  | - *
 | 
		
	
		
			
			| 11 |  | - * The Hyper Text Transfer Protocol (HTTP)
 | 
		
	
		
			
			| 12 |  | - *
 | 
		
	
		
			
			| 13 |  | - * This file implements the TCP-based HTTP protocol. It connects to the
 | 
		
	
		
			
			| 14 |  | - * server specified in http_request::tcp and transmit an HTTP GET request
 | 
		
	
		
			
			| 15 |  | - * for the file specified in http_request::filename. It then decoded the
 | 
		
	
		
			
			| 16 |  | - * HTTP header, determining file status and file size. Then sends the file
 | 
		
	
		
			
			| 17 |  | - * to the callback function at http_request::callback().
 | 
		
	
		
			
			| 18 |  | - * **NOTE**: still working on correcting the closing of the tcp connection
 | 
		
	
		
			
			| 19 |  | - *
 | 
		
	
		
			
			| 20 |  | - * To use this code, do something like:
 | 
		
	
		
			
			|  | 36 | +static inline struct http_request *
 | 
		
	
		
			
			|  | 37 | +tcp_to_http ( struct tcp_application *app ) {
 | 
		
	
		
			
			|  | 38 | +	return container_of ( app, struct http_request, tcp );
 | 
		
	
		
			
			|  | 39 | +}
 | 
		
	
		
			
			|  | 40 | +
 | 
		
	
		
			
			|  | 41 | +/**
 | 
		
	
		
			
			|  | 42 | + * Mark HTTP request as complete
 | 
		
	
		
			
			| 21 | 43 |   *
 | 
		
	
		
			
			| 22 |  | - * @code
 | 
		
	
		
			
			|  | 44 | + * @v http		HTTP request
 | 
		
	
		
			
			|  | 45 | + * @v rc		Return status code
 | 
		
	
		
			
			| 23 | 46 |   *
 | 
		
	
		
			
			| 24 |  | - *   static void my_callback ( struct http_request *http, char *data, size_t len ) {
 | 
		
	
		
			
			| 25 |  | - *     ... process data ...
 | 
		
	
		
			
			| 26 |  | - *   }
 | 
		
	
		
			
			|  | 47 | + */
 | 
		
	
		
			
			|  | 48 | +static void http_done ( struct http_request *http, int rc ) {
 | 
		
	
		
			
			|  | 49 | +
 | 
		
	
		
			
			|  | 50 | +	/* Close TCP connection */
 | 
		
	
		
			
			|  | 51 | +	tcp_close ( &http->tcp );
 | 
		
	
		
			
			|  | 52 | +
 | 
		
	
		
			
			|  | 53 | +	/* Prevent further processing of any current packet */
 | 
		
	
		
			
			|  | 54 | +	http->rx_state = HTTP_RX_DEAD;
 | 
		
	
		
			
			|  | 55 | +
 | 
		
	
		
			
			|  | 56 | +	/* Free up any dynamically allocated storage */
 | 
		
	
		
			
			|  | 57 | +	empty_line_buffer ( &http->linebuf );
 | 
		
	
		
			
			|  | 58 | +
 | 
		
	
		
			
			|  | 59 | +	/* If we had a Content-Length, and the received content length
 | 
		
	
		
			
			|  | 60 | +	 * isn't correct, flag an error
 | 
		
	
		
			
			|  | 61 | +	 */
 | 
		
	
		
			
			|  | 62 | +	if ( http->content_length &&
 | 
		
	
		
			
			|  | 63 | +	     ( http->content_length != http->buffer->fill ) ) {
 | 
		
	
		
			
			|  | 64 | +		DBGC ( http, "HTTP %p incorrect length %zd, should be %zd\n",
 | 
		
	
		
			
			|  | 65 | +		       http, http->buffer->fill, http->content_length );
 | 
		
	
		
			
			|  | 66 | +		rc = -EIO;
 | 
		
	
		
			
			|  | 67 | +	}
 | 
		
	
		
			
			|  | 68 | +
 | 
		
	
		
			
			|  | 69 | +	/* Mark async operation as complete */
 | 
		
	
		
			
			|  | 70 | +	async_done ( &http->aop, rc );
 | 
		
	
		
			
			|  | 71 | +}
 | 
		
	
		
			
			|  | 72 | +
 | 
		
	
		
			
			|  | 73 | +/**
 | 
		
	
		
			
			|  | 74 | + * Convert HTTP response code to return status code
 | 
		
	
		
			
			| 27 | 75 |   *
 | 
		
	
		
			
			| 28 |  | - *   struct http_request http = {
 | 
		
	
		
			
			| 29 |  | - *     .filename = "path/to/file",
 | 
		
	
		
			
			| 30 |  | - *     .callback = my_callback,
 | 
		
	
		
			
			| 31 |  | - *   };
 | 
		
	
		
			
			|  | 76 | + * @v response		HTTP response code
 | 
		
	
		
			
			|  | 77 | + * @ret rc		Return status code
 | 
		
	
		
			
			|  | 78 | + */
 | 
		
	
		
			
			|  | 79 | +static int http_response_to_rc ( unsigned int response ) {
 | 
		
	
		
			
			|  | 80 | +	switch ( response ) {
 | 
		
	
		
			
			|  | 81 | +	case 200:
 | 
		
	
		
			
			|  | 82 | +		return 0;
 | 
		
	
		
			
			|  | 83 | +	case 404:
 | 
		
	
		
			
			|  | 84 | +		return -ENOENT;
 | 
		
	
		
			
			|  | 85 | +	case 403:
 | 
		
	
		
			
			|  | 86 | +		return -EPERM;
 | 
		
	
		
			
			|  | 87 | +	default:
 | 
		
	
		
			
			|  | 88 | +		return -EIO;
 | 
		
	
		
			
			|  | 89 | +	}
 | 
		
	
		
			
			|  | 90 | +}
 | 
		
	
		
			
			|  | 91 | +
 | 
		
	
		
			
			|  | 92 | +/**
 | 
		
	
		
			
			|  | 93 | + * Handle HTTP response
 | 
		
	
		
			
			| 32 | 94 |   *
 | 
		
	
		
			
			| 33 |  | - *   ... assign http.tcp.server ...
 | 
		
	
		
			
			|  | 95 | + * @v http		HTTP request
 | 
		
	
		
			
			|  | 96 | + * @v response		HTTP response
 | 
		
	
		
			
			|  | 97 | + */
 | 
		
	
		
			
			|  | 98 | +static void http_rx_response ( struct http_request *http, char *response ) {
 | 
		
	
		
			
			|  | 99 | +	char *spc;
 | 
		
	
		
			
			|  | 100 | +	int rc = -EIO;
 | 
		
	
		
			
			|  | 101 | +
 | 
		
	
		
			
			|  | 102 | +	DBGC ( http, "HTTP %p response \"%s\"\n", http, response );
 | 
		
	
		
			
			|  | 103 | +
 | 
		
	
		
			
			|  | 104 | +	/* Check response starts with "HTTP/" */
 | 
		
	
		
			
			|  | 105 | +	if ( strncmp ( response, "HTTP/", 5 ) != 0 )
 | 
		
	
		
			
			|  | 106 | +		goto err;
 | 
		
	
		
			
			|  | 107 | +
 | 
		
	
		
			
			|  | 108 | +	/* Locate and check response code */
 | 
		
	
		
			
			|  | 109 | +	spc = strchr ( response, ' ' );
 | 
		
	
		
			
			|  | 110 | +	if ( ! spc )
 | 
		
	
		
			
			|  | 111 | +		goto err;
 | 
		
	
		
			
			|  | 112 | +	http->response = strtoul ( spc, NULL, 10 );
 | 
		
	
		
			
			|  | 113 | +	if ( ( rc = http_response_to_rc ( http->response ) ) != 0 )
 | 
		
	
		
			
			|  | 114 | +		goto err;
 | 
		
	
		
			
			|  | 115 | +
 | 
		
	
		
			
			|  | 116 | +	/* Move to received headers */
 | 
		
	
		
			
			|  | 117 | +	http->rx_state = HTTP_RX_HEADER;
 | 
		
	
		
			
			|  | 118 | +	return;
 | 
		
	
		
			
			|  | 119 | +
 | 
		
	
		
			
			|  | 120 | + err:
 | 
		
	
		
			
			|  | 121 | +	DBGC ( http, "HTTP %p bad response\n", http );
 | 
		
	
		
			
			|  | 122 | +	http_done ( http, rc );
 | 
		
	
		
			
			|  | 123 | +	return;
 | 
		
	
		
			
			|  | 124 | +}
 | 
		
	
		
			
			|  | 125 | +
 | 
		
	
		
			
			|  | 126 | +/**
 | 
		
	
		
			
			|  | 127 | + * Handle HTTP Content-Length header
 | 
		
	
		
			
			| 34 | 128 |   *
 | 
		
	
		
			
			| 35 |  | - *   rc = async_wait ( get_http ( &http ) );
 | 
		
	
		
			
			|  | 129 | + * @v http		HTTP request
 | 
		
	
		
			
			|  | 130 | + * @v value		HTTP header value
 | 
		
	
		
			
			|  | 131 | + * @ret rc		Return status code
 | 
		
	
		
			
			|  | 132 | + */
 | 
		
	
		
			
			|  | 133 | +static int http_rx_content_length ( struct http_request *http,
 | 
		
	
		
			
			|  | 134 | +				    const char *value ) {
 | 
		
	
		
			
			|  | 135 | +	char *endp;
 | 
		
	
		
			
			|  | 136 | +
 | 
		
	
		
			
			|  | 137 | +	http->content_length = strtoul ( value, &endp, 10 );
 | 
		
	
		
			
			|  | 138 | +	if ( *endp != '\0' ) {
 | 
		
	
		
			
			|  | 139 | +		DBGC ( http, "HTTP %p invalid Content-Length \"%s\"\n",
 | 
		
	
		
			
			|  | 140 | +		       http, value );
 | 
		
	
		
			
			|  | 141 | +		return -EIO;
 | 
		
	
		
			
			|  | 142 | +	}
 | 
		
	
		
			
			|  | 143 | +
 | 
		
	
		
			
			|  | 144 | +	return 0;
 | 
		
	
		
			
			|  | 145 | +}
 | 
		
	
		
			
			|  | 146 | +
 | 
		
	
		
			
			|  | 147 | +/**
 | 
		
	
		
			
			|  | 148 | + * An HTTP header handler
 | 
		
	
		
			
			| 36 | 149 |   *
 | 
		
	
		
			
			| 37 |  | - * @endcode
 | 
		
	
		
			
			|  | 150 | + */
 | 
		
	
		
			
			|  | 151 | +struct http_header_handler {
 | 
		
	
		
			
			|  | 152 | +	/** Name (e.g. "Content-Length") */
 | 
		
	
		
			
			|  | 153 | +	const char *header;
 | 
		
	
		
			
			|  | 154 | +	/** Handle received header
 | 
		
	
		
			
			|  | 155 | +	 *
 | 
		
	
		
			
			|  | 156 | +	 * @v http	HTTP request
 | 
		
	
		
			
			|  | 157 | +	 * @v value	HTTP header value
 | 
		
	
		
			
			|  | 158 | +	 * @ret rc	Return status code
 | 
		
	
		
			
			|  | 159 | +	 */
 | 
		
	
		
			
			|  | 160 | +	int ( * rx ) ( struct http_request *http, const char *value );
 | 
		
	
		
			
			|  | 161 | +};
 | 
		
	
		
			
			|  | 162 | +
 | 
		
	
		
			
			|  | 163 | +/** List of HTTP header handlers */
 | 
		
	
		
			
			|  | 164 | +struct http_header_handler http_header_handlers[] = {
 | 
		
	
		
			
			|  | 165 | +	{
 | 
		
	
		
			
			|  | 166 | +		.header = "Content-Length",
 | 
		
	
		
			
			|  | 167 | +		.rx = http_rx_content_length,
 | 
		
	
		
			
			|  | 168 | +	},
 | 
		
	
		
			
			|  | 169 | +	{ NULL, NULL }
 | 
		
	
		
			
			|  | 170 | +};
 | 
		
	
		
			
			|  | 171 | +
 | 
		
	
		
			
			|  | 172 | +/**
 | 
		
	
		
			
			|  | 173 | + * Handle HTTP header
 | 
		
	
		
			
			| 38 | 174 |   *
 | 
		
	
		
			
			|  | 175 | + * @v http		HTTP request
 | 
		
	
		
			
			|  | 176 | + * @v header		HTTP header
 | 
		
	
		
			
			| 39 | 177 |   */
 | 
		
	
		
			
			|  | 178 | +static void http_rx_header ( struct http_request *http, char *header ) {
 | 
		
	
		
			
			|  | 179 | +	struct http_header_handler *handler;
 | 
		
	
		
			
			|  | 180 | +	char *separator;
 | 
		
	
		
			
			|  | 181 | +	char *value;
 | 
		
	
		
			
			|  | 182 | +	int rc = -EIO;
 | 
		
	
		
			
			| 40 | 183 |  
 | 
		
	
		
			
			| 41 |  | -static inline struct http_request *
 | 
		
	
		
			
			| 42 |  | -tcp_to_http ( struct tcp_application *app ) {
 | 
		
	
		
			
			| 43 |  | -	return container_of ( app, struct http_request, tcp );
 | 
		
	
		
			
			|  | 184 | +	/* An empty header line marks the transition to the data phase */
 | 
		
	
		
			
			|  | 185 | +	if ( ! header[0] ) {
 | 
		
	
		
			
			|  | 186 | +		DBGC ( http, "HTTP %p start of data\n", http );
 | 
		
	
		
			
			|  | 187 | +		empty_line_buffer ( &http->linebuf );
 | 
		
	
		
			
			|  | 188 | +		http->rx_state = HTTP_RX_DATA;
 | 
		
	
		
			
			|  | 189 | +		return;
 | 
		
	
		
			
			|  | 190 | +	}
 | 
		
	
		
			
			|  | 191 | +
 | 
		
	
		
			
			|  | 192 | +	DBGC ( http, "HTTP %p header \"%s\"\n", http, header );
 | 
		
	
		
			
			|  | 193 | +
 | 
		
	
		
			
			|  | 194 | +	/* Split header at the ": " */
 | 
		
	
		
			
			|  | 195 | +	separator = strstr ( header, ": " );
 | 
		
	
		
			
			|  | 196 | +	if ( ! separator )
 | 
		
	
		
			
			|  | 197 | +		goto err;
 | 
		
	
		
			
			|  | 198 | +	*separator = '\0';
 | 
		
	
		
			
			|  | 199 | +	value = ( separator + 2 );
 | 
		
	
		
			
			|  | 200 | +
 | 
		
	
		
			
			|  | 201 | +	/* Hand off to header handler, if one exists */
 | 
		
	
		
			
			|  | 202 | +	for ( handler = http_header_handlers ; handler->header ; handler++ ) {
 | 
		
	
		
			
			|  | 203 | +		if ( strcasecmp ( header, handler->header ) == 0 ) {
 | 
		
	
		
			
			|  | 204 | +			if ( ( rc = handler->rx ( http, value ) ) != 0 )
 | 
		
	
		
			
			|  | 205 | +				goto err;
 | 
		
	
		
			
			|  | 206 | +			break;
 | 
		
	
		
			
			|  | 207 | +		}
 | 
		
	
		
			
			|  | 208 | +	}
 | 
		
	
		
			
			|  | 209 | +	return;
 | 
		
	
		
			
			|  | 210 | +
 | 
		
	
		
			
			|  | 211 | + err:
 | 
		
	
		
			
			|  | 212 | +	DBGC ( http, "HTTP %p bad header\n", http );
 | 
		
	
		
			
			|  | 213 | +	http_done ( http, rc );
 | 
		
	
		
			
			|  | 214 | +	return;
 | 
		
	
		
			
			| 44 | 215 |  }
 | 
		
	
		
			
			| 45 | 216 |  
 | 
		
	
		
			
			| 46 | 217 |  /**
 | 
		
	
		
			
			| 47 |  | - * Close an HTTP connection
 | 
		
	
		
			
			|  | 218 | + * Handle new data arriving via HTTP connection in the data phase
 | 
		
	
		
			
			| 48 | 219 |   *
 | 
		
	
		
			
			| 49 |  | - * @v app	a TCP Application
 | 
		
	
		
			
			| 50 |  | - * @v status	connection status at close
 | 
		
	
		
			
			|  | 220 | + * @v http		HTTP request
 | 
		
	
		
			
			|  | 221 | + * @v data		New data
 | 
		
	
		
			
			|  | 222 | + * @v len		Length of new data
 | 
		
	
		
			
			| 51 | 223 |   */
 | 
		
	
		
			
			| 52 |  | -static void http_closed ( struct tcp_application *app, int status ) {
 | 
		
	
		
			
			| 53 |  | -	struct http_request *http = tcp_to_http ( app );
 | 
		
	
		
			
			| 54 |  | -	async_done ( &http->aop, status );
 | 
		
	
		
			
			|  | 224 | +static void http_rx_data ( struct http_request *http,
 | 
		
	
		
			
			|  | 225 | +			   const char *data, size_t len ) {
 | 
		
	
		
			
			|  | 226 | +	int rc;
 | 
		
	
		
			
			|  | 227 | +
 | 
		
	
		
			
			|  | 228 | +	/* Fill data buffer */
 | 
		
	
		
			
			|  | 229 | +	if ( ( rc = fill_buffer ( http->buffer, data,
 | 
		
	
		
			
			|  | 230 | +				  http->buffer->fill, len ) ) != 0 ) {
 | 
		
	
		
			
			|  | 231 | +		DBGC ( http, "HTTP %p failed to fill data buffer: %s\n",
 | 
		
	
		
			
			|  | 232 | +		       http, strerror ( rc ) );
 | 
		
	
		
			
			|  | 233 | +		http_done ( http, rc );
 | 
		
	
		
			
			|  | 234 | +		return;
 | 
		
	
		
			
			|  | 235 | +	}
 | 
		
	
		
			
			|  | 236 | +
 | 
		
	
		
			
			|  | 237 | +	/* If we have reached the content-length, stop now */
 | 
		
	
		
			
			|  | 238 | +	if ( http->content_length &&
 | 
		
	
		
			
			|  | 239 | +	     ( http->buffer->fill >= http->content_length ) ) {
 | 
		
	
		
			
			|  | 240 | +		http_done ( http, 0 );
 | 
		
	
		
			
			|  | 241 | +	}
 | 
		
	
		
			
			| 55 | 242 |  }
 | 
		
	
		
			
			| 56 | 243 |  
 | 
		
	
		
			
			| 57 | 244 |  /**
 | 
		
	
		
			
			| 58 |  | - * Callback after a TCP connection is established
 | 
		
	
		
			
			|  | 245 | + * Handle new data arriving via HTTP connection
 | 
		
	
		
			
			| 59 | 246 |   *
 | 
		
	
		
			
			| 60 |  | - * @v app	a TCP Application
 | 
		
	
		
			
			|  | 247 | + * @v http		HTTP request
 | 
		
	
		
			
			|  | 248 | + * @v data		New data
 | 
		
	
		
			
			|  | 249 | + * @v len		Length of new data
 | 
		
	
		
			
			| 61 | 250 |   */
 | 
		
	
		
			
			| 62 |  | -static void http_connected ( struct tcp_application *app ) {
 | 
		
	
		
			
			|  | 251 | +static void http_newdata ( struct tcp_application *app,
 | 
		
	
		
			
			|  | 252 | +			   void *data, size_t len ) {
 | 
		
	
		
			
			| 63 | 253 |  	struct http_request *http = tcp_to_http ( app );
 | 
		
	
		
			
			|  | 254 | +	const char *buf = data;
 | 
		
	
		
			
			|  | 255 | +	char *line;
 | 
		
	
		
			
			|  | 256 | +	int rc;
 | 
		
	
		
			
			| 64 | 257 |  
 | 
		
	
		
			
			| 65 |  | -	http->state = HTTP_REQUEST_FILE;
 | 
		
	
		
			
			|  | 258 | +	while ( len ) {
 | 
		
	
		
			
			|  | 259 | +		if ( http->rx_state == HTTP_RX_DEAD ) {
 | 
		
	
		
			
			|  | 260 | +			/* Do no further processing */
 | 
		
	
		
			
			|  | 261 | +			return;
 | 
		
	
		
			
			|  | 262 | +		} else if ( http->rx_state == HTTP_RX_DATA ) {
 | 
		
	
		
			
			|  | 263 | +			/* Once we're into the data phase, just fill
 | 
		
	
		
			
			|  | 264 | +			 * the data buffer
 | 
		
	
		
			
			|  | 265 | +			 */
 | 
		
	
		
			
			|  | 266 | +			http_rx_data ( http, buf, len );
 | 
		
	
		
			
			|  | 267 | +			return;
 | 
		
	
		
			
			|  | 268 | +		} else {
 | 
		
	
		
			
			|  | 269 | +			/* In the other phases, buffer and process a
 | 
		
	
		
			
			|  | 270 | +			 * line at a time
 | 
		
	
		
			
			|  | 271 | +			 */
 | 
		
	
		
			
			|  | 272 | +			if ( ( rc = line_buffer ( &http->linebuf, &buf,
 | 
		
	
		
			
			|  | 273 | +						  &len ) ) != 0 ) {
 | 
		
	
		
			
			|  | 274 | +				DBGC ( http, "HTTP %p could not buffer line: "
 | 
		
	
		
			
			|  | 275 | +				       "%s\n", http, strerror ( rc ) );
 | 
		
	
		
			
			|  | 276 | +				http_done ( http, rc );
 | 
		
	
		
			
			|  | 277 | +				return;
 | 
		
	
		
			
			|  | 278 | +			}
 | 
		
	
		
			
			|  | 279 | +			if ( ( line = buffered_line ( &http->linebuf ) ) ) {
 | 
		
	
		
			
			|  | 280 | +				switch ( http->rx_state ) {
 | 
		
	
		
			
			|  | 281 | +				case HTTP_RX_RESPONSE:
 | 
		
	
		
			
			|  | 282 | +					http_rx_response ( http, line );
 | 
		
	
		
			
			|  | 283 | +					break;
 | 
		
	
		
			
			|  | 284 | +				case HTTP_RX_HEADER:
 | 
		
	
		
			
			|  | 285 | +					http_rx_header ( http, line );
 | 
		
	
		
			
			|  | 286 | +					break;
 | 
		
	
		
			
			|  | 287 | +				default:
 | 
		
	
		
			
			|  | 288 | +					assert ( 0 );
 | 
		
	
		
			
			|  | 289 | +					break;
 | 
		
	
		
			
			|  | 290 | +				}
 | 
		
	
		
			
			|  | 291 | +			}
 | 
		
	
		
			
			|  | 292 | +		}
 | 
		
	
		
			
			|  | 293 | +	}
 | 
		
	
		
			
			| 66 | 294 |  }
 | 
		
	
		
			
			| 67 | 295 |  
 | 
		
	
		
			
			| 68 | 296 |  /**
 | 
		
	
		
			
			| 69 |  | - * Callback for when TCP data is acknowledged
 | 
		
	
		
			
			|  | 297 | + * Send HTTP data
 | 
		
	
		
			
			| 70 | 298 |   *
 | 
		
	
		
			
			| 71 |  | - * @v app	a TCP Application
 | 
		
	
		
			
			| 72 |  | - * @v len	the length of data acked
 | 
		
	
		
			
			|  | 299 | + * @v app		TCP application
 | 
		
	
		
			
			|  | 300 | + * @v buf		Temporary data buffer
 | 
		
	
		
			
			|  | 301 | + * @v len		Length of temporary data buffer
 | 
		
	
		
			
			| 73 | 302 |   */
 | 
		
	
		
			
			| 74 |  | -static void http_acked ( struct tcp_application *app, size_t len __attribute__ ((unused)) ) {
 | 
		
	
		
			
			|  | 303 | +static void http_senddata ( struct tcp_application *app,
 | 
		
	
		
			
			|  | 304 | +			    void *buf, size_t len ) {
 | 
		
	
		
			
			| 75 | 305 |  	struct http_request *http = tcp_to_http ( app );
 | 
		
	
		
			
			| 76 | 306 |  
 | 
		
	
		
			
			| 77 |  | -	// assume that the whole GET request was sent in on epacket
 | 
		
	
		
			
			| 78 |  | -
 | 
		
	
		
			
			| 79 |  | -	switch ( http->state ) {
 | 
		
	
		
			
			| 80 |  | -	case HTTP_REQUEST_FILE:
 | 
		
	
		
			
			| 81 |  | -		http->state = HTTP_PARSE_HEADER;
 | 
		
	
		
			
			| 82 |  | -		break;
 | 
		
	
		
			
			| 83 |  | -	case HTTP_PARSE_HEADER:
 | 
		
	
		
			
			| 84 |  | -	case HTTP_RECV_FILE:
 | 
		
	
		
			
			| 85 |  | -		break;
 | 
		
	
		
			
			| 86 |  | -	case HTTP_DONE:
 | 
		
	
		
			
			| 87 |  | -		//tcp_close(app);
 | 
		
	
		
			
			| 88 |  | -		break;
 | 
		
	
		
			
			| 89 |  | -	default:
 | 
		
	
		
			
			| 90 |  | -		break;
 | 
		
	
		
			
			| 91 |  | -	}
 | 
		
	
		
			
			| 92 |  | -	//printf("acked\n");
 | 
		
	
		
			
			|  | 307 | +	len = snprintf ( buf, len,
 | 
		
	
		
			
			|  | 308 | +			 "GET /%s HTTP/1.1\r\n"
 | 
		
	
		
			
			|  | 309 | +			 "User-Agent: gPXE/" VERSION "\r\n"
 | 
		
	
		
			
			|  | 310 | +			 "Host: %s\r\n"
 | 
		
	
		
			
			|  | 311 | +			 "\r\n", http->filename, http->hostname );
 | 
		
	
		
			
			|  | 312 | +	tcp_send ( app, ( buf + http->tx_offset ), ( len - http->tx_offset ) );
 | 
		
	
		
			
			| 93 | 313 |  }
 | 
		
	
		
			
			| 94 | 314 |  
 | 
		
	
		
			
			| 95 | 315 |  /**
 | 
		
	
		
			
			| 96 |  | - * Callback when new TCP data is recieved
 | 
		
	
		
			
			|  | 316 | + * HTTP data acknowledged
 | 
		
	
		
			
			| 97 | 317 |   *
 | 
		
	
		
			
			| 98 |  | - * @v app	a TCP Application
 | 
		
	
		
			
			| 99 |  | - * @v data	a pointer to the data recieved
 | 
		
	
		
			
			| 100 |  | - * @v len	length of data buffer
 | 
		
	
		
			
			|  | 318 | + * @v app		TCP application
 | 
		
	
		
			
			|  | 319 | + * @v len		Length of acknowledged data
 | 
		
	
		
			
			| 101 | 320 |   */
 | 
		
	
		
			
			| 102 |  | -static void http_newdata ( struct tcp_application *app, void *data,
 | 
		
	
		
			
			| 103 |  | -			    size_t len ) {
 | 
		
	
		
			
			|  | 321 | +static void http_acked ( struct tcp_application *app, size_t len ) {
 | 
		
	
		
			
			| 104 | 322 |  	struct http_request *http = tcp_to_http ( app );
 | 
		
	
		
			
			| 105 |  | -	char *content_length;
 | 
		
	
		
			
			| 106 |  | -	char *start = data;
 | 
		
	
		
			
			| 107 |  | -	char *rcp; int rc;
 | 
		
	
		
			
			| 108 |  | -
 | 
		
	
		
			
			| 109 |  | -	switch ( http->state ) {
 | 
		
	
		
			
			| 110 |  | -	case HTTP_PARSE_HEADER:
 | 
		
	
		
			
			| 111 |  | -		if(strncmp("HTTP/",data,5) != 0){
 | 
		
	
		
			
			| 112 |  | -			// no http header
 | 
		
	
		
			
			| 113 |  | -			printf("Error: no HTTP Header\n");
 | 
		
	
		
			
			| 114 |  | -		}
 | 
		
	
		
			
			| 115 |  | -		// if rc is not 200, then handle problem
 | 
		
	
		
			
			| 116 |  | -		// either redirect or not there
 | 
		
	
		
			
			| 117 |  | -		rcp = strstr(data,"HTTP");
 | 
		
	
		
			
			| 118 |  | -		if(rcp == NULL){ printf("Could not find header status line.\n"); }
 | 
		
	
		
			
			| 119 |  | -		rcp += 9;
 | 
		
	
		
			
			| 120 |  | -		rc = strtoul(rcp,NULL,10);
 | 
		
	
		
			
			| 121 |  | -		printf("RC=%d\n",rc);
 | 
		
	
		
			
			| 122 |  | -		content_length = strstr(data,"Content-Length: ");
 | 
		
	
		
			
			| 123 |  | -		if(content_length != NULL){
 | 
		
	
		
			
			| 124 |  | -			content_length += 16;
 | 
		
	
		
			
			| 125 |  | -			http->file_size = strtoul(content_length,NULL,10);
 | 
		
	
		
			
			| 126 |  | -			http->file_recv = 0;
 | 
		
	
		
			
			| 127 |  | -			printf("http->file_size = %d\n", http->file_size);
 | 
		
	
		
			
			| 128 |  | -		}
 | 
		
	
		
			
			| 129 |  | -		start = strstr(data,"\r\n\r\n");
 | 
		
	
		
			
			| 130 |  | -		if(start == NULL){ printf("No end of header\n"); }
 | 
		
	
		
			
			| 131 |  | -		else{
 | 
		
	
		
			
			| 132 |  | -			start += 4;
 | 
		
	
		
			
			| 133 |  | -			len -= ((void *)start - data);
 | 
		
	
		
			
			| 134 |  | -			http->state = HTTP_RECV_FILE;
 | 
		
	
		
			
			| 135 |  | -		}
 | 
		
	
		
			
			| 136 | 323 |  
 | 
		
	
		
			
			| 137 |  | -		if ( http->state != HTTP_RECV_FILE )
 | 
		
	
		
			
			| 138 |  | -			break;
 | 
		
	
		
			
			| 139 |  | -	case HTTP_RECV_FILE:
 | 
		
	
		
			
			| 140 |  | -		http->callback(http,start,len);
 | 
		
	
		
			
			| 141 |  | -		//http->file_size -= len;
 | 
		
	
		
			
			| 142 |  | -		//printf("File recv is %d\n", http->file_recv);
 | 
		
	
		
			
			| 143 |  | -		if ( http->file_recv == http->file_size ){
 | 
		
	
		
			
			| 144 |  | -			http->state = HTTP_DONE;
 | 
		
	
		
			
			| 145 |  | -			tcp_close(app);
 | 
		
	
		
			
			| 146 |  | -		}
 | 
		
	
		
			
			| 147 |  | -		break;
 | 
		
	
		
			
			| 148 |  | -	case HTTP_REQUEST_FILE:
 | 
		
	
		
			
			| 149 |  | -	case HTTP_DONE:
 | 
		
	
		
			
			| 150 |  | -	default:
 | 
		
	
		
			
			| 151 |  | -		break;
 | 
		
	
		
			
			| 152 |  | -	}
 | 
		
	
		
			
			|  | 324 | +	http->tx_offset += len;
 | 
		
	
		
			
			| 153 | 325 |  }
 | 
		
	
		
			
			| 154 | 326 |  
 | 
		
	
		
			
			| 155 | 327 |  /**
 | 
		
	
		
			
			| 156 |  | - * Callback for sending TCP data
 | 
		
	
		
			
			|  | 328 | + * HTTP connection closed by network stack
 | 
		
	
		
			
			| 157 | 329 |   *
 | 
		
	
		
			
			| 158 |  | - * @v app	a TCP Application
 | 
		
	
		
			
			|  | 330 | + * @v app		TCP application
 | 
		
	
		
			
			| 159 | 331 |   */
 | 
		
	
		
			
			| 160 |  | -static void http_senddata ( struct tcp_application *app, void *buf, size_t len ) {
 | 
		
	
		
			
			|  | 332 | +static void http_closed ( struct tcp_application *app, int rc ) {
 | 
		
	
		
			
			| 161 | 333 |  	struct http_request *http = tcp_to_http ( app );
 | 
		
	
		
			
			| 162 | 334 |  
 | 
		
	
		
			
			| 163 |  | -	switch ( http->state ){
 | 
		
	
		
			
			| 164 |  | -	case HTTP_REQUEST_FILE:
 | 
		
	
		
			
			| 165 |  | -		len = snprintf(buf,len,"GET %s HTTP/1.0\r\n\r\n",http->filename);
 | 
		
	
		
			
			| 166 |  | -		printf("%s\n",(char *)buf);
 | 
		
	
		
			
			| 167 |  | -        	// string is: GET <file> HTTP/1.0\r\n\r\n
 | 
		
	
		
			
			| 168 |  | -
 | 
		
	
		
			
			| 169 |  | -		tcp_send ( app, buf, len);
 | 
		
	
		
			
			| 170 |  | -		break;
 | 
		
	
		
			
			| 171 |  | -	case HTTP_PARSE_HEADER:
 | 
		
	
		
			
			| 172 |  | -	case HTTP_RECV_FILE:
 | 
		
	
		
			
			| 173 |  | -		break;
 | 
		
	
		
			
			| 174 |  | -	case HTTP_DONE:
 | 
		
	
		
			
			| 175 |  | -		//tcp_close(app)
 | 
		
	
		
			
			| 176 |  | -		break;
 | 
		
	
		
			
			| 177 |  | -	default:
 | 
		
	
		
			
			| 178 |  | -		break;
 | 
		
	
		
			
			| 179 |  | -	}
 | 
		
	
		
			
			|  | 335 | +	DBGC ( http, "HTTP %p connection closed: %s\n",
 | 
		
	
		
			
			|  | 336 | +	       http, strerror ( rc ) );
 | 
		
	
		
			
			|  | 337 | +	
 | 
		
	
		
			
			|  | 338 | +	http_done ( http, rc );
 | 
		
	
		
			
			| 180 | 339 |  }
 | 
		
	
		
			
			| 181 | 340 |  
 | 
		
	
		
			
			|  | 341 | +/** HTTP TCP operations */
 | 
		
	
		
			
			| 182 | 342 |  static struct tcp_operations http_tcp_operations = {
 | 
		
	
		
			
			| 183 | 343 |  	.closed		= http_closed,
 | 
		
	
		
			
			| 184 |  | -	.connected	= http_connected,
 | 
		
	
		
			
			| 185 | 344 |  	.acked		= http_acked,
 | 
		
	
		
			
			| 186 | 345 |  	.newdata	= http_newdata,
 | 
		
	
		
			
			| 187 | 346 |  	.senddata	= http_senddata,
 | 
		
	
	
		
			
			|  | @@ -192,11 +351,10 @@ static struct tcp_operations http_tcp_operations = {
 | 
		
	
		
			
			| 192 | 351 |   *
 | 
		
	
		
			
			| 193 | 352 |   * @v http	a HTTP request
 | 
		
	
		
			
			| 194 | 353 |   */
 | 
		
	
		
			
			| 195 |  | -struct async_operation * get_http ( struct http_request *http ) {
 | 
		
	
		
			
			|  | 354 | +struct async_operation * http_get ( struct http_request *http ) {
 | 
		
	
		
			
			| 196 | 355 |  	int rc;
 | 
		
	
		
			
			| 197 | 356 |  
 | 
		
	
		
			
			| 198 | 357 |  	http->tcp.tcp_op = &http_tcp_operations;
 | 
		
	
		
			
			| 199 |  | -	http->state = HTTP_REQUEST_FILE;
 | 
		
	
		
			
			| 200 | 358 |  	if ( ( rc = tcp_connect ( &http->tcp, &http->server, 0 ) ) != 0 )
 | 
		
	
		
			
			| 201 | 359 |  		async_done ( &http->aop, rc );
 | 
		
	
		
			
			| 202 | 360 |  
 |