Browse Source

New HTTP protocol and test code

tags/v0.9.3
Derek Pryor 18 years ago
parent
commit
25ea34a8d7
4 changed files with 318 additions and 0 deletions
  1. 58
    0
      src/include/gpxe/http.h
  2. 201
    0
      src/net/tcp/http.c
  3. 22
    0
      src/tests/dhcptest.c
  4. 37
    0
      src/tests/httptest.c

+ 58
- 0
src/include/gpxe/http.h View File

@@ -0,0 +1,58 @@
1
+#ifndef _GPXE_HTTP_H
2
+#define _GPXE_HTTP_H
3
+
4
+/** @file
5
+ *
6
+ * Hyper Text Transport Protocol
7
+ *
8
+ */
9
+
10
+#include <stdint.h>
11
+#include <gpxe/tcp.h>
12
+#include <gpxe/async.h>
13
+
14
+/** HTTP default port */
15
+#define HTTP_PORT 80
16
+
17
+enum http_state {
18
+	HTTP_INIT_CONN = 0,
19
+	HTTP_REQUEST_FILE,
20
+	HTTP_PARSE_HEADER,
21
+	HTTP_RECV_FILE,
22
+	HTTP_DONE,
23
+};
24
+
25
+/**
26
+ * A HTTP request
27
+ *
28
+ */
29
+struct http_request;
30
+
31
+struct http_request {
32
+	/** TCP connection for this request */
33
+	struct tcp_connection tcp;
34
+	/** Current state */
35
+	enum http_state state;
36
+        /** File to download */
37
+        const char *filename;
38
+        /** Size of file downloading */
39
+        size_t file_size;
40
+	/** Number of bytes recieved so far */
41
+	size_t file_recv;
42
+	/** Callback function
43
+	 *
44
+	 * @v http	HTTP request struct
45
+	 * @v data	Received data
46
+	 * @v len	Length of received data
47
+	 *
48
+	 * This function is called for all data received from the
49
+	 * remote server.
50
+	 */
51
+	void ( *callback ) ( struct http_request *http, char *data, size_t len );
52
+	/** Asynchronous operation */
53
+	struct async_operation aop;
54
+};
55
+
56
+extern struct async_operation * get_http ( struct http_request *http );
57
+
58
+#endif /* _GPXE_HTTP_H */

+ 201
- 0
src/net/tcp/http.c View File

