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