aboutsummaryrefslogtreecommitdiff
path: root/src/examples/mhd2spdy.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/examples/mhd2spdy.c')
-rw-r--r--src/examples/mhd2spdy.c322
1 files changed, 322 insertions, 0 deletions
diff --git a/src/examples/mhd2spdy.c b/src/examples/mhd2spdy.c
new file mode 100644
index 00000000..a2275087
--- /dev/null
+++ b/src/examples/mhd2spdy.c
@@ -0,0 +1,322 @@
+/*
+ Copyright Copyright (C) 2013 Andrey Uzunov
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file mhd2spdy.c
+ * @brief The main file of the HTTP-to-SPDY proxy with the 'main' function
+ * and event loop. No threads are used.
+ * Currently only GET is supported.
+ * TODOs:
+ * - non blocking SSL connect
+ * - check certificate
+ * - on closing spdy session, close sockets for all requests
+ * @author Andrey Uzunov
+ */
+
+
+#include "mhd2spdy_structures.h"
+#include "mhd2spdy_spdy.h"
+#include "mhd2spdy_http.h"
+
+
+static int run = 1;
+//static int spdy_close = 0;
+
+
+static void
+catch_signal(int signal)
+{
+ (void)signal;
+ //spdy_close = 1;
+ run = 0;
+}
+
+
+void
+print_stat()
+{
+ if(!glob_opt.statistics)
+ return;
+
+ printf("--------------------------\n");
+ printf("Statistics (TLS overhead is ignored when used):\n");
+ //printf("HTTP bytes received: %lld\n", glob_stat.http_bytes_received);
+ //printf("HTTP bytes sent: %lld\n", glob_stat.http_bytes_sent);
+ printf("SPDY bytes sent: %lld\n", glob_stat.spdy_bytes_sent);
+ printf("SPDY bytes received: %lld\n", glob_stat.spdy_bytes_received);
+ printf("SPDY bytes received and dropped: %lld\n", glob_stat.spdy_bytes_received_and_dropped);
+}
+
+
+int
+run_everything ()
+{
+ unsigned long long timeoutlong=0;
+ struct timeval timeout;
+ int ret;
+ fd_set rs;
+ fd_set ws;
+ fd_set es;
+ int maxfd = -1;
+ int maxfd_s = -1;
+ struct MHD_Daemon *daemon;
+ nfds_t spdy_npollfds = 1;
+ struct URI * spdy2http_uri = NULL;
+ struct SPDY_Connection *connection;
+ struct SPDY_Connection *connections[MAX_SPDY_CONNECTIONS];
+ struct SPDY_Connection *connection_for_delete;
+
+ if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
+ PRINT_INFO("signal failed");
+
+ if (signal(SIGINT, catch_signal) == SIG_ERR)
+ PRINT_INFO("signal failed");
+
+ glob_opt.streams_opened = 0;
+ glob_opt.responses_pending = 0;
+ //glob_opt.global_memory = 0;
+
+ srand(time(NULL));
+
+ if(init_parse_uri(&glob_opt.uri_preg))
+ DIE("Regexp compilation failed");
+
+ if(NULL != glob_opt.spdy2http_str)
+ {
+ ret = parse_uri(&glob_opt.uri_preg, glob_opt.spdy2http_str, &spdy2http_uri);
+ if(ret != 0)
+ DIE("spdy_parse_uri failed");
+ }
+
+ SSL_load_error_strings();
+ SSL_library_init();
+ glob_opt.ssl_ctx = SSL_CTX_new(SSLv23_client_method());
+ if(glob_opt.ssl_ctx == NULL) {
+ PRINT_INFO2("SSL_CTX_new %s", ERR_error_string(ERR_get_error(), NULL));
+ abort();
+ }
+ spdy_ssl_init_ssl_ctx(glob_opt.ssl_ctx, &glob_opt.spdy_proto_version);
+
+ daemon = MHD_start_daemon (
+ MHD_SUPPRESS_DATE_NO_CLOCK,
+ glob_opt.listen_port,
+ NULL, NULL, &http_cb_request, NULL,
+ MHD_OPTION_URI_LOG_CALLBACK, &http_cb_log, NULL,
+ MHD_OPTION_NOTIFY_COMPLETED, &http_cb_request_completed, NULL,
+ MHD_OPTION_END);
+ if(NULL==daemon)
+ DIE("MHD_start_daemon failed");
+
+ do
+ {
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+
+ if(NULL == glob_opt.spdy_connection && NULL != glob_opt.spdy2http_str)
+ {
+ glob_opt.spdy_connection = spdy_connect(spdy2http_uri, spdy2http_uri->port, strcmp("https", spdy2http_uri->scheme)==0);
+ if(NULL == glob_opt.spdy_connection && glob_opt.only_proxy)
+ PRINT_INFO("cannot connect to the proxy");
+ }
+
+ FD_ZERO(&rs);
+ FD_ZERO(&ws);
+ FD_ZERO(&es);
+
+ ret = MHD_get_timeout(daemon, &timeoutlong);
+ if(MHD_NO == ret || timeoutlong > 5000)
+ timeout.tv_sec = 5;
+ else
+ {
+ timeout.tv_sec = timeoutlong / 1000;
+ timeout.tv_usec = (timeoutlong % 1000) * 1000;
+ }
+
+ if(MHD_NO == MHD_get_fdset (daemon,
+ &rs,
+ &ws,
+ &es,
+ &maxfd))
+ {
+ PRINT_INFO("MHD_get_fdset error");
+ }
+ assert(-1 != maxfd);
+
+ maxfd_s = spdy_get_selectfdset(
+ &rs,
+ &ws,
+ &es,
+ connections, MAX_SPDY_CONNECTIONS, &spdy_npollfds);
+ if(maxfd_s > maxfd)
+ maxfd = maxfd_s;
+
+ PRINT_INFO2("MHD timeout %lld %lld", (unsigned long long)timeout.tv_sec, (unsigned long long)timeout.tv_usec);
+
+ glob_opt.spdy_data_received = false;
+
+ ret = select(maxfd+1, &rs, &ws, &es, &timeout);
+ PRINT_INFO2("timeout now %lld %lld ret is %i", (unsigned long long)timeout.tv_sec, (unsigned long long)timeout.tv_usec, ret);
+
+ switch(ret)
+ {
+ case -1:
+ PRINT_INFO2("select error: %i", errno);
+ break;
+ case 0:
+ //break;
+ default:
+ PRINT_INFO("run");
+ //MHD_run_from_select(daemon,&rs, &ws, &es); //not closing FDs at some time in past
+ MHD_run(daemon);
+ spdy_run_select(&rs, &ws, &es, connections, spdy_npollfds);
+ if(glob_opt.spdy_data_received)
+ {
+ PRINT_INFO("MHD run again");
+ //MHD_run_from_select(daemon,&rs, &ws, &es); //not closing FDs at some time in past
+ MHD_run(daemon);
+ }
+ break;
+ }
+ }
+ while(run);
+
+ MHD_stop_daemon (daemon);
+
+ //TODO SSL_free brakes
+ spdy_free_connection(glob_opt.spdy_connection);
+
+ connection = glob_opt.spdy_connections_head;
+ while(NULL != connection)
+ {
+ connection_for_delete = connection;
+ connection = connection_for_delete->next;
+ glob_opt.streams_opened -= connection_for_delete->streams_opened;
+ DLL_remove(glob_opt.spdy_connections_head, glob_opt.spdy_connections_tail, connection_for_delete);
+ spdy_free_connection(connection_for_delete);
+ }
+
+ free_uri(spdy2http_uri);
+
+ deinit_parse_uri(&glob_opt.uri_preg);
+
+ SSL_CTX_free(glob_opt.ssl_ctx);
+ ERR_free_strings();
+ EVP_cleanup();
+
+ PRINT_INFO2("spdy streams: %i; http requests: %i", glob_opt.streams_opened, glob_opt.responses_pending);
+ //PRINT_INFO2("memory allocated %zu bytes", glob_opt.global_memory);
+
+ print_stat();
+
+ return 0;
+}
+
+
+void
+display_usage()
+{
+ printf(
+ "Usage: mhd2spdy [-ovs] [-b <SPDY2HTTP-PROXY>] -p <PORT>\n\n"
+ "OPTIONS:\n"
+ " -p, --port Listening port.\n"
+ " -b, --backend-proxy If set, he proxy will send requests to\n"
+ " that SPDY server or proxy. Set the address\n"
+ " in the form 'http://host:port'. Use 'https'\n"
+ " for SPDY over TLS, or 'http' for plain SPDY\n"
+ " communication with the backend.\n"
+ " -o, --only-proxy If set, the proxy will always forward the\n"
+ " requests to the backend proxy. If not set,\n"
+ " the proxy will first try to establsh SPDY\n"
+ " connection to the requested server. If the\n"
+ " server does not support SPDY and TLS, the\n"
+ " backend proxy will be used for the request.\n"
+ " -v, --verbose Print debug information.\n"
+ " -s, --statistics Print simple statistics on exit.\n\n"
+
+ );
+}
+
+
+int
+main (int argc,
+ char *const *argv)
+{
+ int getopt_ret;
+ int option_index;
+ struct option long_options[] = {
+ {"port", required_argument, 0, 'p'},
+ {"backend-proxy", required_argument, 0, 'b'},
+ {"verbose", no_argument, 0, 'v'},
+ {"only-proxy", no_argument, 0, 'o'},
+ {"statistics", no_argument, 0, 's'},
+ {0, 0, 0, 0}
+ };
+
+ while (1)
+ {
+ getopt_ret = getopt_long( argc, argv, "p:b:vos", long_options, &option_index);
+ if (getopt_ret == -1)
+ break;
+
+ switch(getopt_ret)
+ {
+ case 'p':
+ glob_opt.listen_port = atoi(optarg);
+ break;
+
+ case 'b':
+ glob_opt.spdy2http_str = strdup(optarg);
+ if(NULL == glob_opt.spdy2http_str)
+ return 1;
+ break;
+
+ case 'v':
+ glob_opt.verbose = true;
+ break;
+
+ case 'o':
+ glob_opt.only_proxy = true;
+ break;
+
+ case 's':
+ glob_opt.statistics = true;
+ break;
+
+ case 0:
+ PRINT_INFO("0 from getopt");
+ break;
+
+ case '?':
+ display_usage();
+ return 1;
+
+ default:
+ DIE("default from getopt");
+ }
+ }
+
+ if(
+ 0 == glob_opt.listen_port
+ || (glob_opt.only_proxy && NULL == glob_opt.spdy2http_str)
+ )
+ {
+ display_usage();
+ return 1;
+ }
+
+ return run_everything();
+}