aboutsummaryrefslogtreecommitdiff
path: root/src/examples/mhd2spdy_http.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/examples/mhd2spdy_http.c')
-rw-r--r--src/examples/mhd2spdy_http.c422
1 files changed, 422 insertions, 0 deletions
diff --git a/src/examples/mhd2spdy_http.c b/src/examples/mhd2spdy_http.c
new file mode 100644
index 00000000..895f07fa
--- /dev/null
+++ b/src/examples/mhd2spdy_http.c
@@ -0,0 +1,422 @@
+/*
+ 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_http.c
+ * @brief HTTP part of the proxy. libmicrohttpd is used for the server side.
+ * @author Andrey Uzunov
+ */
+
+#include "mhd2spdy_structures.h"
+#include "mhd2spdy_http.h"
+#include "mhd2spdy_spdy.h"
+
+
+void *
+http_cb_log(void * cls,
+const char * uri)
+{
+ (void)cls;
+
+ struct HTTP_URI * http_uri;
+
+ PRINT_INFO2("log uri '%s'\n", uri);
+
+ //TODO not freed once in a while
+ if(NULL == (http_uri = au_malloc(sizeof(struct HTTP_URI ))))
+ return NULL;
+ http_uri->uri = strdup(uri);
+ return http_uri;
+}
+
+
+static int
+http_cb_iterate(void *cls,
+ enum MHD_ValueKind kind,
+ const char *name,
+ const char *value)
+{
+ (void)kind;
+
+ static char * const forbidden[] = {"Transfer-Encoding",
+ "Proxy-Connection",
+ "Keep-Alive",
+ "Connection"};
+ static int forbidden_size = 4;
+ int i;
+ struct SPDY_Headers *spdy_headers = (struct SPDY_Headers *)cls;
+
+ if(0 == strcasecmp(name, "Host"))
+ spdy_headers->nv[9] = (char *)value;
+ else
+ {
+ for(i=0; i<forbidden_size; ++i)
+ if(0 == strcasecmp(forbidden[i], name))
+ return MHD_YES;
+ spdy_headers->nv[spdy_headers->cnt++] = (char *)name;
+ spdy_headers->nv[spdy_headers->cnt++] = (char *)value;
+ }
+
+ return MHD_YES;
+}
+
+
+static ssize_t
+http_cb_response (void *cls,
+ uint64_t pos,
+ char *buffer,
+ size_t max)
+{
+ (void)pos;
+
+ int ret;
+ struct Proxy *proxy = (struct Proxy *)cls;
+ void *newbody;
+ const union MHD_ConnectionInfo *info;
+ int val = 1;
+
+ PRINT_INFO2("http_cb_response for %s", proxy->url);
+
+ if(proxy->spdy_error)
+ return MHD_CONTENT_READER_END_WITH_ERROR;
+
+ if(0 == proxy->http_body_size && (proxy->done || !proxy->spdy_active))
+ {
+ PRINT_INFO("sent end of stream");
+ return MHD_CONTENT_READER_END_OF_STREAM;
+ }
+
+ if(!proxy->http_body_size)//nothing to write now
+ {
+ //flush data
+ info = MHD_get_connection_info (proxy->http_connection,
+ MHD_CONNECTION_INFO_CONNECTION_FD);
+ ret = setsockopt(info->connect_fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val));
+ if(ret == -1) {
+ DIE("setsockopt");
+ }
+
+ PRINT_INFO("FLUSH data");
+ return 0;
+ }
+
+ if(max >= proxy->http_body_size)
+ {
+ ret = proxy->http_body_size;
+ newbody = NULL;
+ }
+ else
+ {
+ ret = max;
+ if(NULL == (newbody = au_malloc(proxy->http_body_size - max)))
+ {
+ PRINT_INFO("no memory");
+ return MHD_CONTENT_READER_END_WITH_ERROR;
+ }
+ memcpy(newbody, proxy->http_body + max, proxy->http_body_size - max);
+ }
+ memcpy(buffer, proxy->http_body, ret);
+ free(proxy->http_body);
+ proxy->http_body = newbody;
+ proxy->http_body_size -= ret;
+
+ if(proxy->length >= 0)
+ {
+ proxy->length -= ret;
+ }
+
+ PRINT_INFO2("response_callback, size: %i",ret);
+
+ return ret;
+}
+
+
+static void
+http_cb_response_done(void *cls)
+{
+ (void)cls;
+ //TODO remove
+}
+
+int
+http_cb_request (void *cls,
+ struct MHD_Connection *connection,
+ const char *url,
+ const char *method,
+ const char *version,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **ptr)
+{
+ (void)cls;
+ (void)url;
+ (void)upload_data;
+ (void)upload_data_size;
+
+ int ret;
+ struct Proxy *proxy;
+ struct SPDY_Headers spdy_headers;
+ bool with_body = false;
+ struct HTTP_URI *http_uri;
+ const char *header_value;
+
+ if (NULL == ptr || NULL == *ptr)
+ return MHD_NO;
+
+ http_uri = (struct HTTP_URI *)*ptr;
+
+ if(NULL == http_uri->proxy)
+ {
+ //first call for this request
+ if (0 != strcmp (method, MHD_HTTP_METHOD_GET) && 0 != strcmp (method, MHD_HTTP_METHOD_POST))
+ {
+ free(http_uri->uri);
+ free(http_uri);
+ PRINT_INFO2("unexpected method %s", method);
+ return MHD_NO;
+ }
+
+ if(NULL == (proxy = au_malloc(sizeof(struct Proxy))))
+ {
+ free(http_uri->uri);
+ free(http_uri);
+ PRINT_INFO("No memory");
+ return MHD_NO;
+ }
+
+ ++glob_opt.responses_pending;
+ proxy->id = rand();
+ proxy->http_active = true;
+ proxy->http_connection = connection;
+ http_uri->proxy = proxy;
+ return MHD_YES;
+ }
+
+ proxy = http_uri->proxy;
+
+ if(proxy->spdy_error || proxy->http_error)
+ return MHD_NO; // handled at different place TODO? leaks?
+
+ if(proxy->spdy_active)
+ {
+ if(0 == strcmp (method, MHD_HTTP_METHOD_POST))
+ {
+ PRINT_INFO("POST processing");
+
+ int rc= spdylay_session_resume_data(proxy->spdy_connection->session, proxy->stream_id);
+ PRINT_INFO2("rc is %i stream is %i", rc, proxy->stream_id);
+ proxy->spdy_connection->want_io |= WANT_WRITE;
+
+ if(0 == *upload_data_size)
+ {
+ PRINT_INFO("POST http EOF");
+ proxy->receiving_done = true;
+ return MHD_YES;
+ }
+
+ if(!copy_buffer(upload_data, *upload_data_size, &proxy->received_body, &proxy->received_body_size))
+ {
+ //TODO handle it better?
+ PRINT_INFO("not enough memory (malloc/realloc returned NULL)");
+ return MHD_NO;
+ }
+
+ *upload_data_size = 0;
+
+ return MHD_YES;
+ }
+
+ //already handled
+ PRINT_INFO("unnecessary call to http_cb_request");
+ return MHD_YES;
+ }
+
+ //second call for this request
+
+ PRINT_INFO2("received request for '%s %s %s'", method, http_uri->uri, version);
+
+ proxy->url = http_uri->uri;
+
+ header_value = MHD_lookup_connection_value(connection,
+ MHD_HEADER_KIND, MHD_HTTP_HEADER_CONTENT_LENGTH);
+
+ with_body = 0 == strcmp (method, MHD_HTTP_METHOD_POST)
+ && (NULL == header_value || 0 != strcmp ("0", header_value));
+
+ PRINT_INFO2("body will be sent %i", with_body);
+
+ ret = parse_uri(&glob_opt.uri_preg, proxy->url, &proxy->uri);
+ if(ret != 0)
+ DIE("parse_uri failed");
+ proxy->http_uri = http_uri;
+
+ spdy_headers.num = MHD_get_connection_values (connection,
+ MHD_HEADER_KIND,
+ NULL,
+ NULL);
+ if(NULL == (spdy_headers.nv = au_malloc(((spdy_headers.num + 5) * 2 + 1) * sizeof(char *))))
+ DIE("no memory");
+ spdy_headers.nv[0] = ":method"; spdy_headers.nv[1] = method;
+ spdy_headers.nv[2] = ":path"; spdy_headers.nv[3] = proxy->uri->path_and_more;
+ spdy_headers.nv[4] = ":version"; spdy_headers.nv[5] = (char *)version;
+ spdy_headers.nv[6] = ":scheme"; spdy_headers.nv[7] = proxy->uri->scheme;
+ spdy_headers.nv[8] = ":host"; spdy_headers.nv[9] = NULL;
+ //nv[14] = NULL;
+ spdy_headers.cnt = 10;
+ MHD_get_connection_values (connection,
+ MHD_HEADER_KIND,
+ &http_cb_iterate,
+ &spdy_headers);
+
+ spdy_headers.nv[spdy_headers.cnt] = NULL;
+ if(NULL == spdy_headers.nv[9])
+ spdy_headers.nv[9] = proxy->uri->host_and_port;
+
+ if(0 != spdy_request(spdy_headers.nv, proxy, with_body))
+ {
+ free(spdy_headers.nv);
+ //free_proxy(proxy);
+
+ return MHD_NO;
+ }
+ free(spdy_headers.nv);
+
+ proxy->http_response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
+ 4096,
+ &http_cb_response,
+ proxy,
+ &http_cb_response_done);
+
+ if (NULL == proxy->http_response)
+ DIE("no response");
+
+ if(MHD_NO == MHD_add_response_header (proxy->http_response,
+ "Proxy-Connection", "keep-alive"))
+ PRINT_INFO("SPDY_name_value_add failed: ");
+ if(MHD_NO == MHD_add_response_header (proxy->http_response,
+ "Connection", "Keep-Alive"))
+ PRINT_INFO("SPDY_name_value_add failed: ");
+ if(MHD_NO == MHD_add_response_header (proxy->http_response,
+ "Keep-Alive", "timeout=5, max=100"))
+ PRINT_INFO("SPDY_name_value_add failed: ");
+
+ proxy->spdy_active = true;
+
+ return MHD_YES;
+}
+
+
+void
+http_create_response(struct Proxy* proxy,
+ char **nv)
+{
+ size_t i;
+
+ if(!proxy->http_active)
+ return;
+
+ for(i = 0; nv[i]; i += 2) {
+ if(0 == strcmp(":status", nv[i]))
+ {
+ char tmp[4];
+ memcpy(&tmp,nv[i+1],3);
+ tmp[3]=0;
+ proxy->status = atoi(tmp);
+ continue;
+ }
+ else if(0 == strcmp(":version", nv[i]))
+ {
+ proxy->version = nv[i+1];
+ continue;
+ }
+ else if(0 == strcmp("content-length", nv[i]))
+ {
+ continue;
+ }
+
+ char *header = *(nv+i);
+ if(MHD_NO == MHD_add_response_header (proxy->http_response,
+ header, nv[i+1]))
+ {
+ PRINT_INFO2("SPDY_name_value_add failed: '%s' '%s'", header, nv[i+1]);
+ }
+ PRINT_INFO2("adding '%s: %s'",header, nv[i+1]);
+ }
+
+ if(MHD_NO == MHD_queue_response (proxy->http_connection, proxy->status, proxy->http_response)){
+ PRINT_INFO("No queue");
+ //TODO
+ //abort();
+ proxy->http_error = true;
+ }
+
+ MHD_destroy_response (proxy->http_response);
+ proxy->http_response = NULL;
+}
+
+void
+http_cb_request_completed (void *cls,
+ struct MHD_Connection *connection,
+ void **con_cls,
+ enum MHD_RequestTerminationCode toe)
+{
+ (void)cls;
+ (void)connection;
+ struct HTTP_URI *http_uri;
+ struct Proxy *proxy;
+
+ http_uri = (struct HTTP_URI *)*con_cls;
+ if(NULL == http_uri)
+ return;
+ proxy = (struct Proxy *)http_uri->proxy;
+ assert(NULL != proxy);
+
+ PRINT_INFO2("http_cb_request_completed %i for %s; id %i",toe, http_uri->uri, proxy->id);
+
+ if(NULL != proxy->http_response)
+ {
+ MHD_destroy_response (proxy->http_response);
+ proxy->http_response = NULL;
+ }
+
+ if(proxy->spdy_active)
+ {
+ proxy->http_active = false;
+ if(MHD_REQUEST_TERMINATED_COMPLETED_OK != toe)
+ {
+ proxy->http_error = true;
+ if(proxy->stream_id > 0 /*&& NULL != proxy->spdy_connection->session*/)
+ {
+ //send RST_STREAM_STATUS_CANCEL
+ PRINT_INFO2("send rst_stream %i %i",proxy->spdy_active, proxy->stream_id );
+ spdylay_submit_rst_stream(proxy->spdy_connection->session, proxy->stream_id, 5);
+ }
+ /*else
+ {
+ DLL_remove(proxy->spdy_connection->proxies_head, proxy->spdy_connection->proxies_tail, proxy);
+ free_proxy(proxy);
+ }*/
+ }
+ }
+ else
+ {
+ PRINT_INFO2("proxy free http id %i ", proxy->id);
+ free_proxy(proxy);
+ }
+
+ --glob_opt.responses_pending;
+}