1 /*
2 Copyright Copyright (C) 2013 Andrey Uzunov
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU 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, see <http://www.gnu.org/licenses/>.
16 */
17
18 /**
19 * @file mhd2spdy_http.c
20 * @brief HTTP part of the proxy. libmicrohttpd is used for the server side.
21 * @author Andrey Uzunov
22 */
23
24 #include "mhd2spdy_structures.h"
25 #include "mhd2spdy_http.h"
26 #include "mhd2spdy_spdy.h"
27
28
29 void *
http_cb_log(void * cls,const char * uri)30 http_cb_log(void * cls,
31 const char * uri)
32 {
33 (void)cls;
34
35 struct HTTP_URI * http_uri;
36
37 PRINT_INFO2("log uri '%s'\n", uri);
38
39 //TODO not freed once in a while
40 if(NULL == (http_uri = au_malloc(sizeof(struct HTTP_URI ))))
41 return NULL;
42 http_uri->uri = strdup(uri);
43 return http_uri;
44 }
45
46
47 static int
http_cb_iterate(void * cls,enum MHD_ValueKind kind,const char * name,const char * value)48 http_cb_iterate(void *cls,
49 enum MHD_ValueKind kind,
50 const char *name,
51 const char *value)
52 {
53 (void)kind;
54
55 static char * const forbidden[] = {"Transfer-Encoding",
56 "Proxy-Connection",
57 "Keep-Alive",
58 "Connection"};
59 static int forbidden_size = 4;
60 int i;
61 struct SPDY_Headers *spdy_headers = (struct SPDY_Headers *)cls;
62
63 if(0 == strcasecmp(name, "Host"))
64 spdy_headers->nv[9] = (char *)value;
65 else
66 {
67 for(i=0; i<forbidden_size; ++i)
68 if(0 == strcasecmp(forbidden[i], name))
69 return MHD_YES;
70 spdy_headers->nv[spdy_headers->cnt++] = (char *)name;
71 spdy_headers->nv[spdy_headers->cnt++] = (char *)value;
72 }
73
74 return MHD_YES;
75 }
76
77
78 static ssize_t
http_cb_response(void * cls,uint64_t pos,char * buffer,size_t max)79 http_cb_response (void *cls,
80 uint64_t pos,
81 char *buffer,
82 size_t max)
83 {
84 (void)pos;
85
86 int ret;
87 struct Proxy *proxy = (struct Proxy *)cls;
88 void *newbody;
89 const union MHD_ConnectionInfo *info;
90 int val = 1;
91
92 PRINT_INFO2("http_cb_response for %s", proxy->url);
93
94 if(proxy->spdy_error)
95 return MHD_CONTENT_READER_END_WITH_ERROR;
96
97 if(0 == proxy->http_body_size && (proxy->done || !proxy->spdy_active))
98 {
99 PRINT_INFO("sent end of stream");
100 return MHD_CONTENT_READER_END_OF_STREAM;
101 }
102
103 if(!proxy->http_body_size)//nothing to write now
104 {
105 //flush data
106 info = MHD_get_connection_info (proxy->http_connection,
107 MHD_CONNECTION_INFO_CONNECTION_FD);
108 ret = setsockopt(info->connect_fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val));
109 if(ret == -1) {
110 DIE("setsockopt");
111 }
112
113 PRINT_INFO("FLUSH data");
114 return 0;
115 }
116
117 if(max >= proxy->http_body_size)
118 {
119 ret = proxy->http_body_size;
120 newbody = NULL;
121 }
122 else
123 {
124 ret = max;
125 if(NULL == (newbody = au_malloc(proxy->http_body_size - max)))
126 {
127 PRINT_INFO("no memory");
128 return MHD_CONTENT_READER_END_WITH_ERROR;
129 }
130 memcpy(newbody, proxy->http_body + max, proxy->http_body_size - max);
131 }
132 memcpy(buffer, proxy->http_body, ret);
133 free(proxy->http_body);
134 proxy->http_body = newbody;
135 proxy->http_body_size -= ret;
136
137 if(proxy->length >= 0)
138 {
139 proxy->length -= ret;
140 }
141
142 PRINT_INFO2("response_callback, size: %i",ret);
143
144 return ret;
145 }
146
147
148 static void
http_cb_response_done(void * cls)149 http_cb_response_done(void *cls)
150 {
151 (void)cls;
152 //TODO remove
153 }
154
155 int
http_cb_request(void * cls,struct MHD_Connection * connection,const char * url,const char * method,const char * version,const char * upload_data,size_t * upload_data_size,void ** ptr)156 http_cb_request (void *cls,
157 struct MHD_Connection *connection,
158 const char *url,
159 const char *method,
160 const char *version,
161 const char *upload_data,
162 size_t *upload_data_size,
163 void **ptr)
164 {
165 (void)cls;
166 (void)url;
167 (void)upload_data;
168 (void)upload_data_size;
169
170 int ret;
171 struct Proxy *proxy;
172 struct SPDY_Headers spdy_headers;
173 bool with_body = false;
174 struct HTTP_URI *http_uri;
175 const char *header_value;
176
177 if (NULL == ptr || NULL == *ptr)
178 return MHD_NO;
179
180 http_uri = (struct HTTP_URI *)*ptr;
181
182 if(NULL == http_uri->proxy)
183 {
184 //first call for this request
185 if (0 != strcmp (method, MHD_HTTP_METHOD_GET) && 0 != strcmp (method, MHD_HTTP_METHOD_POST))
186 {
187 free(http_uri->uri);
188 free(http_uri);
189 PRINT_INFO2("unexpected method %s", method);
190 return MHD_NO;
191 }
192
193 if(NULL == (proxy = au_malloc(sizeof(struct Proxy))))
194 {
195 free(http_uri->uri);
196 free(http_uri);
197 PRINT_INFO("No memory");
198 return MHD_NO;
199 }
200
201 ++glob_opt.responses_pending;
202 proxy->id = rand();
203 proxy->http_active = true;
204 proxy->http_connection = connection;
205 http_uri->proxy = proxy;
206 return MHD_YES;
207 }
208
209 proxy = http_uri->proxy;
210
211 if(proxy->spdy_error || proxy->http_error)
212 return MHD_NO; // handled at different place TODO? leaks?
213
214 if(proxy->spdy_active)
215 {
216 if(0 == strcmp (method, MHD_HTTP_METHOD_POST))
217 {
218 PRINT_INFO("POST processing");
219
220 int rc= spdylay_session_resume_data(proxy->spdy_connection->session, proxy->stream_id);
221 PRINT_INFO2("rc is %i stream is %i", rc, proxy->stream_id);
222 proxy->spdy_connection->want_io |= WANT_WRITE;
223
224 if(0 == *upload_data_size)
225 {
226 PRINT_INFO("POST http EOF");
227 proxy->receiving_done = true;
228 return MHD_YES;
229 }
230
231 if(!copy_buffer(upload_data, *upload_data_size, &proxy->received_body, &proxy->received_body_size))
232 {
233 //TODO handle it better?
234 PRINT_INFO("not enough memory (malloc/realloc returned NULL)");
235 return MHD_NO;
236 }
237
238 *upload_data_size = 0;
239
240 return MHD_YES;
241 }
242
243 //already handled
244 PRINT_INFO("unnecessary call to http_cb_request");
245 return MHD_YES;
246 }
247
248 //second call for this request
249
250 PRINT_INFO2("received request for '%s %s %s'", method, http_uri->uri, version);
251
252 proxy->url = http_uri->uri;
253
254 header_value = MHD_lookup_connection_value(connection,
255 MHD_HEADER_KIND, MHD_HTTP_HEADER_CONTENT_LENGTH);
256
257 with_body = 0 == strcmp (method, MHD_HTTP_METHOD_POST)
258 && (NULL == header_value || 0 != strcmp ("0", header_value));
259
260 PRINT_INFO2("body will be sent %i", with_body);
261
262 ret = parse_uri(&glob_opt.uri_preg, proxy->url, &proxy->uri);
263 if(ret != 0)
264 DIE("parse_uri failed");
265 proxy->http_uri = http_uri;
266
267 spdy_headers.num = MHD_get_connection_values (connection,
268 MHD_HEADER_KIND,
269 NULL,
270 NULL);
271 if(NULL == (spdy_headers.nv = au_malloc(((spdy_headers.num + 5) * 2 + 1) * sizeof(char *))))
272 DIE("no memory");
273 spdy_headers.nv[0] = ":method"; spdy_headers.nv[1] = method;
274 spdy_headers.nv[2] = ":path"; spdy_headers.nv[3] = proxy->uri->path_and_more;
275 spdy_headers.nv[4] = ":version"; spdy_headers.nv[5] = (char *)version;
276 spdy_headers.nv[6] = ":scheme"; spdy_headers.nv[7] = proxy->uri->scheme;
277 spdy_headers.nv[8] = ":host"; spdy_headers.nv[9] = NULL;
278 //nv[14] = NULL;
279 spdy_headers.cnt = 10;
280 MHD_get_connection_values (connection,
281 MHD_HEADER_KIND,
282 &http_cb_iterate,
283 &spdy_headers);
284
285 spdy_headers.nv[spdy_headers.cnt] = NULL;
286 if(NULL == spdy_headers.nv[9])
287 spdy_headers.nv[9] = proxy->uri->host_and_port;
288
289 if(0 != spdy_request(spdy_headers.nv, proxy, with_body))
290 {
291 free(spdy_headers.nv);
292 //free_proxy(proxy);
293
294 return MHD_NO;
295 }
296 free(spdy_headers.nv);
297
298 proxy->http_response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
299 4096,
300 &http_cb_response,
301 proxy,
302 &http_cb_response_done);
303
304 if (NULL == proxy->http_response)
305 DIE("no response");
306
307 if(MHD_NO == MHD_add_response_header (proxy->http_response,
308 "Proxy-Connection", "keep-alive"))
309 PRINT_INFO("SPDY_name_value_add failed: ");
310 if(MHD_NO == MHD_add_response_header (proxy->http_response,
311 "Connection", "Keep-Alive"))
312 PRINT_INFO("SPDY_name_value_add failed: ");
313 if(MHD_NO == MHD_add_response_header (proxy->http_response,
314 "Keep-Alive", "timeout=5, max=100"))
315 PRINT_INFO("SPDY_name_value_add failed: ");
316
317 proxy->spdy_active = true;
318
319 return MHD_YES;
320 }
321
322
323 void
http_create_response(struct Proxy * proxy,char ** nv)324 http_create_response(struct Proxy* proxy,
325 char **nv)
326 {
327 size_t i;
328
329 if(!proxy->http_active)
330 return;
331
332 for(i = 0; nv[i]; i += 2) {
333 if(0 == strcmp(":status", nv[i]))
334 {
335 char tmp[4];
336 memcpy(&tmp,nv[i+1],3);
337 tmp[3]=0;
338 proxy->status = atoi(tmp);
339 continue;
340 }
341 else if(0 == strcmp(":version", nv[i]))
342 {
343 proxy->version = nv[i+1];
344 continue;
345 }
346 else if(0 == strcmp("content-length", nv[i]))
347 {
348 continue;
349 }
350
351 char *header = *(nv+i);
352 if(MHD_NO == MHD_add_response_header (proxy->http_response,
353 header, nv[i+1]))
354 {
355 PRINT_INFO2("SPDY_name_value_add failed: '%s' '%s'", header, nv[i+1]);
356 }
357 PRINT_INFO2("adding '%s: %s'",header, nv[i+1]);
358 }
359
360 if(MHD_NO == MHD_queue_response (proxy->http_connection, proxy->status, proxy->http_response)){
361 PRINT_INFO("No queue");
362 //TODO
363 //abort();
364 proxy->http_error = true;
365 }
366
367 MHD_destroy_response (proxy->http_response);
368 proxy->http_response = NULL;
369 }
370
371 void
http_cb_request_completed(void * cls,struct MHD_Connection * connection,void ** con_cls,enum MHD_RequestTerminationCode toe)372 http_cb_request_completed (void *cls,
373 struct MHD_Connection *connection,
374 void **con_cls,
375 enum MHD_RequestTerminationCode toe)
376 {
377 (void)cls;
378 (void)connection;
379 struct HTTP_URI *http_uri;
380 struct Proxy *proxy;
381
382 http_uri = (struct HTTP_URI *)*con_cls;
383 if(NULL == http_uri)
384 return;
385 proxy = (struct Proxy *)http_uri->proxy;
386 assert(NULL != proxy);
387
388 PRINT_INFO2("http_cb_request_completed %i for %s; id %i",toe, http_uri->uri, proxy->id);
389
390 if(NULL != proxy->http_response)
391 {
392 MHD_destroy_response (proxy->http_response);
393 proxy->http_response = NULL;
394 }
395
396 if(proxy->spdy_active)
397 {
398 proxy->http_active = false;
399 if(MHD_REQUEST_TERMINATED_COMPLETED_OK != toe)
400 {
401 proxy->http_error = true;
402 if(proxy->stream_id > 0 /*&& NULL != proxy->spdy_connection->session*/)
403 {
404 //send RST_STREAM_STATUS_CANCEL
405 PRINT_INFO2("send rst_stream %i %i",proxy->spdy_active, proxy->stream_id );
406 spdylay_submit_rst_stream(proxy->spdy_connection->session, proxy->stream_id, 5);
407 }
408 /*else
409 {
410 DLL_remove(proxy->spdy_connection->proxies_head, proxy->spdy_connection->proxies_tail, proxy);
411 free_proxy(proxy);
412 }*/
413 }
414 }
415 else
416 {
417 PRINT_INFO2("proxy free http id %i ", proxy->id);
418 free_proxy(proxy);
419 }
420
421 --glob_opt.responses_pending;
422 }
423