New HTTP protocol and test code
[people/sha0/gpxe.git] / src / net / tcp / http.c
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 }