@@ -0,0 +1,201 @@
1
+#include <stddef.h>
2
+#include <string.h>
3
+#include <vsprintf.h>
4
+#include <assert.h>
5
+#include <gpxe/async.h>
6
+#include <gpxe/http.h>
7
+
8
+/** @file
9
+ *
10
+ * The Hyper Text Transfer Protocol (HTTP)
11
+ *
12
+ * This file implements the TCP-based HTTP protocol. It connects to the
13
+ * server specified in http_request::tcp and transmit an HTTP GET request
14
+ * for the file specified in http_request::filename. It then decoded the
15
+ * HTTP header, determining file status and file size. Then sends the file
16
+ * to the callback function at http_request::callback().
17
+ * **NOTE**: still working on correcting the closing of the tcp connection
18
+ *
19
+ * To use this code, do something like:
20
+ *
21
+ * @code
22
+ *
23
+ *   static void my_callback ( struct http_request *http, char *data, size_t len ) {
24
+ *     ... process data ...
25
+ *   }
26
+ *
27
+ *   struct http_request http = {
28
+ *     .filename = "path/to/file",
29
+ *     .callback = my_callback,
30
+ *   };
31
+ *
32
+ *   ... assign http.tcp.server ...
33
+ *
34
+ *   rc = async_wait ( get_http ( &http ) );
35
+ *
36
+ * @endcode
37
+ *
38
+ */
39
+
40
+static inline struct http_request *
41
+tcp_to_http ( struct tcp_connection *conn ) {
42
+	return container_of ( conn, struct http_request, tcp );
43
+}
44
+
45
+/**
46
+ * Close an HTTP connection
47
+ *
48
+ * @v conn	a TCP Connection
49
+ * @v status	connection status at close
50
+ */
51
+static void http_closed ( struct tcp_connection *conn, int status ) {
52
+	struct http_request *http = tcp_to_http ( conn );
53
+	async_done ( &http->aop, status );
54
+}
55
+
56
+/**
57
+ * Callback after a TCP connection is established
58
+ *
59
+ * @v conn	a TCP Connection
60
+ */
61
+static void http_connected ( struct tcp_connection *conn ) {
62
+	struct http_request *http = tcp_to_http ( conn );
63
+
64
+	http->state = HTTP_REQUEST_FILE;
65
+}
66
+
67
+/**
68
+ * Callback for when TCP data is acknowledged
69
+ *
70
+ * @v conn	a TCP Connection
71
+ * @v len	the length of data acked
72
+ */
73
+static void http_acked ( struct tcp_connection *conn, size_t len ) {
74
+	struct http_request *http = tcp_to_http ( conn );
75
+
76
+	// assume that the whole GET request was sent in on epacket
77
+
78
+	switch ( http->state ) {
79
+	case HTTP_REQUEST_FILE:
80
+		http->state = HTTP_PARSE_HEADER;
81
+		break;
82
+	case HTTP_PARSE_HEADER:
83
+	case HTTP_RECV_FILE:
84
+		break;
85
+	case HTTP_DONE:
86
+		//tcp_close(conn);
87
+		break;
88
+	default:
89
+		break;
90
+	}
91
+	//printf("acked\n");
92
+}
93
+
94
+/**
95
+ * Callback when new TCP data is recieved
96
+ *
97
+ * @v conn	a TCP Connection
98
+ * @v data	a pointer to the data recieved
99
+ * @v len	length of data buffer
100
+ */
101
+static void http_newdata ( struct tcp_connection *conn, void *data,
102
+			    size_t len ) {
103
+	struct http_request *http = tcp_to_http ( conn );
104
+	char *content_length;
105
+	char *start = data;
106
+	char *rcp; int rc;
107
+
108
+	switch ( http->state ) {
109
+	case HTTP_PARSE_HEADER:
110
+		if(strncmp("HTTP/",data,5) != 0){
111
+			// no http header
112
+			printf("Error: no HTTP Header\n");
113
+		}
114
+		// if rc is not 200, then handle problem
115
+		// either redirect or not there
116
+		rcp = strstr(data,"HTTP");
117
+		if(rcp == NULL){ printf("Could not find header status line.\n"); }
118
+		rcp += 9;
119
+		rc = strtoul(rcp,NULL,10);
120
+		printf("RC=%d\n",rc);
121
+		content_length = strstr(data,"Content-Length: ");
122
+		if(content_length != NULL){
123
+			content_length += 16;
124
+			http->file_size = strtoul(content_length,NULL,10);
125
+			http->file_recv = 0;
126
+			printf("http->file_size = %d\n", http->file_size);
127
+		}
128
+		start = strstr(data,"\r\n\r\n");
129
+		if(start == NULL){ printf("No end of header\n"); }
130
+		else{
131
+			start += 4;
132
+			len -= ((void *)start - data);
133
+			http->state = HTTP_RECV_FILE;
134
+		}
135
+
136
+		if ( http->state != HTTP_RECV_FILE )
137
+			break;
138
+	case HTTP_RECV_FILE:
139
+		http->callback(http,start,len);
140
+		//http->file_size -= len;
141
+		//printf("File recv is %d\n", http->file_recv);
142
+		if ( http->file_recv == http->file_size ){
143
+			http->state = HTTP_DONE;
144
+			tcp_close(conn);
145
+		}
146
+		break;
147
+	case HTTP_REQUEST_FILE:
148
+	case HTTP_DONE:
149
+	default:
150
+		break;
151
+	}
152
+}
153
+
154
+/**
155
+ * Callback for sending TCP data
156
+ *
157
+ * @v conn	a TCP Connection
158
+ */
159
+static void http_senddata ( struct tcp_connection *conn, void *buf, size_t len ) {
160
+	struct http_request *http = tcp_to_http ( conn );
161
+	char buf[66]; // 16 request + 50 for filename
162
+	size_t len;
163
+
164
+	switch ( http->state ){
165
+	case HTTP_REQUEST_FILE:
166
+		len = snprintf(buf,66,"GET %s HTTP/1.0\r\n\r\n",http->filename);
167
+		printf("%s\n",buf);
168
+        	// string is: GET <file> HTTP/1.0\r\n\r\n
169
+
170
+		tcp_send ( conn, buf, len);
171
+		break;
172
+	case HTTP_PARSE_HEADER:
173
+	case HTTP_RECV_FILE:
174
+		break;
175
+	case HTTP_DONE:
176
+		//tcp_close(conn)
177
+		break;
178
+	default:
179
+		break;
180
+	}
181
+}
182
+
183
+static struct tcp_operations http_tcp_operations = {
184
+	.closed		= http_closed,
185
+	.connected	= http_connected,
186
+	.acked		= http_acked,
187
+	.newdata	= http_newdata,
188
+	.senddata	= http_senddata,
189
+};
190
+
191
+/**
192
+ * Initiate a HTTP connection
193
+ *
194
+ * @v http	a HTTP request
195
+ */
196
+struct async_operation * get_http ( struct http_request *http ) {
197
+	http->tcp.tcp_op = &http_tcp_operations;
198
+	http->state = HTTP_REQUEST_FILE;
199
+	tcp_connect ( &http->tcp );
200
+	return &http->aop;
201
+}

