Home | History | Annotate | Download | only in examples
      1 /*
      2     Copyright Copyright (C) 2013 Andrey Uzunov
      3 
      4     This program is free software: you can redistribute it and/or modify
      5     it under the terms of the GNU General Public License as published by
      6     the Free Software Foundation, either version 3 of the License, or
      7     (at your option) any later version.
      8 
      9     This program is distributed in the hope that it will be useful,
     10     but WITHOUT ANY WARRANTY; without even the implied warranty of
     11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12     GNU General Public License for more details.
     13 
     14     You should have received a copy of the GNU General Public License
     15     along with this program.  If not, see <http://www.gnu.org/licenses/>.
     16 */
     17 
     18 /**
     19  * @file mhd2spdy.c
     20  * @brief  The main file of the HTTP-to-SPDY proxy with the 'main' function
     21  *         and event loop. No threads are used.
     22  *         Currently only GET is supported.
     23  *         TODOs:
     24  *         - non blocking SSL connect
     25  *         - check certificate
     26  *         - on closing spdy session, close sockets for all requests
     27  * @author Andrey Uzunov
     28  */
     29 
     30 
     31 #include "mhd2spdy_structures.h"
     32 #include "mhd2spdy_spdy.h"
     33 #include "mhd2spdy_http.h"
     34 
     35 
     36 static int run = 1;
     37 //static int spdy_close = 0;
     38 
     39 
     40 static void
     41 catch_signal(int signal)
     42 {
     43   (void)signal;
     44   //spdy_close = 1;
     45   run = 0;
     46 }
     47 
     48 
     49 void
     50 print_stat()
     51 {
     52   if(!glob_opt.statistics)
     53     return;
     54 
     55   printf("--------------------------\n");
     56   printf("Statistics (TLS overhead is ignored when used):\n");
     57   //printf("HTTP bytes received: %lld\n", glob_stat.http_bytes_received);
     58   //printf("HTTP bytes sent: %lld\n", glob_stat.http_bytes_sent);
     59   printf("SPDY bytes sent: %lld\n", glob_stat.spdy_bytes_sent);
     60   printf("SPDY bytes received: %lld\n", glob_stat.spdy_bytes_received);
     61   printf("SPDY bytes received and dropped: %lld\n", glob_stat.spdy_bytes_received_and_dropped);
     62 }
     63 
     64 
     65 int
     66 run_everything ()
     67 {
     68   unsigned long long timeoutlong=0;
     69   struct timeval timeout;
     70   int ret;
     71   fd_set rs;
     72   fd_set ws;
     73   fd_set es;
     74   int maxfd = -1;
     75   int maxfd_s = -1;
     76   struct MHD_Daemon *daemon;
     77   nfds_t spdy_npollfds = 1;
     78   struct URI * spdy2http_uri = NULL;
     79   struct SPDY_Connection *connection;
     80   struct SPDY_Connection *connections[MAX_SPDY_CONNECTIONS];
     81   struct SPDY_Connection *connection_for_delete;
     82 
     83   if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
     84     PRINT_INFO("signal failed");
     85 
     86   if (signal(SIGINT, catch_signal) == SIG_ERR)
     87     PRINT_INFO("signal failed");
     88 
     89   glob_opt.streams_opened = 0;
     90   glob_opt.responses_pending = 0;
     91   //glob_opt.global_memory = 0;
     92 
     93   srand(time(NULL));
     94 
     95   if(init_parse_uri(&glob_opt.uri_preg))
     96     DIE("Regexp compilation failed");
     97 
     98   if(NULL != glob_opt.spdy2http_str)
     99   {
    100     ret = parse_uri(&glob_opt.uri_preg, glob_opt.spdy2http_str, &spdy2http_uri);
    101     if(ret != 0)
    102       DIE("spdy_parse_uri failed");
    103   }
    104 
    105   SSL_load_error_strings();
    106   SSL_library_init();
    107   glob_opt.ssl_ctx = SSL_CTX_new(SSLv23_client_method());
    108   if(glob_opt.ssl_ctx == NULL) {
    109     PRINT_INFO2("SSL_CTX_new %s", ERR_error_string(ERR_get_error(), NULL));
    110     abort();
    111   }
    112   spdy_ssl_init_ssl_ctx(glob_opt.ssl_ctx, &glob_opt.spdy_proto_version);
    113 
    114   daemon = MHD_start_daemon (
    115           MHD_SUPPRESS_DATE_NO_CLOCK,
    116           glob_opt.listen_port,
    117           NULL, NULL, &http_cb_request, NULL,
    118           MHD_OPTION_URI_LOG_CALLBACK, &http_cb_log, NULL,
    119           MHD_OPTION_NOTIFY_COMPLETED, &http_cb_request_completed, NULL,
    120           MHD_OPTION_END);
    121   if(NULL==daemon)
    122     DIE("MHD_start_daemon failed");
    123 
    124   do
    125   {
    126     timeout.tv_sec = 0;
    127     timeout.tv_usec = 0;
    128 
    129     if(NULL == glob_opt.spdy_connection && NULL != glob_opt.spdy2http_str)
    130     {
    131       glob_opt.spdy_connection = spdy_connect(spdy2http_uri, spdy2http_uri->port, strcmp("https", spdy2http_uri->scheme)==0);
    132       if(NULL == glob_opt.spdy_connection && glob_opt.only_proxy)
    133         PRINT_INFO("cannot connect to the proxy");
    134     }
    135 
    136     FD_ZERO(&rs);
    137     FD_ZERO(&ws);
    138     FD_ZERO(&es);
    139 
    140     ret = MHD_get_timeout(daemon, &timeoutlong);
    141     if(MHD_NO == ret || timeoutlong > 5000)
    142       timeout.tv_sec = 5;
    143     else
    144     {
    145       timeout.tv_sec = timeoutlong / 1000;
    146       timeout.tv_usec = (timeoutlong % 1000) * 1000;
    147     }
    148 
    149     if(MHD_NO == MHD_get_fdset (daemon,
    150                                   &rs,
    151                                   &ws,
    152                                   &es,
    153                                   &maxfd))
    154     {
    155       PRINT_INFO("MHD_get_fdset error");
    156     }
    157     assert(-1 != maxfd);
    158 
    159     maxfd_s = spdy_get_selectfdset(
    160                                   &rs,
    161                                   &ws,
    162                                   &es,
    163                                   connections, MAX_SPDY_CONNECTIONS, &spdy_npollfds);
    164     if(maxfd_s > maxfd)
    165       maxfd = maxfd_s;
    166 
    167     PRINT_INFO2("MHD timeout %lld %lld", (unsigned long long)timeout.tv_sec, (unsigned long long)timeout.tv_usec);
    168 
    169     glob_opt.spdy_data_received = false;
    170 
    171     ret = select(maxfd+1, &rs, &ws, &es, &timeout);
    172     PRINT_INFO2("timeout now %lld %lld ret is %i", (unsigned long long)timeout.tv_sec, (unsigned long long)timeout.tv_usec, ret);
    173 
    174     switch(ret)
    175     {
    176       case -1:
    177         PRINT_INFO2("select error: %i", errno);
    178         break;
    179       case 0:
    180         //break;
    181       default:
    182       PRINT_INFO("run");
    183         //MHD_run_from_select(daemon,&rs, &ws, &es); //not closing FDs at some time in past
    184         MHD_run(daemon);
    185         spdy_run_select(&rs, &ws, &es, connections, spdy_npollfds);
    186         if(glob_opt.spdy_data_received)
    187         {
    188           PRINT_INFO("MHD run again");
    189           //MHD_run_from_select(daemon,&rs, &ws, &es); //not closing FDs at some time in past
    190           MHD_run(daemon);
    191         }
    192         break;
    193     }
    194   }
    195   while(run);
    196 
    197   MHD_stop_daemon (daemon);
    198 
    199   //TODO SSL_free brakes
    200   spdy_free_connection(glob_opt.spdy_connection);
    201 
    202   connection = glob_opt.spdy_connections_head;
    203   while(NULL != connection)
    204   {
    205     connection_for_delete = connection;
    206     connection = connection_for_delete->next;
    207     glob_opt.streams_opened -= connection_for_delete->streams_opened;
    208     DLL_remove(glob_opt.spdy_connections_head, glob_opt.spdy_connections_tail, connection_for_delete);
    209     spdy_free_connection(connection_for_delete);
    210   }
    211 
    212   free_uri(spdy2http_uri);
    213 
    214   deinit_parse_uri(&glob_opt.uri_preg);
    215 
    216   SSL_CTX_free(glob_opt.ssl_ctx);
    217   ERR_free_strings();
    218   EVP_cleanup();
    219 
    220   PRINT_INFO2("spdy streams: %i; http requests: %i", glob_opt.streams_opened, glob_opt.responses_pending);
    221   //PRINT_INFO2("memory allocated %zu bytes", glob_opt.global_memory);
    222 
    223   print_stat();
    224 
    225   return 0;
    226 }
    227 
    228 
    229 void
    230 display_usage()
    231 {
    232   printf(
    233     "Usage: mhd2spdy [-ovs] [-b <SPDY2HTTP-PROXY>] -p <PORT>\n\n"
    234     "OPTIONS:\n"
    235     "    -p, --port            Listening port.\n"
    236     "    -b, --backend-proxy   If set, he proxy will send requests to\n"
    237     "                          that SPDY server or proxy. Set the address\n"
    238     "                          in the form 'http://host:port'. Use 'https'\n"
    239     "                          for SPDY over TLS, or 'http' for plain SPDY\n"
    240     "                          communication with the backend.\n"
    241     "    -o, --only-proxy      If set, the proxy will always forward the\n"
    242     "                          requests to the backend proxy. If not set,\n"
    243     "                          the proxy will first try to establsh SPDY\n"
    244     "                          connection to the requested server. If the\n"
    245     "                          server does not support SPDY and TLS, the\n"
    246     "                          backend proxy will be used for the request.\n"
    247     "    -v, --verbose         Print debug information.\n"
    248     "    -s, --statistics      Print simple statistics on exit.\n\n"
    249 
    250   );
    251 }
    252 
    253 
    254 int
    255 main (int argc,
    256       char *const *argv)
    257 {
    258   int getopt_ret;
    259   int option_index;
    260   struct option long_options[] = {
    261     {"port",  required_argument, 0, 'p'},
    262     {"backend-proxy",  required_argument, 0, 'b'},
    263     {"verbose",  no_argument, 0, 'v'},
    264     {"only-proxy",  no_argument, 0, 'o'},
    265     {"statistics",  no_argument, 0, 's'},
    266     {0, 0, 0, 0}
    267   };
    268 
    269   while (1)
    270   {
    271     getopt_ret = getopt_long( argc, argv, "p:b:vos", long_options, &option_index);
    272     if (getopt_ret == -1)
    273       break;
    274 
    275     switch(getopt_ret)
    276     {
    277       case 'p':
    278         glob_opt.listen_port = atoi(optarg);
    279         break;
    280 
    281       case 'b':
    282         glob_opt.spdy2http_str = strdup(optarg);
    283         if(NULL == glob_opt.spdy2http_str)
    284           return 1;
    285         break;
    286 
    287       case 'v':
    288         glob_opt.verbose = true;
    289         break;
    290 
    291       case 'o':
    292         glob_opt.only_proxy = true;
    293         break;
    294 
    295       case 's':
    296         glob_opt.statistics = true;
    297         break;
    298 
    299       case 0:
    300         PRINT_INFO("0 from getopt");
    301         break;
    302 
    303       case '?':
    304         display_usage();
    305         return 1;
    306 
    307       default:
    308         DIE("default from getopt");
    309     }
    310   }
    311 
    312   if(
    313     0 == glob_opt.listen_port
    314     || (glob_opt.only_proxy && NULL == glob_opt.spdy2http_str)
    315     )
    316   {
    317     display_usage();
    318     return 1;
    319   }
    320 
    321   return run_everything();
    322 }
    323