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
free_uri(struct URI * uri)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
init_parse_uri(regex_t * preg)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
deinit_parse_uri(regex_t * preg)226 deinit_parse_uri(regex_t * preg)
227 {
228   regfree(preg);
229 }
230 
231 
232 static int
parse_uri(regex_t * preg,const char * full_uri,struct URI ** uri)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
store_in_buffer(const void * src,size_t src_size,void ** dst,size_t * dst_size)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
get_from_buffer(void ** src,size_t * src_size,void * dst,size_t max_size)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
catch_signal(int signal)359 catch_signal(int signal)
360 {
361   (void)signal;
362 
363   loop = 0;
364 }
365 
366 static void
new_session_cb(void * cls,struct SPDY_Session * session)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
session_closed_cb(void * cls,struct SPDY_Session * session,int by_client)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
spdy_post_data_cb(void * cls,struct SPDY_Request * request,const void * buf,size_t size,bool more)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
response_callback(void * cls,void * buffer,size_t max,bool * more)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
cleanup(struct Proxy * proxy)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
response_done_callback(void * cls,struct SPDY_Response * response,struct SPDY_Request * request,enum SPDY_RESPONSE_RESULT status,bool streamopened)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
curl_header_cb(void * ptr,size_t size,size_t nmemb,void * userp)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
curl_write_cb(void * contents,size_t size,size_t nmemb,void * userp)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
curl_read_cb(void * ptr,size_t size,size_t nmemb,void * userp)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
iterate_cb(void * cls,const char * name,const char * const * value,int num_values)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
standard_request_handler(void * cls,struct SPDY_Request * request,uint8_t priority,const char * method,const char * path,const char * version,const char * host,const char * scheme,struct SPDY_NameValue * headers,bool more)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
run()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
display_usage()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
main(int argc,char * const * argv)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