+ 22
- 0
src/tests/dhcptest.c View File

@@ -57,6 +57,26 @@ static int test_dhcp_hello ( char *helloname ) {
57 57
 	return 0;
58 58
 }
59 59
 
60
+static int test_dhcp_http ( struct net_device *netdev, char *url ) {
61
+	union {
62
+		struct sockaddr_in sin;
63
+		struct sockaddr_tcpip st;
64
+	} target;
65
+
66
+	memset ( &target, 0, sizeof ( target ) );
67
+	target.sin.sin_family = AF_INET;
68
+	target.sin.sin_port = htons ( 80 );
69
+
70
+	char *addr = url + 7; // http://
71
+        char *file = strchr(addr, '/');
72
+	*file = '\0'; // for printf and inet_aton to work
73
+	printf("connecting to %s\n", addr);
74
+	inet_aton ( addr, &target.sin.sin_addr );
75
+	*file = '/';
76
+	test_http ( netdev, &target.st, file );
77
+	return 0;
78
+}
79
+
60 80
 static int test_dhcp_tftp ( struct net_device *netdev, char *tftpname ) {
61 81
 	union {
62 82
 		struct sockaddr_in sin;
@@ -79,6 +99,8 @@ static int test_dhcp_boot ( struct net_device *netdev, char *filename ) {
79 99
 		return test_dhcp_iscsi_boot ( &filename[6] );
80 100
 	} else if ( strncmp ( filename, "hello:", 6 ) == 0 ) {
81 101
 		return test_dhcp_hello ( &filename[6] );
102
+	} else if ( strncmp ( filename, "http:", 5 ) == 0 ) {
103
+		return test_dhcp_http ( netdev, filename );
82 104
 	} else {
83 105
 		return test_dhcp_tftp ( netdev, filename );
84 106
 	}

+ 37
- 0
src/tests/httptest.c View File

@@ -0,0 +1,37 @@
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.tcp.peer, server, sizeof ( http.tcp.peer ) );
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
+}

Loading…
Cancel
Save