• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
catch_signal(int signal)41 catch_signal(int signal)
42 {
43   (void)signal;
44   //spdy_close = 1;
45   run = 0;
46 }
47 
48 
49 void
print_stat()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
run_everything()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
display_usage()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
main(int argc,char * const * argv)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