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
29void *
30http_cb_log(void * cls,
31const 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
47static int
48http_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
78static ssize_t
79http_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
148static void
149http_cb_response_done(void *cls)
150{
151  (void)cls;
152  //TODO remove
153}
154
155int
156http_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
323void
324http_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
371void
372http_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