1/*
2    This file is part of libmicrospdy
3    Copyright Copyright (C) 2013 Andrey Uzunov
4
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation, either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file proxy.c
21 * @brief   Translates incoming SPDY requests to http server on localhost.
22 * 			Uses libcurl.
23 * 			No error handling for curl requests.
24 *      TODO:
25 * - test all options!
26 * - don't abort on lack of memory
27 * - Correct recapitalizetion of header names before giving the headers
28 * to curl.
29 * - curl does not close sockets when connection is closed and no
30 * new sockets are opened (they stay in CLOSE_WAIT)
31 * - add '/' when a user requests http://example.com . Now this is a bad
32 * request
33 * - curl returns 0 or 1 ms for timeout even when nothing will be done;
34 * thus the loop uses CPU for nothing
35 * @author Andrey Uzunov
36 */
37
38#include "platform.h"
39#include <unistd.h>
40#include <stdlib.h>
41#include <stdint.h>
42#include <stdbool.h>
43#include <string.h>
44#include <stdio.h>
45#include <ctype.h>
46#include <errno.h>
47#include "microspdy.h"
48#include <curl/curl.h>
49#include <assert.h>
50#include <getopt.h>
51#include <regex.h>
52
53#define ERROR_RESPONSE "502 Bad Gateway"
54
55
56struct global_options
57{
58  char *http_backend;
59  char *cert;
60  char *cert_key;
61  char *listen_host;
62  unsigned int timeout;
63  uint16_t listen_port;
64  bool verbose;
65  bool curl_verbose;
66  bool transparent;
67  bool http10;
68  bool notls;
69  bool nodelay;
70  bool ipv4;
71  bool ipv6;
72} glob_opt;
73
74
75struct URI
76{
77  char * full_uri;
78  char * scheme;
79  char * host_and_port;
80  //char * host_and_port_for_connecting;
81  char * host;
82  char * path;
83  char * path_and_more;
84  char * query;
85  char * fragment;
86  uint16_t port;
87};
88
89
90#define PRINT_INFO(msg) do{\
91	fprintf(stdout, "%i:%s\n", __LINE__, msg);\
92	fflush(stdout);\
93	}\
94	while(0)
95
96
97#define PRINT_INFO2(fmt, ...) do{\
98	fprintf(stdout, "%i\n", __LINE__);\
99	fprintf(stdout, fmt,##__VA_ARGS__);\
100	fprintf(stdout, "\n");\
101	fflush(stdout);\
102	}\
103	while(0)
104
105
106#define PRINT_VERBOSE(msg) do{\
107  if(glob_opt.verbose){\
108	fprintf(stdout, "%i:%s\n", __LINE__, msg);\
109	fflush(stdout);\
110	}\
111  }\
112	while(0)
113
114
115#define PRINT_VERBOSE2(fmt, ...) do{\
116  if(glob_opt.verbose){\
117	fprintf(stdout, "%i\n", __LINE__);\
118	fprintf(stdout, fmt,##__VA_ARGS__);\
119	fprintf(stdout, "\n");\
120	fflush(stdout);\
121	}\
122	}\
123	while(0)
124
125
126#define CURL_SETOPT(handle, opt, val) do{\
127	int ret; \
128	if(CURLE_OK != (ret = curl_easy_setopt(handle, opt, val))) \
129	{ \
130		PRINT_INFO2("curl_easy_setopt failed (%i = %i)", opt, ret); \
131		abort(); \
132	} \
133	}\
134	while(0)
135
136
137#define DIE(msg) do{\
138	printf("FATAL ERROR (line %i): %s\n", __LINE__, msg);\
139	fflush(stdout);\
140  exit(EXIT_FAILURE);\
141	}\
142	while(0)
143
144
145static int loop = 1;
146
147static CURLM *multi_handle;
148
149static int still_running = 0; /* keep number of running handles */
150
151static regex_t uri_preg;
152
153static bool call_spdy_run;
154static bool call_curl_run;
155
156int debug_num_curls;
157
158
159struct Proxy
160{
161	char *url;
162	struct SPDY_Request *request;
163	struct SPDY_Response *response;
164	CURL *curl_handle;
165	struct curl_slist *curl_headers;
166	struct SPDY_NameValue *headers;
167	char *version;
168	char *status_msg;
169	void *http_body;
170	void *received_body;
171  bool *session_alive;
172	size_t http_body_size;
173	size_t received_body_size;
174	//ssize_t length;
175	int status;
176  //bool done;
177  bool receiving_done;
178  bool is_curl_read_paused;
179  bool is_with_body_data;
180  //bool error;
181  bool curl_done;
182  bool curl_error;
183  bool spdy_done;
184  bool spdy_error;
185};
186
187
188static void
189free_uri(struct URI * uri)
190{
191  if(NULL != uri)
192  {
193    free(uri->full_uri);
194    free(uri->scheme);
195    free(uri->host_and_port);
196    //free(uri->host_and_port_for_connecting);
197    free(uri->host);
198    free(uri->path);
199    free(uri->path_and_more);
200    free(uri->query);
201    free(uri->fragment);
202    uri->port = 0;
203    free(uri);
204  }
205}
206
207
208static int
209init_parse_uri(regex_t * preg)
210{
211  // RFC 2396
212  // ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
213      /*
214        scheme    = $2
215      authority = $4
216      path      = $5
217      query     = $7
218      fragment  = $9
219      */
220
221  return regcomp(preg, "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?", REG_EXTENDED);
222}
223
224
225static void
226deinit_parse_uri(regex_t * preg)
227{
228  regfree(preg);
229}
230
231
232static int
233parse_uri(regex_t * preg, const char * full_uri, struct URI ** uri)
234{
235  //TODO memeory checks
236  int ret;
237  char *colon;
238  long long port;
239  size_t nmatch = 10;
240  regmatch_t pmatch[10];
241
242  if (0 != (ret = regexec(preg, full_uri, nmatch, pmatch, 0)))
243    return ret;
244
245  *uri = malloc(sizeof(struct URI));
246  if(NULL == *uri)
247    return -200;
248
249  (*uri)->full_uri = strdup(full_uri);
250
251  asprintf(&((*uri)->scheme),
252	   "%.*s",
253	   (int) (pmatch[2].rm_eo - pmatch[2].rm_so),
254	   &full_uri[pmatch[2].rm_so]);
255  asprintf(&((*uri)->host_and_port), "%.*s",
256	   (int) (pmatch[4].rm_eo - pmatch[4].rm_so),
257	   &full_uri[pmatch[4].rm_so]);
258  asprintf(&((*uri)->path),
259	   "%.*s",
260	   (int) (pmatch[5].rm_eo - pmatch[5].rm_so),
261	   &full_uri[pmatch[5].rm_so]);
262  asprintf(&((*uri)->path_and_more),
263	   "%.*s",
264	   (int) (pmatch[9].rm_eo - pmatch[5].rm_so),
265	   &full_uri[pmatch[5].rm_so]);
266  asprintf(&((*uri)->query),
267	   "%.*s",
268	   (int) (pmatch[7].rm_eo - pmatch[7].rm_so),
269	   &full_uri[pmatch[7].rm_so]);
270  asprintf(&((*uri)->fragment),
271	   "%.*s",
272	   (int) (pmatch[9].rm_eo - pmatch[9].rm_so),
273	   &full_uri[pmatch[9].rm_so]);
274
275  colon = strrchr((*uri)->host_and_port, ':');
276  if(NULL == colon)
277  {
278    (*uri)->host = strdup((*uri)->host_and_port);
279    /*if(0 == strcasecmp("http", uri->scheme))
280    {
281      uri->port = 80;
282      asprintf(&(uri->host_and_port_for_connecting), "%s:80", uri->host_and_port);
283    }
284    else if(0 == strcasecmp("https", uri->scheme))
285    {
286      uri->port = 443;
287      asprintf(&(uri->host_and_port_for_connecting), "%s:443", uri->host_and_port);
288    }
289    else
290    {
291      PRINT_INFO("no standard scheme!");
292      */(*uri)->port = 0;
293      /*uri->host_and_port_for_connecting = strdup(uri->host_and_port);
294    }*/
295    return 0;
296  }
297
298  port = atoi(colon  + 1);
299  if(port<1 || port >= 256 * 256)
300  {
301    free_uri(*uri);
302    return -100;
303  }
304  (*uri)->port = port;
305  asprintf(&((*uri)->host), "%.*s", (int)(colon - (*uri)->host_and_port), (*uri)->host_and_port);
306
307  return 0;
308}
309
310
311static bool
312store_in_buffer(const void *src, size_t src_size, void **dst, size_t *dst_size)
313{
314  if(0 == src_size)
315    return true;
316
317  if(NULL == *dst)
318		*dst = malloc(src_size);
319	else
320		*dst = realloc(*dst, src_size + *dst_size);
321	if(NULL == *dst)
322		return false;
323
324	memcpy(*dst + *dst_size, src, src_size);
325	*dst_size += src_size;
326
327  return true;
328}
329
330
331static ssize_t
332get_from_buffer(void **src, size_t *src_size, void *dst, size_t max_size)
333{
334  size_t ret;
335  void *newbody;
336
337	if(max_size >= *src_size)
338	{
339		ret = *src_size;
340		newbody = NULL;
341	}
342	else
343	{
344		ret = max_size;
345		if(NULL == (newbody = malloc(*src_size - max_size)))
346			return -1;
347		memcpy(newbody, *src + ret, *src_size - ret);
348	}
349	memcpy(dst, *src, ret);
350	free(*src);
351	*src = newbody;
352	*src_size -= ret;
353
354  return ret;
355}
356
357
358static void
359catch_signal(int signal)
360{
361  (void)signal;
362
363  loop = 0;
364}
365
366static void
367new_session_cb (void * cls,
368							struct SPDY_Session * session)
369{
370  (void)cls;
371
372  bool *session_alive;
373
374  PRINT_VERBOSE("new session");
375  //TODO clean this memory
376  if(NULL == (session_alive = malloc(sizeof(bool))))
377  {
378			DIE("no memory");
379  }
380  *session_alive = true;
381  SPDY_set_cls_to_session(session,
382						session_alive);
383}
384
385static void
386session_closed_cb (void * cls,
387								struct SPDY_Session * session,
388								int by_client)
389{
390  (void)cls;
391
392  bool *session_alive;
393
394  PRINT_VERBOSE2("session closed; by client: %i", by_client);
395
396  session_alive = SPDY_get_cls_from_session(session);
397  assert(NULL != session_alive);
398
399  *session_alive = false;
400}
401
402
403static int
404spdy_post_data_cb (void * cls,
405					 struct SPDY_Request *request,
406					 const void * buf,
407					 size_t size,
408					 bool more)
409{
410  (void)cls;
411  int ret;
412	struct Proxy *proxy = (struct Proxy *)SPDY_get_cls_from_request(request);
413
414  if(!store_in_buffer(buf, size, &proxy->received_body, &proxy->received_body_size))
415	{
416		PRINT_INFO("not enough memory (malloc/realloc returned NULL)");
417		return 0;
418	}
419
420  proxy->receiving_done = !more;
421
422  PRINT_VERBOSE2("POST bytes from SPDY: %zu", size);
423
424  call_curl_run = true;
425
426  if(proxy->is_curl_read_paused)
427  {
428    if(CURLE_OK != (ret = curl_easy_pause(proxy->curl_handle, CURLPAUSE_CONT)))
429    {
430      PRINT_INFO2("curl_easy_pause returned %i", ret);
431      abort();
432    }
433    PRINT_VERBOSE("curl_read_cb pause resumed");
434  }
435
436  return SPDY_YES;
437}
438
439
440ssize_t
441response_callback (void *cls,
442						void *buffer,
443						size_t max,
444						bool *more)
445{
446	ssize_t ret;
447	struct Proxy *proxy = (struct Proxy *)cls;
448
449  *more = true;
450
451  assert(!proxy->spdy_error);
452
453  if(proxy->curl_error)
454  {
455    PRINT_VERBOSE("tell spdy about the error");
456    return -1;
457  }
458
459	if(!proxy->http_body_size)//nothing to write now
460  {
461    PRINT_VERBOSE("nothing to write now");
462    if(proxy->curl_done || proxy->curl_error) *more = false;
463		return 0;
464  }
465
466  ret = get_from_buffer(&(proxy->http_body), &(proxy->http_body_size), buffer, max);
467  if(ret < 0)
468  {
469    PRINT_INFO("no memory");
470    //TODO error?
471    return -1;
472  }
473
474  if((proxy->curl_done || proxy->curl_error) && 0 == proxy->http_body_size) *more = false;
475
476  PRINT_VERBOSE2("given bytes to microspdy: %zd", ret);
477
478	return ret;
479}
480
481
482static void
483cleanup(struct Proxy *proxy)
484{
485  int ret;
486
487  //fprintf(stderr, "free proxy for %s\n", proxy->url);
488
489	if(CURLM_OK != (ret = curl_multi_remove_handle(multi_handle, proxy->curl_handle)))
490	{
491		PRINT_INFO2("curl_multi_remove_handle failed (%i)", ret);
492    DIE("bug in cleanup");
493	}
494  debug_num_curls--;
495  //TODO bug on ku6.com or amazon.cn
496  // after curl_multi_remove_handle returned CURLM_BAD_EASY_HANDLE
497	curl_slist_free_all(proxy->curl_headers);
498	curl_easy_cleanup(proxy->curl_handle);
499
500	free(proxy->url);
501	free(proxy);
502}
503
504
505static void
506response_done_callback(void *cls,
507						struct SPDY_Response *response,
508						struct SPDY_Request *request,
509						enum SPDY_RESPONSE_RESULT status,
510						bool streamopened)
511{
512	(void)streamopened;
513	struct Proxy *proxy = (struct Proxy *)cls;
514
515	if(SPDY_RESPONSE_RESULT_SUCCESS != status)
516	{
517    free(proxy->http_body);
518    proxy->http_body = NULL;
519    proxy->spdy_error = true;
520	}
521	cleanup(proxy);
522	SPDY_destroy_request(request);
523	SPDY_destroy_response(response);
524}
525
526
527static size_t
528curl_header_cb(void *ptr, size_t size, size_t nmemb, void *userp)
529{
530	size_t realsize = size * nmemb;
531	struct Proxy *proxy = (struct Proxy *)userp;
532	char *line = (char *)ptr;
533	char *name;
534	char *value;
535	char *status;
536	unsigned int i;
537	unsigned int pos;
538	int ret;
539  int num_values;
540  const char * const * values;
541  bool abort_it;
542
543	//printf("curl_header_cb %s\n", line);
544  if(!*(proxy->session_alive))
545  {
546    PRINT_VERBOSE("headers received, but session is dead");
547    proxy->spdy_error = true;
548    proxy->curl_error = true;
549    return 0;
550  }
551
552  //trailer
553  if(NULL != proxy->response) return 0;
554
555	if('\r' == line[0] || '\n' == line[0])
556	{
557		//all headers were already handled; prepare spdy frames
558		if(NULL == (proxy->response = SPDY_build_response_with_callback(proxy->status,
559							proxy->status_msg,
560							proxy->version,
561							proxy->headers,
562							&response_callback,
563							proxy,
564							0)))
565							//256)))
566			DIE("no response");
567
568		SPDY_name_value_destroy(proxy->headers);
569    proxy->headers = NULL;
570		free(proxy->status_msg);
571    proxy->status_msg = NULL;
572		free(proxy->version);
573    proxy->version = NULL;
574
575		if(SPDY_YES != SPDY_queue_response(proxy->request,
576							proxy->response,
577							true,
578							false,
579							&response_done_callback,
580							proxy))
581    {
582			//DIE("no queue");
583      //TODO right?
584      proxy->spdy_error = true;
585      proxy->curl_error = true;
586      PRINT_VERBOSE2("no queue in curl_header_cb for %s", proxy->url);
587      SPDY_destroy_response(proxy->response);
588      proxy->response = NULL;
589      return 0;
590    }
591
592    call_spdy_run = true;
593
594		return realsize;
595	}
596
597	pos = 0;
598	if(NULL == proxy->version)
599	{
600		//first line from headers
601		//version
602		for(i=pos; i<realsize && ' '!=line[i]; ++i);
603		if(i == realsize)
604			DIE("error on parsing headers");
605		if(NULL == (proxy->version = strndup(line, i - pos)))
606        DIE("No memory");
607		pos = i+1;
608
609		//status (number)
610		for(i=pos; i<realsize && ' '!=line[i] && '\r'!=line[i] && '\n'!=line[i]; ++i);
611		if(NULL == (status = strndup(&(line[pos]), i - pos)))
612        DIE("No memory");
613		proxy->status = atoi(status);
614		free(status);
615		if(i<realsize && '\r'!=line[i] && '\n'!=line[i])
616		{
617			//status (message)
618			pos = i+1;
619			for(i=pos; i<realsize && '\r'!=line[i] && '\n'!=line[i]; ++i);
620			if(NULL == (proxy->status_msg = strndup(&(line[pos]), i - pos)))
621        DIE("No memory");
622		}
623    PRINT_VERBOSE2("Header line received '%s' '%i' '%s' ", proxy->version, proxy->status, proxy->status_msg);
624		return realsize;
625	}
626
627	//other lines
628	//header name
629	for(i=pos; i<realsize && ':'!=line[i] && '\r'!=line[i] && '\n'!=line[i]; ++i)
630		line[i] = tolower(line[i]); //spdy requires lower case
631	if(NULL == (name = strndup(line, i - pos)))
632        DIE("No memory");
633	if(0 == strcmp(SPDY_HTTP_HEADER_CONNECTION, name)
634		|| 0 == strcmp(SPDY_HTTP_HEADER_KEEP_ALIVE, name)
635		|| 0 == strcmp(SPDY_HTTP_HEADER_TRANSFER_ENCODING, name)
636    )
637	{
638		//forbidden in spdy, ignore
639		free(name);
640		return realsize;
641	}
642	if(i == realsize || '\r'==line[i] || '\n'==line[i])
643	{
644		//no value. is it possible?
645		if(SPDY_YES != SPDY_name_value_add(proxy->headers, name, ""))
646			DIE("SPDY_name_value_add failed");
647		return realsize;
648	}
649
650	//header value
651	pos = i+1;
652	while(pos<realsize && isspace(line[pos])) ++pos; //remove leading space
653	for(i=pos; i<realsize && '\r'!=line[i] && '\n'!=line[i]; ++i);
654	if(NULL == (value = strndup(&(line[pos]), i - pos)))
655        DIE("No memory");
656  PRINT_VERBOSE2("Adding header: '%s': '%s'", name, value);
657	if(SPDY_YES != (ret = SPDY_name_value_add(proxy->headers, name, value)))
658	{
659    abort_it=true;
660    if(NULL != (values = SPDY_name_value_lookup(proxy->headers, name, &num_values)))
661      for(i=0; i<(unsigned int)num_values; ++i)
662        if(0 == strcasecmp(value, values[i]))
663        {
664          abort_it=false;
665          PRINT_VERBOSE2("header appears more than once with same value '%s: %s'", name, value);
666          break;
667        }
668
669    if(abort_it)
670    {
671      PRINT_INFO2("SPDY_name_value_add failed (%i) for '%s'", ret, name);
672      abort();
673    }
674	}
675	free(name);
676	free(value);
677
678	return realsize;
679}
680
681
682static size_t
683curl_write_cb(void *contents, size_t size, size_t nmemb, void *userp)
684{
685	size_t realsize = size * nmemb;
686	struct Proxy *proxy = (struct Proxy *)userp;
687
688	//printf("curl_write_cb %i\n", realsize);
689  if(!*(proxy->session_alive))
690  {
691    PRINT_VERBOSE("data received, but session is dead");
692      proxy->spdy_error = true;
693      proxy->curl_error = true;
694    return 0;
695  }
696
697  if(!store_in_buffer(contents, realsize, &proxy->http_body, &proxy->http_body_size))
698	{
699		PRINT_INFO("not enough memory (malloc/realloc returned NULL)");
700      proxy->curl_error = true;
701		return 0;
702	}
703  /*
704	if(NULL == proxy->http_body)
705		proxy->http_body = malloc(realsize);
706	else
707		proxy->http_body = realloc(proxy->http_body, proxy->http_body_size + realsize);
708	if(NULL == proxy->http_body)
709	{
710		PRINT_INFO("not enough memory (realloc returned NULL)");
711		return 0;
712	}
713
714	memcpy(proxy->http_body + proxy->http_body_size, contents, realsize);
715	proxy->http_body_size += realsize;
716  */
717
718  PRINT_VERBOSE2("received bytes from curl: %zu", realsize);
719
720  call_spdy_run = true;
721
722	return realsize;
723}
724
725
726static size_t
727curl_read_cb(void *ptr, size_t size, size_t nmemb, void *userp)
728{
729	ssize_t ret;
730	size_t max = size * nmemb;
731	struct Proxy *proxy = (struct Proxy *)userp;
732	//void *newbody;
733
734
735  if((proxy->receiving_done && !proxy->received_body_size) || !proxy->is_with_body_data || max < 1)
736  {
737    PRINT_VERBOSE("curl_read_cb last call");
738    return 0;
739  }
740
741  if(!*(proxy->session_alive))
742  {
743    PRINT_VERBOSE("POST is still being sent, but session is dead");
744    return CURL_READFUNC_ABORT;
745  }
746
747	if(!proxy->received_body_size)//nothing to write now
748  {
749    PRINT_VERBOSE("curl_read_cb called paused");
750    proxy->is_curl_read_paused = true;
751		return CURL_READFUNC_PAUSE;//TODO curl pause should be used
752  }
753
754  ret = get_from_buffer(&(proxy->received_body), &(proxy->received_body_size), ptr, max);
755  if(ret < 0)
756  {
757    PRINT_INFO("no memory");
758    return CURL_READFUNC_ABORT;
759  }
760
761	/*
762	if(max >= proxy->received_body_size)
763	{
764		ret = proxy->received_body_size;
765		newbody = NULL;
766	}
767	else
768	{
769		ret = max;
770		if(NULL == (newbody = malloc(proxy->received_body_size - max)))
771		{
772			PRINT_INFO("no memory");
773			return CURL_READFUNC_ABORT;
774		}
775		memcpy(newbody, proxy->received_body + max, proxy->received_body_size - max);
776	}
777	memcpy(ptr, proxy->received_body, ret);
778	free(proxy->received_body);
779	proxy->received_body = newbody;
780	proxy->received_body_size -= ret;
781  * */
782
783  PRINT_VERBOSE2("given POST bytes to curl: %zd", ret);
784
785	return ret;
786}
787
788
789static int
790iterate_cb (void *cls, const char *name, const char * const * value, int num_values)
791{
792	struct Proxy *proxy = (struct Proxy *)cls;
793  struct curl_slist **curl_headers = (&(proxy->curl_headers));
794  char *line;
795  int line_len = strlen(name) + 3; //+ ": \0"
796  int i;
797
798  for(i=0; i<num_values; ++i)
799  {
800		if(i) line_len += 2; //", "
801		line_len += strlen(value[i]);
802	}
803
804	if(NULL == (line = malloc(line_len)))//no recovery
805    DIE("No memory");
806	line[0] = 0;
807
808  strcat(line, name);
809  strcat(line, ": ");
810  //all spdy header names are lower case;
811  //for simplicity here we just capitalize the first letter
812  line[0] = toupper(line[0]);
813
814	for(i=0; i<num_values; ++i)
815	{
816		if(i) strcat(line, ", ");
817  PRINT_VERBOSE2("Adding request header: '%s' len %ld", value[i], strlen(value[i]));
818		strcat(line, value[i]);
819	}
820  PRINT_VERBOSE2("Adding request header: '%s'", line);
821  if(NULL == (*curl_headers = curl_slist_append(*curl_headers, line)))
822		DIE("curl_slist_append failed");
823	free(line);
824
825	return SPDY_YES;
826}
827
828
829static void
830standard_request_handler(void *cls,
831                        struct SPDY_Request * request,
832                        uint8_t priority,
833                        const char *method,
834                        const char *path,
835                        const char *version,
836                        const char *host,
837                        const char *scheme,
838                        struct SPDY_NameValue * headers,
839                        bool more)
840{
841	(void)cls;
842	(void)priority;
843	(void)host;
844	(void)scheme;
845
846	struct Proxy *proxy;
847	int ret;
848  struct URI *uri;
849  struct SPDY_Session *session;
850
851  proxy = SPDY_get_cls_from_request(request);
852  if(NULL != proxy)
853  {
854    //ignore trailers or more headers
855    return;
856  }
857
858	PRINT_VERBOSE2("received request for '%s %s %s'\n", method, path, version);
859
860  //TODO not freed once in a while
861	if(NULL == (proxy = malloc(sizeof(struct Proxy))))
862    DIE("No memory");
863	memset(proxy, 0, sizeof(struct Proxy));
864
865  //fprintf(stderr, "new  proxy for %s\n", path);
866
867  session = SPDY_get_session_for_request(request);
868  assert(NULL != session);
869  proxy->session_alive = SPDY_get_cls_from_session(session);
870  assert(NULL != proxy->session_alive);
871
872  SPDY_set_cls_to_request(request, proxy);
873
874	proxy->request = request;
875	proxy->is_with_body_data = more;
876	if(NULL == (proxy->headers = SPDY_name_value_create()))
877        DIE("No memory");
878
879  if(glob_opt.transparent)
880  {
881    if(NULL != glob_opt.http_backend) //use always same host
882      ret = asprintf(&(proxy->url),"%s://%s%s", scheme, glob_opt.http_backend, path);
883    else //use host header
884      ret = asprintf(&(proxy->url),"%s://%s%s", scheme, host, path);
885    if(-1 == ret)
886        DIE("No memory");
887
888    ret = parse_uri(&uri_preg, proxy->url, &uri);
889    if(ret != 0)
890      DIE("parsing built uri failed");
891  }
892  else
893  {
894    ret = parse_uri(&uri_preg, path, &uri);
895    PRINT_INFO2("path %s '%s' '%s'", path, uri->scheme, uri->host);
896    if(ret != 0 || !strlen(uri->scheme) || !strlen(uri->host))
897      DIE("parsing received uri failed");
898
899    if(NULL != glob_opt.http_backend) //use backend host
900    {
901      ret = asprintf(&(proxy->url),"%s://%s%s", uri->scheme, glob_opt.http_backend, uri->path_and_more);
902      if(-1 == ret)
903        DIE("No memory");
904    }
905    else //use request path
906      if(NULL == (proxy->url = strdup(path)))
907        DIE("No memory");
908  }
909
910  free_uri(uri);
911
912  PRINT_VERBOSE2("curl will request '%s'", proxy->url);
913
914  SPDY_name_value_iterate(headers, &iterate_cb, proxy);
915
916	if(NULL == (proxy->curl_handle = curl_easy_init()))
917    {
918		PRINT_INFO("curl_easy_init failed");
919		abort();
920	}
921
922	if(glob_opt.curl_verbose)
923    CURL_SETOPT(proxy->curl_handle, CURLOPT_VERBOSE, 1);
924
925  if(0 == strcmp(SPDY_HTTP_METHOD_POST,method))
926  {
927    if(NULL == (proxy->curl_headers = curl_slist_append(proxy->curl_headers, "Expect:")))
928      DIE("curl_slist_append failed");
929    CURL_SETOPT(proxy->curl_handle, CURLOPT_POST, 1);
930    CURL_SETOPT(proxy->curl_handle, CURLOPT_READFUNCTION, curl_read_cb);
931    CURL_SETOPT(proxy->curl_handle, CURLOPT_READDATA, proxy);
932  }
933
934  if(glob_opt.timeout)
935    CURL_SETOPT(proxy->curl_handle, CURLOPT_TIMEOUT, glob_opt.timeout);
936	CURL_SETOPT(proxy->curl_handle, CURLOPT_URL, proxy->url);
937	if(glob_opt.http10)
938		CURL_SETOPT(proxy->curl_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
939	CURL_SETOPT(proxy->curl_handle, CURLOPT_WRITEFUNCTION, curl_write_cb);
940	CURL_SETOPT(proxy->curl_handle, CURLOPT_WRITEDATA, proxy);
941	CURL_SETOPT(proxy->curl_handle, CURLOPT_HEADERFUNCTION, curl_header_cb);
942	CURL_SETOPT(proxy->curl_handle, CURLOPT_HEADERDATA, proxy);
943	CURL_SETOPT(proxy->curl_handle, CURLOPT_PRIVATE, proxy);
944	CURL_SETOPT(proxy->curl_handle, CURLOPT_HTTPHEADER, proxy->curl_headers);
945  CURL_SETOPT(proxy->curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);//TODO
946  CURL_SETOPT(proxy->curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);
947  if(glob_opt.ipv4 && !glob_opt.ipv6)
948    CURL_SETOPT(proxy->curl_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
949  else if(glob_opt.ipv6 && !glob_opt.ipv4)
950    CURL_SETOPT(proxy->curl_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6);
951
952	if(CURLM_OK != (ret = curl_multi_add_handle(multi_handle, proxy->curl_handle)))
953	{
954		PRINT_INFO2("curl_multi_add_handle failed (%i)", ret);
955		abort();
956	}
957  debug_num_curls++;
958
959  //~5ms additional latency for calling this
960	if(CURLM_OK != (ret = curl_multi_perform(multi_handle, &still_running))
961		&& CURLM_CALL_MULTI_PERFORM != ret)
962	{
963		PRINT_INFO2("curl_multi_perform failed (%i)", ret);
964		abort();
965	}
966
967  call_curl_run = true;
968}
969
970
971static int
972run ()
973{
974  unsigned long long timeoutlong = 0;
975  unsigned long long timeout_spdy = 0;
976  long timeout_curl = -1;
977	struct timeval timeout;
978	int ret;
979	int ret_curl;
980	int ret_spdy;
981	fd_set rs;
982	fd_set ws;
983	fd_set es;
984	int maxfd = -1;
985	int maxfd_curl = -1;
986	struct SPDY_Daemon *daemon;
987  CURLMsg *msg;
988  int msgs_left;
989  struct Proxy *proxy;
990  struct sockaddr_in *addr;
991  struct addrinfo hints;
992  char service[NI_MAXSERV];
993  struct addrinfo *gai;
994  enum SPDY_IO_SUBSYSTEM io = glob_opt.notls ? SPDY_IO_SUBSYSTEM_RAW : SPDY_IO_SUBSYSTEM_OPENSSL;
995  enum SPDY_DAEMON_FLAG flags = SPDY_DAEMON_FLAG_NO;
996  //struct SPDY_Response *error_response;
997  char *curl_private;
998
999	signal(SIGPIPE, SIG_IGN);
1000
1001  if (signal(SIGINT, catch_signal) == SIG_ERR)
1002    PRINT_VERBOSE("signal failed");
1003
1004  srand(time(NULL));
1005  if(init_parse_uri(&uri_preg))
1006    DIE("Regexp compilation failed");
1007
1008	SPDY_init();
1009
1010  if(glob_opt.nodelay)
1011    flags |= SPDY_DAEMON_FLAG_NO_DELAY;
1012
1013  if(NULL == glob_opt.listen_host)
1014	{
1015    daemon = SPDY_start_daemon(glob_opt.listen_port,
1016								glob_opt.cert,
1017								glob_opt.cert_key,
1018								&new_session_cb,
1019								&session_closed_cb,
1020								&standard_request_handler,
1021								&spdy_post_data_cb,
1022								NULL,
1023								SPDY_DAEMON_OPTION_SESSION_TIMEOUT,
1024								1800,
1025                SPDY_DAEMON_OPTION_IO_SUBSYSTEM,
1026                io,
1027                SPDY_DAEMON_OPTION_FLAGS,
1028                flags,
1029								SPDY_DAEMON_OPTION_END);
1030  }
1031  else
1032  {
1033    snprintf (service, sizeof(service), "%u", glob_opt.listen_port);
1034    memset (&hints, 0, sizeof(struct addrinfo));
1035    hints.ai_family = AF_INET;
1036    hints.ai_socktype = SOCK_STREAM;
1037
1038    ret = getaddrinfo(glob_opt.listen_host, service, &hints, &gai);
1039    if(ret != 0)
1040      DIE("problem with specified host");
1041
1042    addr = (struct sockaddr_in *) gai->ai_addr;
1043
1044    daemon = SPDY_start_daemon(0,
1045								glob_opt.cert,
1046								glob_opt.cert_key,
1047								&new_session_cb,
1048								&session_closed_cb,
1049								&standard_request_handler,
1050								&spdy_post_data_cb,
1051								NULL,
1052								SPDY_DAEMON_OPTION_SESSION_TIMEOUT,
1053								1800,
1054                SPDY_DAEMON_OPTION_IO_SUBSYSTEM,
1055                io,
1056                SPDY_DAEMON_OPTION_FLAGS,
1057                flags,
1058                SPDY_DAEMON_OPTION_SOCK_ADDR,
1059                addr,
1060                //SPDY_DAEMON_OPTION_MAX_NUM_FRAMES,
1061                //1,
1062								SPDY_DAEMON_OPTION_END);
1063  }
1064
1065	if(NULL==daemon){
1066		printf("no daemon\n");
1067		return 1;
1068	}
1069
1070	multi_handle = curl_multi_init();
1071	if(NULL==multi_handle)
1072		DIE("no multi_handle");
1073
1074	timeout.tv_usec = 0;
1075
1076	do
1077	{
1078		FD_ZERO(&rs);
1079		FD_ZERO(&ws);
1080		FD_ZERO(&es);
1081
1082    PRINT_VERBOSE2("num  curls %i", debug_num_curls);
1083
1084    ret_spdy = SPDY_get_timeout(daemon, &timeout_spdy);
1085    if(SPDY_NO == ret_spdy || timeout_spdy > 5000)
1086      timeoutlong = 5000;
1087    else
1088      timeoutlong = timeout_spdy;
1089    PRINT_VERBOSE2("SPDY timeout %lld; %i", timeout_spdy, ret_spdy);
1090
1091    if(CURLM_OK != (ret_curl = curl_multi_timeout(multi_handle, &timeout_curl)))
1092    {
1093      PRINT_VERBOSE2("curl_multi_timeout failed (%i)", ret_curl);
1094      //curl_timeo = timeoutlong;
1095    }
1096    else if(timeout_curl >= 0 && timeoutlong > (unsigned long)timeout_curl)
1097      timeoutlong = (unsigned long)timeout_curl;
1098
1099    PRINT_VERBOSE2("curl timeout %ld", timeout_curl);
1100
1101    timeout.tv_sec = timeoutlong / 1000;
1102		timeout.tv_usec = (timeoutlong % 1000) * 1000;
1103
1104		maxfd = SPDY_get_fdset (daemon,
1105								&rs,
1106								&ws,
1107								&es);
1108		assert(-1 != maxfd);
1109
1110		if(CURLM_OK != (ret = curl_multi_fdset(multi_handle, &rs,
1111								&ws,
1112								&es, &maxfd_curl)))
1113		{
1114			PRINT_INFO2("curl_multi_fdset failed (%i)", ret);
1115			abort();
1116		}
1117
1118    if(maxfd_curl > maxfd)
1119      maxfd = maxfd_curl;
1120
1121    PRINT_VERBOSE2("timeout before %lld %lld", (unsigned long long)timeout.tv_sec, (unsigned long long)timeout.tv_usec);
1122    ret = select(maxfd+1, &rs, &ws, &es, &timeout);
1123    PRINT_VERBOSE2("timeout after %lld %lld; ret is %i", (unsigned long long)timeout.tv_sec, (unsigned long long)timeout.tv_usec, ret);
1124
1125		/*switch(ret) {
1126			case -1:
1127				PRINT_INFO2("select error: %i", errno);
1128				break;
1129			case 0:
1130				break;
1131			default:*/
1132
1133      //the second part should not happen with current implementation
1134      if(ret > 0 || (SPDY_YES == ret_spdy && 0 == timeout_spdy))
1135      {
1136				PRINT_VERBOSE("run spdy");
1137				SPDY_run(daemon);
1138        call_spdy_run = false;
1139      }
1140
1141      //if(ret > 0 || (CURLM_OK == ret_curl && 0 == timeout_curl) || call_curl_run)
1142      {
1143				PRINT_VERBOSE("run curl");
1144				if(CURLM_OK != (ret = curl_multi_perform(multi_handle, &still_running))
1145					&& CURLM_CALL_MULTI_PERFORM != ret)
1146				{
1147					PRINT_INFO2("curl_multi_perform failed (%i)", ret);
1148					abort();
1149				}
1150        call_curl_run = false;
1151      }
1152			/*break;
1153		}*/
1154
1155    while ((msg = curl_multi_info_read(multi_handle, &msgs_left))) {
1156      if (msg->msg == CURLMSG_DONE) {
1157        PRINT_VERBOSE("A curl handler is done");
1158        if(CURLE_OK != (ret = curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &curl_private)))
1159        {
1160          PRINT_INFO2("err %i",ret);
1161          abort();
1162        }
1163        assert(NULL != curl_private);
1164        proxy = (struct Proxy *)curl_private;
1165        if(CURLE_OK == msg->data.result)
1166        {
1167          proxy->curl_done = true;
1168          call_spdy_run = true;
1169          //TODO what happens with proxy when the client resets a stream
1170          //and response_done is not yet set for the last frame? is it
1171          //possible?
1172        }
1173        else
1174        {
1175          PRINT_VERBOSE2("bad curl result (%i) for '%s'", msg->data.result, proxy->url);
1176          if(proxy->spdy_done || proxy->spdy_error || (NULL == proxy->response && !*(proxy->session_alive)))
1177          {
1178            PRINT_VERBOSE("cleaning");
1179            SPDY_name_value_destroy(proxy->headers);
1180            SPDY_destroy_request(proxy->request);
1181            SPDY_destroy_response(proxy->response);
1182            cleanup(proxy);
1183          }
1184          else if(NULL == proxy->response && *(proxy->session_alive))
1185          {
1186            //generate error for the client
1187            PRINT_VERBOSE("will send Bad Gateway");
1188            SPDY_name_value_destroy(proxy->headers);
1189            proxy->headers = NULL;
1190            if(NULL == (proxy->response = SPDY_build_response(SPDY_HTTP_BAD_GATEWAY,
1191                  NULL,
1192                  SPDY_HTTP_VERSION_1_1,
1193                  NULL,
1194                  ERROR_RESPONSE,
1195                  strlen(ERROR_RESPONSE))))
1196              DIE("no response");
1197            if(SPDY_YES != SPDY_queue_response(proxy->request,
1198                      proxy->response,
1199                      true,
1200                      false,
1201                      &response_done_callback,
1202                      proxy))
1203            {
1204              //clean and forget
1205              PRINT_VERBOSE("cleaning");
1206              SPDY_destroy_request(proxy->request);
1207              SPDY_destroy_response(proxy->response);
1208              cleanup(proxy);
1209            }
1210          }
1211          else
1212          {
1213            proxy->curl_error = true;
1214          }
1215          call_spdy_run = true;
1216          //TODO spdy should be notified to send RST_STREAM
1217        }
1218      }
1219      else PRINT_INFO("shouldn't happen");
1220    }
1221
1222    if(call_spdy_run)
1223    {
1224      PRINT_VERBOSE("second call to SPDY_run");
1225      SPDY_run(daemon);
1226      call_spdy_run = false;
1227    }
1228
1229    if(glob_opt.verbose)
1230    {
1231
1232#ifdef HAVE_CLOCK_GETTIME
1233#ifdef CLOCK_MONOTONIC
1234    struct timespec ts;
1235
1236    if (0 == clock_gettime(CLOCK_MONOTONIC, &ts))
1237    PRINT_VERBOSE2 ("time now %lld %lld",
1238		    (unsigned long long) ts.tv_sec,
1239		    (unsigned long long) ts.tv_nsec);
1240#endif
1241#endif
1242    }
1243
1244  }
1245  while(loop);
1246
1247	SPDY_stop_daemon(daemon);
1248
1249	curl_multi_cleanup(multi_handle);
1250
1251	SPDY_deinit();
1252
1253  deinit_parse_uri(&uri_preg);
1254
1255	return 0;
1256}
1257
1258
1259static void
1260display_usage()
1261{
1262  printf(
1263    "Usage: microspdy2http -p <PORT> [-c <CERTIFICATE>] [-k <CERT-KEY>]\n"
1264    "                      [-rvh0DtT] [-b <HTTP-SERVER>] [-l <HOST>]\n\n"
1265    "OPTIONS:\n"
1266    "    -p, --port            Listening port.\n"
1267    "    -l, --host            Listening host. If not set, will listen on [::]\n"
1268    "    -c, --certificate     Path to a certificate file. Requiered if\n"
1269    "                          --no-tls is not set.\n"
1270    "    -k, --certificate-key Path to a key file for the certificate.\n"
1271    "                          Requiered if --no-tls is not set.\n"
1272    "    -b, --backend-server  If set, the proxy will connect always to it.\n"
1273    "                          Otherwise the proxy will connect to the URL\n"
1274    "                          which is specified in the path or 'Host:'.\n"
1275    "    -v, --verbose         Print debug information.\n"
1276    "    -r, --no-tls          Do not use TLS. Client must use SPDY/3.\n"
1277    "    -h, --curl-verbose    Print debug information for curl.\n"
1278    "    -0, --http10          Prefer HTTP/1.0 connections to the next hop.\n"
1279    "    -D, --no-delay        This makes sense only if --no-tls is used.\n"
1280    "                          TCP_NODELAY will be used for all sessions' sockets.\n"
1281    "    -4, --curl-ipv4       Curl may use IPv4 to connect to the final destination.\n"
1282    "    -6, --curl-ipv6       Curl may use IPv6 to connect to the final destination.\n"
1283    "                          If neither --curl-ipv4 nor --curl-ipv6 is set,\n"
1284    "                          both will be used by default.\n"
1285    "    -T, --timeout         Maximum time in seconds for each HTTP transfer.\n"
1286    "                          Use 0 for no timeout; this is the default value.\n"
1287    "    -t, --transparent     If set, the proxy will fetch an URL which\n"
1288    "                          is based on 'Host:' header and requested path.\n"
1289    "                          Otherwise, full URL in the requested path is required.\n\n"
1290
1291  );
1292}
1293
1294
1295int
1296main (int argc, char *const *argv)
1297{
1298
1299  int getopt_ret;
1300  int option_index;
1301  struct option long_options[] = {
1302    {"port",  required_argument, 0, 'p'},
1303    {"certificate",  required_argument, 0, 'c'},
1304    {"certificate-key",  required_argument, 0, 'k'},
1305    {"backend-server",  required_argument, 0, 'b'},
1306    {"no-tls",  no_argument, 0, 'r'},
1307    {"verbose",  no_argument, 0, 'v'},
1308    {"curl-verbose",  no_argument, 0, 'h'},
1309    {"http10",  no_argument, 0, '0'},
1310    {"no-delay",  no_argument, 0, 'D'},
1311    {"transparent",  no_argument, 0, 't'},
1312    {"curl-ipv4",  no_argument, 0, '4'},
1313    {"curl-ipv6",  no_argument, 0, '6'},
1314    {"timeout",  required_argument, 0, 'T'},
1315    {0, 0, 0, 0}
1316  };
1317
1318  while (1)
1319  {
1320    getopt_ret = getopt_long( argc, argv, "p:l:c:k:b:rv0Dth46T:", long_options, &option_index);
1321    if (getopt_ret == -1)
1322      break;
1323
1324    switch(getopt_ret)
1325    {
1326      case 'p':
1327        glob_opt.listen_port = atoi(optarg);
1328        break;
1329
1330      case 'l':
1331        glob_opt.listen_host= strdup(optarg);
1332        if(NULL == glob_opt.listen_host)
1333          return 1;
1334        break;
1335
1336      case 'c':
1337        glob_opt.cert = strdup(optarg);
1338        break;
1339
1340      case 'k':
1341        glob_opt.cert_key = strdup(optarg);
1342        break;
1343
1344      case 'b':
1345        glob_opt.http_backend = strdup(optarg);
1346        if(NULL == glob_opt.http_backend)
1347          return 1;
1348        break;
1349
1350      case 'r':
1351        glob_opt.notls = true;
1352        break;
1353
1354      case 'v':
1355        glob_opt.verbose = true;
1356        break;
1357
1358      case 'h':
1359        glob_opt.curl_verbose = true;
1360        break;
1361
1362      case '0':
1363        glob_opt.http10 = true;
1364        break;
1365
1366      case 'D':
1367        glob_opt.nodelay = true;
1368        break;
1369
1370      case 't':
1371        glob_opt.transparent = true;
1372        break;
1373
1374      case '4':
1375        glob_opt.ipv4 = true;
1376        break;
1377
1378      case '6':
1379        glob_opt.ipv6 = true;
1380        break;
1381
1382      case 'T':
1383        glob_opt.timeout = atoi(optarg);
1384        break;
1385
1386      case 0:
1387        PRINT_INFO("0 from getopt");
1388        break;
1389
1390      case '?':
1391        display_usage();
1392        return 1;
1393
1394      default:
1395        DIE("default from getopt");
1396    }
1397  }
1398
1399  if(
1400    0 == glob_opt.listen_port
1401    || (!glob_opt.notls && (NULL == glob_opt.cert || NULL == glob_opt.cert_key))
1402    //|| !glob_opt.transparent && NULL != glob_opt.http_backend
1403    )
1404  {
1405    display_usage();
1406    return 1;
1407  }
1408
1409  return run();
1410}
1411
1412