• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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