Merge changes from mcb-tcp-fixes branch.
[people/sha0/gpxe.git] / src / net / tcp / http.c
1 #include <stddef.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <vsprintf.h>
5 #include <assert.h>
6 #include <gpxe/async.h>
7 #include <gpxe/http.h>
8
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:
21  *
22  * @code
23  *
24  *   static void my_callback ( struct http_request *http, char *data, size_t len ) {
25  *     ... process data ...
26  *   }
27  *
28  *   struct http_request http = {
29  *     .filename = "path/to/file",
30  *     .callback = my_callback,
31  *   };
32  *
33  *   ... assign http.tcp.server ...
34  *
35  *   rc = async_wait ( get_http ( &http ) );
36  *
37  * @endcode
38  *
39  */
40
41 static inline struct http_request *
42 tcp_to_http ( struct tcp_application *app ) {
43         return container_of ( app, struct http_request, tcp );
44 }
45
46 /**
47  * Close an HTTP connection
48  *
49  * @v app       a TCP Application
50  * @v status    connection status at close
51  */
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 );
55 }
56
57 /**
58  * Callback after a TCP connection is established
59  *
60  * @v app       a TCP Application
61  */
62 static void http_connected ( struct tcp_application *app ) {
63         struct http_request *http = tcp_to_http ( app );
64
65         http->state = HTTP_REQUEST_FILE;
66 }
67
68 /**
69  * Callback for when TCP data is acknowledged
70  *
71  * @v app       a TCP Application
72  * @v len       the length of data acked
73  */
74 static void http_acked ( struct tcp_application *app, size_t len __attribute__ ((unused)) ) {
75         struct http_request *http = tcp_to_http ( app );
76
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");
93 }
94
95 /**
96  * Callback when new TCP data is recieved
97  *
98  * @v app       a TCP Application
99  * @v data      a pointer to the data recieved
100  * @v len       length of data buffer
101  */
102 static void http_newdata ( struct tcp_application *app, void *data,
103                             size_t len ) {
104         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
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         }
153 }
154
155 /**
156  * Callback for sending TCP data
157  *
158  * @v app       a TCP Application
159  */
160 static void http_senddata ( struct tcp_application *app, void *buf, size_t len ) {
161         struct http_request *http = tcp_to_http ( app );
162
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         }
180 }
181
182 static struct tcp_operations http_tcp_operations = {
183         .closed         = http_closed,
184         .connected      = http_connected,
185         .acked          = http_acked,
186         .newdata        = http_newdata,
187         .senddata       = http_senddata,
188 };
189
190 /**
191  * Initiate a HTTP connection
192  *
193  * @v http      a HTTP request
194  */
195 struct async_operation * get_http ( struct http_request *http ) {
196         int rc;
197
198         http->tcp.tcp_op = &http_tcp_operations;
199         http->state = HTTP_REQUEST_FILE;
200         if ( ( rc = tcp_connect ( &http->tcp, &http->server, 0 ) ) != 0 )
201                 async_done ( &http->aop, rc );
202
203         return &http->aop;
204 }