ソースを参照

Fixed HTTP

tags/v0.9.3
Michael Brown 17年前
コミット
497c3a5aad
3個のファイルの変更318行の追加197行の削除
  1. 28
    28
      src/include/gpxe/http.h
  2. 290
    132
      src/net/tcp/http.c
  3. 0
    37
      src/tests/httptest.c

+ 28
- 28
src/include/gpxe/http.h ファイルの表示

@@ -10,51 +10,51 @@
10 10
 #include <stdint.h>
11 11
 #include <gpxe/tcp.h>
12 12
 #include <gpxe/async.h>
13
+#include <gpxe/linebuf.h>
13 14
 
14 15
 /** HTTP default port */
15 16
 #define HTTP_PORT 80
16 17
 
17
-enum http_state {
18
-	HTTP_INIT_CONN = 0,
19
-	HTTP_REQUEST_FILE,
20
-	HTTP_PARSE_HEADER,
21
-	HTTP_RECV_FILE,
22
-	HTTP_DONE,
18
+/** HTTP receive state */
19
+enum http_rx_state {
20
+	HTTP_RX_RESPONSE = 0,
21
+	HTTP_RX_HEADER,
22
+	HTTP_RX_DATA,
23
+	HTTP_RX_DEAD,
23 24
 };
24 25
 
25 26
 /**
26
- * A HTTP request
27
+ * An HTTP request
27 28
  *
28 29
  */
29
-struct http_request;
30
-
31 30
 struct http_request {
32 31
 	/** Server address */
33 32
 	struct sockaddr_tcpip server;
33
+	/** Server host name */
34
+	const char *hostname;
35
+	/** Filename */
36
+	const char *filename;
37
+	/** Data buffer to fill */
38
+	struct buffer *buffer;
39
+
40
+	/** HTTP response code */
41
+	unsigned int response;
42
+	/** HTTP Content-Length */
43
+	size_t content_length;
44
+
45
+	/** Number of bytes already sent */
46
+	size_t tx_offset;
47
+	/** RX state */
48
+	enum http_rx_state rx_state;
49
+	/** Line buffer for received header lines */
50
+	struct line_buffer linebuf;
51
+
34 52
 	/** TCP application for this request */
35 53
 	struct tcp_application tcp;
36
-	/** Current state */
37
-	enum http_state state;
38
-        /** File to download */
39
-        const char *filename;
40
-        /** Size of file downloading */
41
-        size_t file_size;
42
-	/** Number of bytes recieved so far */
43
-	size_t file_recv;
44
-	/** Callback function
45
-	 *
46
-	 * @v http	HTTP request struct
47
-	 * @v data	Received data
48
-	 * @v len	Length of received data
49
-	 *
50
-	 * This function is called for all data received from the
51
-	 * remote server.
52
-	 */
53
-	void ( *callback ) ( struct http_request *http, char *data, size_t len );
54 54
 	/** Asynchronous operation */
55 55
 	struct async_operation aop;
56 56
 };
57 57
 
58
-extern struct async_operation * get_http ( struct http_request *http );
58
+extern struct async_operation * http_get ( struct http_request *http );
59 59
 
60 60
 #endif /* _GPXE_HTTP_H */

+ 290
- 132
src/net/tcp/http.c ファイルの表示

@@ -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
 

+ 0
- 37
src/tests/httptest.c ファイルの表示

@@ -1,37 +0,0 @@
1
-#include <stdint.h>
2
-#include <string.h>
3
-#include <byteswap.h>
4
-#include <console.h>
5
-#include <vsprintf.h>
6
-#include <gpxe/async.h>
7
-#include <gpxe/http.h>
8
-#include <gpxe/ip.h>
9
-#include <gpxe/uaccess.h>
10
-#include "pxe.h"
11
-
12
-static void test_http_callback ( struct http_request *http, char *data, size_t len ) {
13
-	userptr_t pxe_buffer = real_to_user ( 0, 0x7c00 );
14
-	unsigned long offset = http->file_recv;
15
-	http->file_recv += len;
16
-	copy_to_user ( pxe_buffer, offset, data, len );
17
-}
18
-
19
-void test_http ( struct net_device *netdev, struct sockaddr_tcpip *server, const char *filename ) {
20
-	struct http_request http;
21
-	int rc;
22
-
23
-	memset ( &http, 0, sizeof ( http ) );
24
-	memcpy ( &http.server, server, sizeof ( http.server ) );
25
-	http.filename = filename;
26
-	http.callback = test_http_callback;
27
-
28
-	rc = async_wait ( get_http ( &http ) );
29
-	if ( rc ) {
30
-		printf ( "HTTP fetch failed\n" );
31
-	}
32
-
33
-	printf ( "Attempting PXE boot\n" );
34
-	pxe_netdev = netdev;
35
-	rc = pxe_boot();
36
-	printf ( "PXE NBP returned with status %04x\n", rc);
37
-}

読み込み中…
キャンセル
保存