00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00038 #include "platform.h"
00039 #include <unistd.h>
00040 #include <stdlib.h>
00041 #include <stdint.h>
00042 #include <stdbool.h>
00043 #include <string.h>
00044 #include <stdio.h>
00045 #include <ctype.h>
00046 #include <errno.h>
00047 #include "microspdy.h"
00048 #include <curl/curl.h>
00049 #include <assert.h>
00050 #include <getopt.h>
00051 #include <regex.h>
00052
00053 #define ERROR_RESPONSE "502 Bad Gateway"
00054
00055
00056 struct global_options
00057 {
00058 char *http_backend;
00059 char *cert;
00060 char *cert_key;
00061 char *listen_host;
00062 unsigned int timeout;
00063 uint16_t listen_port;
00064 bool verbose;
00065 bool curl_verbose;
00066 bool transparent;
00067 bool http10;
00068 bool notls;
00069 bool nodelay;
00070 bool ipv4;
00071 bool ipv6;
00072 } glob_opt;
00073
00074
00075 struct URI
00076 {
00077 char * full_uri;
00078 char * scheme;
00079 char * host_and_port;
00080
00081 char * host;
00082 char * path;
00083 char * path_and_more;
00084 char * query;
00085 char * fragment;
00086 uint16_t port;
00087 };
00088
00089
00090 #define PRINT_INFO(msg) do{\
00091 fprintf(stdout, "%i:%s\n", __LINE__, msg);\
00092 fflush(stdout);\
00093 }\
00094 while(0)
00095
00096
00097 #define PRINT_INFO2(fmt, ...) do{\
00098 fprintf(stdout, "%i\n", __LINE__);\
00099 fprintf(stdout, fmt,##__VA_ARGS__);\
00100 fprintf(stdout, "\n");\
00101 fflush(stdout);\
00102 }\
00103 while(0)
00104
00105
00106 #define PRINT_VERBOSE(msg) do{\
00107 if(glob_opt.verbose){\
00108 fprintf(stdout, "%i:%s\n", __LINE__, msg);\
00109 fflush(stdout);\
00110 }\
00111 }\
00112 while(0)
00113
00114
00115 #define PRINT_VERBOSE2(fmt, ...) do{\
00116 if(glob_opt.verbose){\
00117 fprintf(stdout, "%i\n", __LINE__);\
00118 fprintf(stdout, fmt,##__VA_ARGS__);\
00119 fprintf(stdout, "\n");\
00120 fflush(stdout);\
00121 }\
00122 }\
00123 while(0)
00124
00125
00126 #define CURL_SETOPT(handle, opt, val) do{\
00127 int ret; \
00128 if(CURLE_OK != (ret = curl_easy_setopt(handle, opt, val))) \
00129 { \
00130 PRINT_INFO2("curl_easy_setopt failed (%i = %i)", opt, ret); \
00131 abort(); \
00132 } \
00133 }\
00134 while(0)
00135
00136
00137 #define DIE(msg) do{\
00138 printf("FATAL ERROR (line %i): %s\n", __LINE__, msg);\
00139 fflush(stdout);\
00140 exit(EXIT_FAILURE);\
00141 }\
00142 while(0)
00143
00144
00145 static int loop = 1;
00146
00147 static CURLM *multi_handle;
00148
00149 static int still_running = 0;
00150
00151 static regex_t uri_preg;
00152
00153 static bool call_spdy_run;
00154 static bool call_curl_run;
00155
00156 int debug_num_curls;
00157
00158
00159 struct Proxy
00160 {
00161 char *url;
00162 struct SPDY_Request *request;
00163 struct SPDY_Response *response;
00164 CURL *curl_handle;
00165 struct curl_slist *curl_headers;
00166 struct SPDY_NameValue *headers;
00167 char *version;
00168 char *status_msg;
00169 void *http_body;
00170 void *received_body;
00171 bool *session_alive;
00172 size_t http_body_size;
00173 size_t received_body_size;
00174
00175 int status;
00176
00177 bool receiving_done;
00178 bool is_curl_read_paused;
00179 bool is_with_body_data;
00180
00181 bool curl_done;
00182 bool curl_error;
00183 bool spdy_done;
00184 bool spdy_error;
00185 };
00186
00187
00188 static void
00189 free_uri(struct URI * uri)
00190 {
00191 if(NULL != uri)
00192 {
00193 free(uri->full_uri);
00194 free(uri->scheme);
00195 free(uri->host_and_port);
00196
00197 free(uri->host);
00198 free(uri->path);
00199 free(uri->path_and_more);
00200 free(uri->query);
00201 free(uri->fragment);
00202 uri->port = 0;
00203 free(uri);
00204 }
00205 }
00206
00207
00208 static int
00209 init_parse_uri(regex_t * preg)
00210 {
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221 return regcomp(preg, "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?", REG_EXTENDED);
00222 }
00223
00224
00225 static void
00226 deinit_parse_uri(regex_t * preg)
00227 {
00228 regfree(preg);
00229 }
00230
00231
00232 static int
00233 parse_uri(regex_t * preg, const char * full_uri, struct URI ** uri)
00234 {
00235
00236 int ret;
00237 char *colon;
00238 long long port;
00239 size_t nmatch = 10;
00240 regmatch_t pmatch[10];
00241
00242 if (0 != (ret = regexec(preg, full_uri, nmatch, pmatch, 0)))
00243 return ret;
00244
00245 *uri = malloc(sizeof(struct URI));
00246 if(NULL == *uri)
00247 return -200;
00248
00249 (*uri)->full_uri = strdup(full_uri);
00250
00251 asprintf(&((*uri)->scheme),
00252 "%.*s",
00253 (int) (pmatch[2].rm_eo - pmatch[2].rm_so),
00254 &full_uri[pmatch[2].rm_so]);
00255 asprintf(&((*uri)->host_and_port), "%.*s",
00256 (int) (pmatch[4].rm_eo - pmatch[4].rm_so),
00257 &full_uri[pmatch[4].rm_so]);
00258 asprintf(&((*uri)->path),
00259 "%.*s",
00260 (int) (pmatch[5].rm_eo - pmatch[5].rm_so),
00261 &full_uri[pmatch[5].rm_so]);
00262 asprintf(&((*uri)->path_and_more),
00263 "%.*s",
00264 (int) (pmatch[9].rm_eo - pmatch[5].rm_so),
00265 &full_uri[pmatch[5].rm_so]);
00266 asprintf(&((*uri)->query),
00267 "%.*s",
00268 (int) (pmatch[7].rm_eo - pmatch[7].rm_so),
00269 &full_uri[pmatch[7].rm_so]);
00270 asprintf(&((*uri)->fragment),
00271 "%.*s",
00272 (int) (pmatch[9].rm_eo - pmatch[9].rm_so),
00273 &full_uri[pmatch[9].rm_so]);
00274
00275 colon = strrchr((*uri)->host_and_port, ':');
00276 if(NULL == colon)
00277 {
00278 (*uri)->host = strdup((*uri)->host_and_port);
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292 (*uri)->port = 0;
00293
00294
00295 return 0;
00296 }
00297
00298 port = atoi(colon + 1);
00299 if(port<1 || port >= 256 * 256)
00300 {
00301 free_uri(*uri);
00302 return -100;
00303 }
00304 (*uri)->port = port;
00305 asprintf(&((*uri)->host), "%.*s", (int)(colon - (*uri)->host_and_port), (*uri)->host_and_port);
00306
00307 return 0;
00308 }
00309
00310
00311 static bool
00312 store_in_buffer(const void *src, size_t src_size, void **dst, size_t *dst_size)
00313 {
00314 if(0 == src_size)
00315 return true;
00316
00317 if(NULL == *dst)
00318 *dst = malloc(src_size);
00319 else
00320 *dst = realloc(*dst, src_size + *dst_size);
00321 if(NULL == *dst)
00322 return false;
00323
00324 memcpy(*dst + *dst_size, src, src_size);
00325 *dst_size += src_size;
00326
00327 return true;
00328 }
00329
00330
00331 static ssize_t
00332 get_from_buffer(void **src, size_t *src_size, void *dst, size_t max_size)
00333 {
00334 size_t ret;
00335 void *newbody;
00336
00337 if(max_size >= *src_size)
00338 {
00339 ret = *src_size;
00340 newbody = NULL;
00341 }
00342 else
00343 {
00344 ret = max_size;
00345 if(NULL == (newbody = malloc(*src_size - max_size)))
00346 return -1;
00347 memcpy(newbody, *src + ret, *src_size - ret);
00348 }
00349 memcpy(dst, *src, ret);
00350 free(*src);
00351 *src = newbody;
00352 *src_size -= ret;
00353
00354 return ret;
00355 }
00356
00357
00358 static void
00359 catch_signal(int signal)
00360 {
00361 (void)signal;
00362
00363 loop = 0;
00364 }
00365
00366 static void
00367 new_session_cb (void * cls,
00368 struct SPDY_Session * session)
00369 {
00370 (void)cls;
00371
00372 bool *session_alive;
00373
00374 PRINT_VERBOSE("new session");
00375
00376 if(NULL == (session_alive = malloc(sizeof(bool))))
00377 {
00378 DIE("no memory");
00379 }
00380 *session_alive = true;
00381 SPDY_set_cls_to_session(session,
00382 session_alive);
00383 }
00384
00385 static void
00386 session_closed_cb (void * cls,
00387 struct SPDY_Session * session,
00388 int by_client)
00389 {
00390 (void)cls;
00391
00392 bool *session_alive;
00393
00394 PRINT_VERBOSE2("session closed; by client: %i", by_client);
00395
00396 session_alive = SPDY_get_cls_from_session(session);
00397 assert(NULL != session_alive);
00398
00399 *session_alive = false;
00400 }
00401
00402
00403 static int
00404 spdy_post_data_cb (void * cls,
00405 struct SPDY_Request *request,
00406 const void * buf,
00407 size_t size,
00408 bool more)
00409 {
00410 (void)cls;
00411 int ret;
00412 struct Proxy *proxy = (struct Proxy *)SPDY_get_cls_from_request(request);
00413
00414 if(!store_in_buffer(buf, size, &proxy->received_body, &proxy->received_body_size))
00415 {
00416 PRINT_INFO("not enough memory (malloc/realloc returned NULL)");
00417 return 0;
00418 }
00419
00420 proxy->receiving_done = !more;
00421
00422 PRINT_VERBOSE2("POST bytes from SPDY: %zu", size);
00423
00424 call_curl_run = true;
00425
00426 if(proxy->is_curl_read_paused)
00427 {
00428 if(CURLE_OK != (ret = curl_easy_pause(proxy->curl_handle, CURLPAUSE_CONT)))
00429 {
00430 PRINT_INFO2("curl_easy_pause returned %i", ret);
00431 abort();
00432 }
00433 PRINT_VERBOSE("curl_read_cb pause resumed");
00434 }
00435
00436 return SPDY_YES;
00437 }
00438
00439
00440 ssize_t
00441 response_callback (void *cls,
00442 void *buffer,
00443 size_t max,
00444 bool *more)
00445 {
00446 ssize_t ret;
00447 struct Proxy *proxy = (struct Proxy *)cls;
00448
00449 *more = true;
00450
00451 assert(!proxy->spdy_error);
00452
00453 if(proxy->curl_error)
00454 {
00455 PRINT_VERBOSE("tell spdy about the error");
00456 return -1;
00457 }
00458
00459 if(!proxy->http_body_size)
00460 {
00461 PRINT_VERBOSE("nothing to write now");
00462 if(proxy->curl_done || proxy->curl_error) *more = false;
00463 return 0;
00464 }
00465
00466 ret = get_from_buffer(&(proxy->http_body), &(proxy->http_body_size), buffer, max);
00467 if(ret < 0)
00468 {
00469 PRINT_INFO("no memory");
00470
00471 return -1;
00472 }
00473
00474 if((proxy->curl_done || proxy->curl_error) && 0 == proxy->http_body_size) *more = false;
00475
00476 PRINT_VERBOSE2("given bytes to microspdy: %zd", ret);
00477
00478 return ret;
00479 }
00480
00481
00482 static void
00483 cleanup(struct Proxy *proxy)
00484 {
00485 int ret;
00486
00487
00488
00489 if(CURLM_OK != (ret = curl_multi_remove_handle(multi_handle, proxy->curl_handle)))
00490 {
00491 PRINT_INFO2("curl_multi_remove_handle failed (%i)", ret);
00492 DIE("bug in cleanup");
00493 }
00494 debug_num_curls--;
00495
00496
00497 curl_slist_free_all(proxy->curl_headers);
00498 curl_easy_cleanup(proxy->curl_handle);
00499
00500 free(proxy->url);
00501 free(proxy);
00502 }
00503
00504
00505 static void
00506 response_done_callback(void *cls,
00507 struct SPDY_Response *response,
00508 struct SPDY_Request *request,
00509 enum SPDY_RESPONSE_RESULT status,
00510 bool streamopened)
00511 {
00512 (void)streamopened;
00513 struct Proxy *proxy = (struct Proxy *)cls;
00514
00515 if(SPDY_RESPONSE_RESULT_SUCCESS != status)
00516 {
00517 free(proxy->http_body);
00518 proxy->http_body = NULL;
00519 proxy->spdy_error = true;
00520 }
00521 cleanup(proxy);
00522 SPDY_destroy_request(request);
00523 SPDY_destroy_response(response);
00524 }
00525
00526
00527 static size_t
00528 curl_header_cb(void *ptr, size_t size, size_t nmemb, void *userp)
00529 {
00530 size_t realsize = size * nmemb;
00531 struct Proxy *proxy = (struct Proxy *)userp;
00532 char *line = (char *)ptr;
00533 char *name;
00534 char *value;
00535 char *status;
00536 unsigned int i;
00537 unsigned int pos;
00538 int ret;
00539 int num_values;
00540 const char * const * values;
00541 bool abort_it;
00542
00543
00544 if(!*(proxy->session_alive))
00545 {
00546 PRINT_VERBOSE("headers received, but session is dead");
00547 proxy->spdy_error = true;
00548 proxy->curl_error = true;
00549 return 0;
00550 }
00551
00552
00553 if(NULL != proxy->response) return 0;
00554
00555 if('\r' == line[0] || '\n' == line[0])
00556 {
00557
00558 if(NULL == (proxy->response = SPDY_build_response_with_callback(proxy->status,
00559 proxy->status_msg,
00560 proxy->version,
00561 proxy->headers,
00562 &response_callback,
00563 proxy,
00564 0)))
00565
00566 DIE("no response");
00567
00568 SPDY_name_value_destroy(proxy->headers);
00569 proxy->headers = NULL;
00570 free(proxy->status_msg);
00571 proxy->status_msg = NULL;
00572 free(proxy->version);
00573 proxy->version = NULL;
00574
00575 if(SPDY_YES != SPDY_queue_response(proxy->request,
00576 proxy->response,
00577 true,
00578 false,
00579 &response_done_callback,
00580 proxy))
00581 {
00582
00583
00584 proxy->spdy_error = true;
00585 proxy->curl_error = true;
00586 PRINT_VERBOSE2("no queue in curl_header_cb for %s", proxy->url);
00587 SPDY_destroy_response(proxy->response);
00588 proxy->response = NULL;
00589 return 0;
00590 }
00591
00592 call_spdy_run = true;
00593
00594 return realsize;
00595 }
00596
00597 pos = 0;
00598 if(NULL == proxy->version)
00599 {
00600
00601
00602 for(i=pos; i<realsize && ' '!=line[i]; ++i);
00603 if(i == realsize)
00604 DIE("error on parsing headers");
00605 if(NULL == (proxy->version = strndup(line, i - pos)))
00606 DIE("No memory");
00607 pos = i+1;
00608
00609
00610 for(i=pos; i<realsize && ' '!=line[i] && '\r'!=line[i] && '\n'!=line[i]; ++i);
00611 if(NULL == (status = strndup(&(line[pos]), i - pos)))
00612 DIE("No memory");
00613 proxy->status = atoi(status);
00614 free(status);
00615 if(i<realsize && '\r'!=line[i] && '\n'!=line[i])
00616 {
00617
00618 pos = i+1;
00619 for(i=pos; i<realsize && '\r'!=line[i] && '\n'!=line[i]; ++i);
00620 if(NULL == (proxy->status_msg = strndup(&(line[pos]), i - pos)))
00621 DIE("No memory");
00622 }
00623 PRINT_VERBOSE2("Header line received '%s' '%i' '%s' ", proxy->version, proxy->status, proxy->status_msg);
00624 return realsize;
00625 }
00626
00627
00628
00629 for(i=pos; i<realsize && ':'!=line[i] && '\r'!=line[i] && '\n'!=line[i]; ++i)
00630 line[i] = tolower(line[i]);
00631 if(NULL == (name = strndup(line, i - pos)))
00632 DIE("No memory");
00633 if(0 == strcmp(SPDY_HTTP_HEADER_CONNECTION, name)
00634 || 0 == strcmp(SPDY_HTTP_HEADER_KEEP_ALIVE, name)
00635 || 0 == strcmp(SPDY_HTTP_HEADER_TRANSFER_ENCODING, name)
00636 )
00637 {
00638
00639 free(name);
00640 return realsize;
00641 }
00642 if(i == realsize || '\r'==line[i] || '\n'==line[i])
00643 {
00644
00645 if(SPDY_YES != SPDY_name_value_add(proxy->headers, name, ""))
00646 DIE("SPDY_name_value_add failed");
00647 return realsize;
00648 }
00649
00650
00651 pos = i+1;
00652 while(pos<realsize && isspace(line[pos])) ++pos;
00653 for(i=pos; i<realsize && '\r'!=line[i] && '\n'!=line[i]; ++i);
00654 if(NULL == (value = strndup(&(line[pos]), i - pos)))
00655 DIE("No memory");
00656 PRINT_VERBOSE2("Adding header: '%s': '%s'", name, value);
00657 if(SPDY_YES != (ret = SPDY_name_value_add(proxy->headers, name, value)))
00658 {
00659 abort_it=true;
00660 if(NULL != (values = SPDY_name_value_lookup(proxy->headers, name, &num_values)))
00661 for(i=0; i<(unsigned int)num_values; ++i)
00662 if(0 == strcasecmp(value, values[i]))
00663 {
00664 abort_it=false;
00665 PRINT_VERBOSE2("header appears more than once with same value '%s: %s'", name, value);
00666 break;
00667 }
00668
00669 if(abort_it)
00670 {
00671 PRINT_INFO2("SPDY_name_value_add failed (%i) for '%s'", ret, name);
00672 abort();
00673 }
00674 }
00675 free(name);
00676 free(value);
00677
00678 return realsize;
00679 }
00680
00681
00682 static size_t
00683 curl_write_cb(void *contents, size_t size, size_t nmemb, void *userp)
00684 {
00685 size_t realsize = size * nmemb;
00686 struct Proxy *proxy = (struct Proxy *)userp;
00687
00688
00689 if(!*(proxy->session_alive))
00690 {
00691 PRINT_VERBOSE("data received, but session is dead");
00692 proxy->spdy_error = true;
00693 proxy->curl_error = true;
00694 return 0;
00695 }
00696
00697 if(!store_in_buffer(contents, realsize, &proxy->http_body, &proxy->http_body_size))
00698 {
00699 PRINT_INFO("not enough memory (malloc/realloc returned NULL)");
00700 proxy->curl_error = true;
00701 return 0;
00702 }
00703
00704
00705
00706
00707
00708
00709
00710
00711
00712
00713
00714
00715
00716
00717
00718 PRINT_VERBOSE2("received bytes from curl: %zu", realsize);
00719
00720 call_spdy_run = true;
00721
00722 return realsize;
00723 }
00724
00725
00726 static size_t
00727 curl_read_cb(void *ptr, size_t size, size_t nmemb, void *userp)
00728 {
00729 ssize_t ret;
00730 size_t max = size * nmemb;
00731 struct Proxy *proxy = (struct Proxy *)userp;
00732
00733
00734
00735 if((proxy->receiving_done && !proxy->received_body_size) || !proxy->is_with_body_data || max < 1)
00736 {
00737 PRINT_VERBOSE("curl_read_cb last call");
00738 return 0;
00739 }
00740
00741 if(!*(proxy->session_alive))
00742 {
00743 PRINT_VERBOSE("POST is still being sent, but session is dead");
00744 return CURL_READFUNC_ABORT;
00745 }
00746
00747 if(!proxy->received_body_size)
00748 {
00749 PRINT_VERBOSE("curl_read_cb called paused");
00750 proxy->is_curl_read_paused = true;
00751 return CURL_READFUNC_PAUSE;
00752 }
00753
00754 ret = get_from_buffer(&(proxy->received_body), &(proxy->received_body_size), ptr, max);
00755 if(ret < 0)
00756 {
00757 PRINT_INFO("no memory");
00758 return CURL_READFUNC_ABORT;
00759 }
00760
00761
00762
00763
00764
00765
00766
00767
00768
00769
00770
00771
00772
00773
00774
00775
00776
00777
00778
00779
00780
00781
00782
00783 PRINT_VERBOSE2("given POST bytes to curl: %zd", ret);
00784
00785 return ret;
00786 }
00787
00788
00789 static int
00790 iterate_cb (void *cls, const char *name, const char * const * value, int num_values)
00791 {
00792 struct Proxy *proxy = (struct Proxy *)cls;
00793 struct curl_slist **curl_headers = (&(proxy->curl_headers));
00794 char *line;
00795 int line_len = strlen(name) + 3;
00796 int i;
00797
00798 for(i=0; i<num_values; ++i)
00799 {
00800 if(i) line_len += 2;
00801 line_len += strlen(value[i]);
00802 }
00803
00804 if(NULL == (line = malloc(line_len)))
00805 DIE("No memory");
00806 line[0] = 0;
00807
00808 strcat(line, name);
00809 strcat(line, ": ");
00810
00811
00812 line[0] = toupper(line[0]);
00813
00814 for(i=0; i<num_values; ++i)
00815 {
00816 if(i) strcat(line, ", ");
00817 PRINT_VERBOSE2("Adding request header: '%s' len %ld", value[i], strlen(value[i]));
00818 strcat(line, value[i]);
00819 }
00820 PRINT_VERBOSE2("Adding request header: '%s'", line);
00821 if(NULL == (*curl_headers = curl_slist_append(*curl_headers, line)))
00822 DIE("curl_slist_append failed");
00823 free(line);
00824
00825 return SPDY_YES;
00826 }
00827
00828
00829 static void
00830 standard_request_handler(void *cls,
00831 struct SPDY_Request * request,
00832 uint8_t priority,
00833 const char *method,
00834 const char *path,
00835 const char *version,
00836 const char *host,
00837 const char *scheme,
00838 struct SPDY_NameValue * headers,
00839 bool more)
00840 {
00841 (void)cls;
00842 (void)priority;
00843 (void)host;
00844 (void)scheme;
00845
00846 struct Proxy *proxy;
00847 int ret;
00848 struct URI *uri;
00849 struct SPDY_Session *session;
00850
00851 proxy = SPDY_get_cls_from_request(request);
00852 if(NULL != proxy)
00853 {
00854
00855 return;
00856 }
00857
00858 PRINT_VERBOSE2("received request for '%s %s %s'\n", method, path, version);
00859
00860
00861 if(NULL == (proxy = malloc(sizeof(struct Proxy))))
00862 DIE("No memory");
00863 memset(proxy, 0, sizeof(struct Proxy));
00864
00865
00866
00867 session = SPDY_get_session_for_request(request);
00868 assert(NULL != session);
00869 proxy->session_alive = SPDY_get_cls_from_session(session);
00870 assert(NULL != proxy->session_alive);
00871
00872 SPDY_set_cls_to_request(request, proxy);
00873
00874 proxy->request = request;
00875 proxy->is_with_body_data = more;
00876 if(NULL == (proxy->headers = SPDY_name_value_create()))
00877 DIE("No memory");
00878
00879 if(glob_opt.transparent)
00880 {
00881 if(NULL != glob_opt.http_backend)
00882 ret = asprintf(&(proxy->url),"%s://%s%s", scheme, glob_opt.http_backend, path);
00883 else
00884 ret = asprintf(&(proxy->url),"%s://%s%s", scheme, host, path);
00885 if(-1 == ret)
00886 DIE("No memory");
00887
00888 ret = parse_uri(&uri_preg, proxy->url, &uri);
00889 if(ret != 0)
00890 DIE("parsing built uri failed");
00891 }
00892 else
00893 {
00894 ret = parse_uri(&uri_preg, path, &uri);
00895 PRINT_INFO2("path %s '%s' '%s'", path, uri->scheme, uri->host);
00896 if(ret != 0 || !strlen(uri->scheme) || !strlen(uri->host))
00897 DIE("parsing received uri failed");
00898
00899 if(NULL != glob_opt.http_backend)
00900 {
00901 ret = asprintf(&(proxy->url),"%s://%s%s", uri->scheme, glob_opt.http_backend, uri->path_and_more);
00902 if(-1 == ret)
00903 DIE("No memory");
00904 }
00905 else
00906 if(NULL == (proxy->url = strdup(path)))
00907 DIE("No memory");
00908 }
00909
00910 free_uri(uri);
00911
00912 PRINT_VERBOSE2("curl will request '%s'", proxy->url);
00913
00914 SPDY_name_value_iterate(headers, &iterate_cb, proxy);
00915
00916 if(NULL == (proxy->curl_handle = curl_easy_init()))
00917 {
00918 PRINT_INFO("curl_easy_init failed");
00919 abort();
00920 }
00921
00922 if(glob_opt.curl_verbose)
00923 CURL_SETOPT(proxy->curl_handle, CURLOPT_VERBOSE, 1);
00924
00925 if(0 == strcmp(SPDY_HTTP_METHOD_POST,method))
00926 {
00927 if(NULL == (proxy->curl_headers = curl_slist_append(proxy->curl_headers, "Expect:")))
00928 DIE("curl_slist_append failed");
00929 CURL_SETOPT(proxy->curl_handle, CURLOPT_POST, 1);
00930 CURL_SETOPT(proxy->curl_handle, CURLOPT_READFUNCTION, curl_read_cb);
00931 CURL_SETOPT(proxy->curl_handle, CURLOPT_READDATA, proxy);
00932 }
00933
00934 if(glob_opt.timeout)
00935 CURL_SETOPT(proxy->curl_handle, CURLOPT_TIMEOUT, glob_opt.timeout);
00936 CURL_SETOPT(proxy->curl_handle, CURLOPT_URL, proxy->url);
00937 if(glob_opt.http10)
00938 CURL_SETOPT(proxy->curl_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
00939 CURL_SETOPT(proxy->curl_handle, CURLOPT_WRITEFUNCTION, curl_write_cb);
00940 CURL_SETOPT(proxy->curl_handle, CURLOPT_WRITEDATA, proxy);
00941 CURL_SETOPT(proxy->curl_handle, CURLOPT_HEADERFUNCTION, curl_header_cb);
00942 CURL_SETOPT(proxy->curl_handle, CURLOPT_HEADERDATA, proxy);
00943 CURL_SETOPT(proxy->curl_handle, CURLOPT_PRIVATE, proxy);
00944 CURL_SETOPT(proxy->curl_handle, CURLOPT_HTTPHEADER, proxy->curl_headers);
00945 CURL_SETOPT(proxy->curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
00946 CURL_SETOPT(proxy->curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);
00947 if(glob_opt.ipv4 && !glob_opt.ipv6)
00948 CURL_SETOPT(proxy->curl_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
00949 else if(glob_opt.ipv6 && !glob_opt.ipv4)
00950 CURL_SETOPT(proxy->curl_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6);
00951
00952 if(CURLM_OK != (ret = curl_multi_add_handle(multi_handle, proxy->curl_handle)))
00953 {
00954 PRINT_INFO2("curl_multi_add_handle failed (%i)", ret);
00955 abort();
00956 }
00957 debug_num_curls++;
00958
00959
00960 if(CURLM_OK != (ret = curl_multi_perform(multi_handle, &still_running))
00961 && CURLM_CALL_MULTI_PERFORM != ret)
00962 {
00963 PRINT_INFO2("curl_multi_perform failed (%i)", ret);
00964 abort();
00965 }
00966
00967 call_curl_run = true;
00968 }
00969
00970
00971 static int
00972 run ()
00973 {
00974 unsigned long long timeoutlong = 0;
00975 unsigned long long timeout_spdy = 0;
00976 long timeout_curl = -1;
00977 struct timeval timeout;
00978 int ret;
00979 int ret_curl;
00980 int ret_spdy;
00981 fd_set rs;
00982 fd_set ws;
00983 fd_set es;
00984 int maxfd = -1;
00985 int maxfd_curl = -1;
00986 struct SPDY_Daemon *daemon;
00987 CURLMsg *msg;
00988 int msgs_left;
00989 struct Proxy *proxy;
00990 struct sockaddr_in *addr;
00991 struct addrinfo hints;
00992 char service[NI_MAXSERV];
00993 struct addrinfo *gai;
00994 enum SPDY_IO_SUBSYSTEM io = glob_opt.notls ? SPDY_IO_SUBSYSTEM_RAW : SPDY_IO_SUBSYSTEM_OPENSSL;
00995 enum SPDY_DAEMON_FLAG flags = SPDY_DAEMON_FLAG_NO;
00996
00997 char *curl_private;
00998
00999 signal(SIGPIPE, SIG_IGN);
01000
01001 if (signal(SIGINT, catch_signal) == SIG_ERR)
01002 PRINT_VERBOSE("signal failed");
01003
01004 srand(time(NULL));
01005 if(init_parse_uri(&uri_preg))
01006 DIE("Regexp compilation failed");
01007
01008 SPDY_init();
01009
01010 if(glob_opt.nodelay)
01011 flags |= SPDY_DAEMON_FLAG_NO_DELAY;
01012
01013 if(NULL == glob_opt.listen_host)
01014 {
01015 daemon = SPDY_start_daemon(glob_opt.listen_port,
01016 glob_opt.cert,
01017 glob_opt.cert_key,
01018 &new_session_cb,
01019 &session_closed_cb,
01020 &standard_request_handler,
01021 &spdy_post_data_cb,
01022 NULL,
01023 SPDY_DAEMON_OPTION_SESSION_TIMEOUT,
01024 1800,
01025 SPDY_DAEMON_OPTION_IO_SUBSYSTEM,
01026 io,
01027 SPDY_DAEMON_OPTION_FLAGS,
01028 flags,
01029 SPDY_DAEMON_OPTION_END);
01030 }
01031 else
01032 {
01033 snprintf (service, sizeof(service), "%u", glob_opt.listen_port);
01034 memset (&hints, 0, sizeof(struct addrinfo));
01035 hints.ai_family = AF_INET;
01036 hints.ai_socktype = SOCK_STREAM;
01037
01038 ret = getaddrinfo(glob_opt.listen_host, service, &hints, &gai);
01039 if(ret != 0)
01040 DIE("problem with specified host");
01041
01042 addr = (struct sockaddr_in *) gai->ai_addr;
01043
01044 daemon = SPDY_start_daemon(0,
01045 glob_opt.cert,
01046 glob_opt.cert_key,
01047 &new_session_cb,
01048 &session_closed_cb,
01049 &standard_request_handler,
01050 &spdy_post_data_cb,
01051 NULL,
01052 SPDY_DAEMON_OPTION_SESSION_TIMEOUT,
01053 1800,
01054 SPDY_DAEMON_OPTION_IO_SUBSYSTEM,
01055 io,
01056 SPDY_DAEMON_OPTION_FLAGS,
01057 flags,
01058 SPDY_DAEMON_OPTION_SOCK_ADDR,
01059 addr,
01060
01061
01062 SPDY_DAEMON_OPTION_END);
01063 }
01064
01065 if(NULL==daemon){
01066 printf("no daemon\n");
01067 return 1;
01068 }
01069
01070 multi_handle = curl_multi_init();
01071 if(NULL==multi_handle)
01072 DIE("no multi_handle");
01073
01074 timeout.tv_usec = 0;
01075
01076 do
01077 {
01078 FD_ZERO(&rs);
01079 FD_ZERO(&ws);
01080 FD_ZERO(&es);
01081
01082 PRINT_VERBOSE2("num curls %i", debug_num_curls);
01083
01084 ret_spdy = SPDY_get_timeout(daemon, &timeout_spdy);
01085 if(SPDY_NO == ret_spdy || timeout_spdy > 5000)
01086 timeoutlong = 5000;
01087 else
01088 timeoutlong = timeout_spdy;
01089 PRINT_VERBOSE2("SPDY timeout %lld; %i", timeout_spdy, ret_spdy);
01090
01091 if(CURLM_OK != (ret_curl = curl_multi_timeout(multi_handle, &timeout_curl)))
01092 {
01093 PRINT_VERBOSE2("curl_multi_timeout failed (%i)", ret_curl);
01094
01095 }
01096 else if(timeout_curl >= 0 && timeoutlong > (unsigned long)timeout_curl)
01097 timeoutlong = (unsigned long)timeout_curl;
01098
01099 PRINT_VERBOSE2("curl timeout %ld", timeout_curl);
01100
01101 timeout.tv_sec = timeoutlong / 1000;
01102 timeout.tv_usec = (timeoutlong % 1000) * 1000;
01103
01104 maxfd = SPDY_get_fdset (daemon,
01105 &rs,
01106 &ws,
01107 &es);
01108 assert(-1 != maxfd);
01109
01110 if(CURLM_OK != (ret = curl_multi_fdset(multi_handle, &rs,
01111 &ws,
01112 &es, &maxfd_curl)))
01113 {
01114 PRINT_INFO2("curl_multi_fdset failed (%i)", ret);
01115 abort();
01116 }
01117
01118 if(maxfd_curl > maxfd)
01119 maxfd = maxfd_curl;
01120
01121 PRINT_VERBOSE2("timeout before %lld %lld", (unsigned long long)timeout.tv_sec, (unsigned long long)timeout.tv_usec);
01122 ret = select(maxfd+1, &rs, &ws, &es, &timeout);
01123 PRINT_VERBOSE2("timeout after %lld %lld; ret is %i", (unsigned long long)timeout.tv_sec, (unsigned long long)timeout.tv_usec, ret);
01124
01125
01126
01127
01128
01129
01130
01131
01132
01133
01134 if(ret > 0 || (SPDY_YES == ret_spdy && 0 == timeout_spdy))
01135 {
01136 PRINT_VERBOSE("run spdy");
01137 SPDY_run(daemon);
01138 call_spdy_run = false;
01139 }
01140
01141
01142 {
01143 PRINT_VERBOSE("run curl");
01144 if(CURLM_OK != (ret = curl_multi_perform(multi_handle, &still_running))
01145 && CURLM_CALL_MULTI_PERFORM != ret)
01146 {
01147 PRINT_INFO2("curl_multi_perform failed (%i)", ret);
01148 abort();
01149 }
01150 call_curl_run = false;
01151 }
01152
01153
01154
01155 while ((msg = curl_multi_info_read(multi_handle, &msgs_left))) {
01156 if (msg->msg == CURLMSG_DONE) {
01157 PRINT_VERBOSE("A curl handler is done");
01158 if(CURLE_OK != (ret = curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &curl_private)))
01159 {
01160 PRINT_INFO2("err %i",ret);
01161 abort();
01162 }
01163 assert(NULL != curl_private);
01164 proxy = (struct Proxy *)curl_private;
01165 if(CURLE_OK == msg->data.result)
01166 {
01167 proxy->curl_done = true;
01168 call_spdy_run = true;
01169
01170
01171
01172 }
01173 else
01174 {
01175 PRINT_VERBOSE2("bad curl result (%i) for '%s'", msg->data.result, proxy->url);
01176 if(proxy->spdy_done || proxy->spdy_error || (NULL == proxy->response && !*(proxy->session_alive)))
01177 {
01178 PRINT_VERBOSE("cleaning");
01179 SPDY_name_value_destroy(proxy->headers);
01180 SPDY_destroy_request(proxy->request);
01181 SPDY_destroy_response(proxy->response);
01182 cleanup(proxy);
01183 }
01184 else if(NULL == proxy->response && *(proxy->session_alive))
01185 {
01186
01187 PRINT_VERBOSE("will send Bad Gateway");
01188 SPDY_name_value_destroy(proxy->headers);
01189 proxy->headers = NULL;
01190 if(NULL == (proxy->response = SPDY_build_response(SPDY_HTTP_BAD_GATEWAY,
01191 NULL,
01192 SPDY_HTTP_VERSION_1_1,
01193 NULL,
01194 ERROR_RESPONSE,
01195 strlen(ERROR_RESPONSE))))
01196 DIE("no response");
01197 if(SPDY_YES != SPDY_queue_response(proxy->request,
01198 proxy->response,
01199 true,
01200 false,
01201 &response_done_callback,
01202 proxy))
01203 {
01204
01205 PRINT_VERBOSE("cleaning");
01206 SPDY_destroy_request(proxy->request);
01207 SPDY_destroy_response(proxy->response);
01208 cleanup(proxy);
01209 }
01210 }
01211 else
01212 {
01213 proxy->curl_error = true;
01214 }
01215 call_spdy_run = true;
01216
01217 }
01218 }
01219 else PRINT_INFO("shouldn't happen");
01220 }
01221
01222 if(call_spdy_run)
01223 {
01224 PRINT_VERBOSE("second call to SPDY_run");
01225 SPDY_run(daemon);
01226 call_spdy_run = false;
01227 }
01228
01229 if(glob_opt.verbose)
01230 {
01231
01232 #ifdef HAVE_CLOCK_GETTIME
01233 #ifdef CLOCK_MONOTONIC
01234 struct timespec ts;
01235
01236 if (0 == clock_gettime(CLOCK_MONOTONIC, &ts))
01237 PRINT_VERBOSE2 ("time now %lld %lld",
01238 (unsigned long long) ts.tv_sec,
01239 (unsigned long long) ts.tv_nsec);
01240 }
01241 #endif
01242 #endif
01243 }
01244 while(loop);
01245
01246 SPDY_stop_daemon(daemon);
01247
01248 curl_multi_cleanup(multi_handle);
01249
01250 SPDY_deinit();
01251
01252 deinit_parse_uri(&uri_preg);
01253
01254 return 0;
01255 }
01256
01257
01258 static void
01259 display_usage()
01260 {
01261 printf(
01262 "Usage: microspdy2http -p <PORT> [-c <CERTIFICATE>] [-k <CERT-KEY>]\n"
01263 " [-rvh0DtT] [-b <HTTP-SERVER>] [-l <HOST>]\n\n"
01264 "OPTIONS:\n"
01265 " -p, --port Listening port.\n"
01266 " -l, --host Listening host. If not set, will listen on [::]\n"
01267 " -c, --certificate Path to a certificate file. Requiered if\n"
01268 " --no-tls is not set.\n"
01269 " -k, --certificate-key Path to a key file for the certificate.\n"
01270 " Requiered if --no-tls is not set.\n"
01271 " -b, --backend-server If set, the proxy will connect always to it.\n"
01272 " Otherwise the proxy will connect to the URL\n"
01273 " which is specified in the path or 'Host:'.\n"
01274 " -v, --verbose Print debug information.\n"
01275 " -r, --no-tls Do not use TLS. Client must use SPDY/3.\n"
01276 " -h, --curl-verbose Print debug information for curl.\n"
01277 " -0, --http10 Prefer HTTP/1.0 connections to the next hop.\n"
01278 " -D, --no-delay This makes sense only if --no-tls is used.\n"
01279 " TCP_NODELAY will be used for all sessions' sockets.\n"
01280 " -4, --curl-ipv4 Curl may use IPv4 to connect to the final destination.\n"
01281 " -6, --curl-ipv6 Curl may use IPv6 to connect to the final destination.\n"
01282 " If neither --curl-ipv4 nor --curl-ipv6 is set,\n"
01283 " both will be used by default.\n"
01284 " -T, --timeout Maximum time in seconds for each HTTP transfer.\n"
01285 " Use 0 for no timeout; this is the default value.\n"
01286 " -t, --transparent If set, the proxy will fetch an URL which\n"
01287 " is based on 'Host:' header and requested path.\n"
01288 " Otherwise, full URL in the requested path is required.\n\n"
01289
01290 );
01291 }
01292
01293
01294 int
01295 main (int argc, char *const *argv)
01296 {
01297
01298 int getopt_ret;
01299 int option_index;
01300 struct option long_options[] = {
01301 {"port", required_argument, 0, 'p'},
01302 {"certificate", required_argument, 0, 'c'},
01303 {"certificate-key", required_argument, 0, 'k'},
01304 {"backend-server", required_argument, 0, 'b'},
01305 {"no-tls", no_argument, 0, 'r'},
01306 {"verbose", no_argument, 0, 'v'},
01307 {"curl-verbose", no_argument, 0, 'h'},
01308 {"http10", no_argument, 0, '0'},
01309 {"no-delay", no_argument, 0, 'D'},
01310 {"transparent", no_argument, 0, 't'},
01311 {"curl-ipv4", no_argument, 0, '4'},
01312 {"curl-ipv6", no_argument, 0, '6'},
01313 {"timeout", required_argument, 0, 'T'},
01314 {0, 0, 0, 0}
01315 };
01316
01317 while (1)
01318 {
01319 getopt_ret = getopt_long( argc, argv, "p:l:c:k:b:rv0Dth46T:", long_options, &option_index);
01320 if (getopt_ret == -1)
01321 break;
01322
01323 switch(getopt_ret)
01324 {
01325 case 'p':
01326 glob_opt.listen_port = atoi(optarg);
01327 break;
01328
01329 case 'l':
01330 glob_opt.listen_host= strdup(optarg);
01331 if(NULL == glob_opt.listen_host)
01332 return 1;
01333 break;
01334
01335 case 'c':
01336 glob_opt.cert = strdup(optarg);
01337 break;
01338
01339 case 'k':
01340 glob_opt.cert_key = strdup(optarg);
01341 break;
01342
01343 case 'b':
01344 glob_opt.http_backend = strdup(optarg);
01345 if(NULL == glob_opt.http_backend)
01346 return 1;
01347 break;
01348
01349 case 'r':
01350 glob_opt.notls = true;
01351 break;
01352
01353 case 'v':
01354 glob_opt.verbose = true;
01355 break;
01356
01357 case 'h':
01358 glob_opt.curl_verbose = true;
01359 break;
01360
01361 case '0':
01362 glob_opt.http10 = true;
01363 break;
01364
01365 case 'D':
01366 glob_opt.nodelay = true;
01367 break;
01368
01369 case 't':
01370 glob_opt.transparent = true;
01371 break;
01372
01373 case '4':
01374 glob_opt.ipv4 = true;
01375 break;
01376
01377 case '6':
01378 glob_opt.ipv6 = true;
01379 break;
01380
01381 case 'T':
01382 glob_opt.timeout = atoi(optarg);
01383 break;
01384
01385 case 0:
01386 PRINT_INFO("0 from getopt");
01387 break;
01388
01389 case '?':
01390 display_usage();
01391 return 1;
01392
01393 default:
01394 DIE("default from getopt");
01395 }
01396 }
01397
01398 if(
01399 0 == glob_opt.listen_port
01400 || (!glob_opt.notls && (NULL == glob_opt.cert || NULL == glob_opt.cert_key))
01401
01402 )
01403 {
01404 display_usage();
01405 return 1;
01406 }
01407
01408 return run();
01409 }
01410