Home | History | Annotate | Download | only in spdy2http
      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 
     56 struct 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 
     75 struct 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 
    145 static int loop = 1;
    146 
    147 static CURLM *multi_handle;
    148 
    149 static int still_running = 0; /* keep number of running handles */
    150 
    151 static regex_t uri_preg;
    152 
    153 static bool call_spdy_run;
    154 static bool call_curl_run;
    155 
    156 int debug_num_curls;
    157 
    158 
    159 struct 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 
    188 static void
    189 free_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 
    208 static int
    209 init_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 
    225 static void
    226 deinit_parse_uri(regex_t * preg)
    227 {
    228   regfree(preg);
    229 }
    230 
    231 
    232 static int
    233 parse_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 
    311 static bool
    312 store_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 
    331 static ssize_t
    332 get_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 
    358 static void
    359 catch_signal(int signal)
    360 {
    361   (void)signal;
    362 
    363   loop = 0;
    364 }
    365 
    366 static void
    367 new_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 
    385 static void
    386 session_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 
    403 static int
    404 spdy_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 
    440 ssize_t
    441 response_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 
    482 static void
    483 cleanup(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 
    505 static void
    506 response_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 
    527 static size_t
    528 curl_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 
    682 static size_t
    683 curl_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 
    726 static size_t
    727 curl_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 
    789 static int
    790 iterate_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 
    829 static void
    830 standard_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 
    971 static int
    972 run ()
    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 
   1259 static void
   1260 display_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 
   1295 int
   1296 main (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