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_http.c
     20  * @brief  HTTP part of the proxy. libmicrohttpd is used for the server side.
     21  * @author Andrey Uzunov
     22  */
     23 
     24 #include "mhd2spdy_structures.h"
     25 #include "mhd2spdy_http.h"
     26 #include "mhd2spdy_spdy.h"
     27 
     28 
     29 void *
     30 http_cb_log(void * cls,
     31 const char * uri)
     32 {
     33   (void)cls;
     34 
     35   struct HTTP_URI * http_uri;
     36 
     37   PRINT_INFO2("log uri '%s'\n", uri);
     38 
     39   //TODO not freed once in a while
     40   if(NULL == (http_uri = au_malloc(sizeof(struct HTTP_URI ))))
     41     return NULL;
     42   http_uri->uri = strdup(uri);
     43   return http_uri;
     44 }
     45 
     46 
     47 static int
     48 http_cb_iterate(void *cls,
     49                  enum MHD_ValueKind kind,
     50                  const char *name,
     51                  const char *value)
     52 {
     53   (void)kind;
     54 
     55   static char * const forbidden[] = {"Transfer-Encoding",
     56     "Proxy-Connection",
     57     "Keep-Alive",
     58     "Connection"};
     59   static int forbidden_size = 4;
     60   int i;
     61 	struct SPDY_Headers *spdy_headers = (struct SPDY_Headers *)cls;
     62 
     63 	if(0 == strcasecmp(name, "Host"))
     64     spdy_headers->nv[9] = (char *)value;
     65   else
     66   {
     67     for(i=0; i<forbidden_size; ++i)
     68       if(0 == strcasecmp(forbidden[i], name))
     69         return MHD_YES;
     70     spdy_headers->nv[spdy_headers->cnt++] = (char *)name;
     71     spdy_headers->nv[spdy_headers->cnt++] = (char *)value;
     72   }
     73 
     74 	return MHD_YES;
     75 }
     76 
     77 
     78 static ssize_t
     79 http_cb_response (void *cls,
     80                         uint64_t pos,
     81                         char *buffer,
     82                         size_t max)
     83 {
     84   (void)pos;
     85 
     86 	int ret;
     87 	struct Proxy *proxy = (struct Proxy *)cls;
     88 	void *newbody;
     89   const union MHD_ConnectionInfo *info;
     90   int val = 1;
     91 
     92   PRINT_INFO2("http_cb_response for %s", proxy->url);
     93 
     94   if(proxy->spdy_error)
     95     return MHD_CONTENT_READER_END_WITH_ERROR;
     96 
     97 	if(0 == proxy->http_body_size && (proxy->done || !proxy->spdy_active))
     98   {
     99     PRINT_INFO("sent end of stream");
    100     return MHD_CONTENT_READER_END_OF_STREAM;
    101   }
    102 
    103 	if(!proxy->http_body_size)//nothing to write now
    104   {
    105     //flush data
    106     info = MHD_get_connection_info (proxy->http_connection,
    107          MHD_CONNECTION_INFO_CONNECTION_FD);
    108     ret = setsockopt(info->connect_fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val));
    109     if(ret == -1) {
    110       DIE("setsockopt");
    111     }
    112 
    113     PRINT_INFO("FLUSH data");
    114 		return 0;
    115   }
    116 
    117 	if(max >= proxy->http_body_size)
    118 	{
    119 		ret = proxy->http_body_size;
    120 		newbody = NULL;
    121 	}
    122 	else
    123 	{
    124 		ret = max;
    125 		if(NULL == (newbody = au_malloc(proxy->http_body_size - max)))
    126 		{
    127 			PRINT_INFO("no memory");
    128 			return MHD_CONTENT_READER_END_WITH_ERROR;
    129 		}
    130 		memcpy(newbody, proxy->http_body + max, proxy->http_body_size - max);
    131 	}
    132 	memcpy(buffer, proxy->http_body, ret);
    133 	free(proxy->http_body);
    134 	proxy->http_body = newbody;
    135 	proxy->http_body_size -= ret;
    136 
    137 	if(proxy->length >= 0)
    138 	{
    139 		proxy->length -= ret;
    140 	}
    141 
    142 	PRINT_INFO2("response_callback, size: %i",ret);
    143 
    144 	return ret;
    145 }
    146 
    147 
    148 static void
    149 http_cb_response_done(void *cls)
    150 {
    151   (void)cls;
    152   //TODO remove
    153 }
    154 
    155 int
    156 http_cb_request (void *cls,
    157                 struct MHD_Connection *connection,
    158                 const char *url,
    159                 const char *method,
    160                 const char *version,
    161                 const char *upload_data,
    162                 size_t *upload_data_size,
    163                 void **ptr)
    164 {
    165   (void)cls;
    166   (void)url;
    167   (void)upload_data;
    168   (void)upload_data_size;
    169 
    170   int ret;
    171   struct Proxy *proxy;
    172   struct SPDY_Headers spdy_headers;
    173   bool with_body = false;
    174   struct HTTP_URI *http_uri;
    175   const char *header_value;
    176 
    177   if (NULL == ptr || NULL == *ptr)
    178     return MHD_NO;
    179 
    180   http_uri = (struct HTTP_URI *)*ptr;
    181 
    182   if(NULL == http_uri->proxy)
    183   {
    184     //first call for this request
    185     if (0 != strcmp (method, MHD_HTTP_METHOD_GET) && 0 != strcmp (method, MHD_HTTP_METHOD_POST))
    186     {
    187       free(http_uri->uri);
    188       free(http_uri);
    189       PRINT_INFO2("unexpected method %s", method);
    190       return MHD_NO;
    191     }
    192 
    193     if(NULL == (proxy = au_malloc(sizeof(struct Proxy))))
    194     {
    195       free(http_uri->uri);
    196       free(http_uri);
    197       PRINT_INFO("No memory");
    198       return MHD_NO;
    199     }
    200 
    201     ++glob_opt.responses_pending;
    202     proxy->id = rand();
    203     proxy->http_active = true;
    204     proxy->http_connection = connection;
    205     http_uri->proxy = proxy;
    206     return MHD_YES;
    207   }
    208 
    209   proxy = http_uri->proxy;
    210 
    211   if(proxy->spdy_error || proxy->http_error)
    212     return MHD_NO; // handled at different place TODO? leaks?
    213 
    214   if(proxy->spdy_active)
    215   {
    216     if(0 == strcmp (method, MHD_HTTP_METHOD_POST))
    217     {
    218       PRINT_INFO("POST processing");
    219 
    220       int rc= spdylay_session_resume_data(proxy->spdy_connection->session, proxy->stream_id);
    221       PRINT_INFO2("rc is %i stream is %i", rc, proxy->stream_id);
    222       proxy->spdy_connection->want_io |= WANT_WRITE;
    223 
    224       if(0 == *upload_data_size)
    225       {
    226       PRINT_INFO("POST http EOF");
    227         proxy->receiving_done = true;
    228         return MHD_YES;
    229       }
    230 
    231       if(!copy_buffer(upload_data, *upload_data_size, &proxy->received_body, &proxy->received_body_size))
    232       {
    233         //TODO handle it better?
    234         PRINT_INFO("not enough memory (malloc/realloc returned NULL)");
    235         return MHD_NO;
    236       }
    237 
    238       *upload_data_size = 0;
    239 
    240       return MHD_YES;
    241     }
    242 
    243     //already handled
    244     PRINT_INFO("unnecessary call to http_cb_request");
    245     return MHD_YES;
    246   }
    247 
    248   //second call for this request
    249 
    250   PRINT_INFO2("received request for '%s %s %s'", method, http_uri->uri, version);
    251 
    252   proxy->url = http_uri->uri;
    253 
    254   header_value = MHD_lookup_connection_value(connection,
    255     MHD_HEADER_KIND, MHD_HTTP_HEADER_CONTENT_LENGTH);
    256 
    257   with_body = 0 == strcmp (method, MHD_HTTP_METHOD_POST)
    258     && (NULL == header_value || 0 != strcmp ("0", header_value));
    259 
    260   PRINT_INFO2("body will be sent %i", with_body);
    261 
    262   ret = parse_uri(&glob_opt.uri_preg, proxy->url, &proxy->uri);
    263   if(ret != 0)
    264     DIE("parse_uri failed");
    265   proxy->http_uri = http_uri;
    266 
    267   spdy_headers.num = MHD_get_connection_values (connection,
    268                        MHD_HEADER_KIND,
    269                        NULL,
    270                        NULL);
    271   if(NULL == (spdy_headers.nv = au_malloc(((spdy_headers.num + 5) * 2 + 1) * sizeof(char *))))
    272     DIE("no memory");
    273   spdy_headers.nv[0] = ":method";     spdy_headers.nv[1] = method;
    274   spdy_headers.nv[2] = ":path";       spdy_headers.nv[3] = proxy->uri->path_and_more;
    275   spdy_headers.nv[4] = ":version";    spdy_headers.nv[5] = (char *)version;
    276   spdy_headers.nv[6] = ":scheme";     spdy_headers.nv[7] = proxy->uri->scheme;
    277   spdy_headers.nv[8] = ":host";       spdy_headers.nv[9] = NULL;
    278   //nv[14] = NULL;
    279   spdy_headers.cnt = 10;
    280   MHD_get_connection_values (connection,
    281                        MHD_HEADER_KIND,
    282                        &http_cb_iterate,
    283                        &spdy_headers);
    284 
    285   spdy_headers.nv[spdy_headers.cnt] = NULL;
    286   if(NULL == spdy_headers.nv[9])
    287     spdy_headers.nv[9] = proxy->uri->host_and_port;
    288 
    289   if(0 != spdy_request(spdy_headers.nv, proxy, with_body))
    290   {
    291     free(spdy_headers.nv);
    292     //free_proxy(proxy);
    293 
    294     return MHD_NO;
    295   }
    296   free(spdy_headers.nv);
    297 
    298   proxy->http_response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
    299                          4096,
    300                          &http_cb_response,
    301                          proxy,
    302                          &http_cb_response_done);
    303 
    304   if (NULL == proxy->http_response)
    305     DIE("no response");
    306 
    307   if(MHD_NO == MHD_add_response_header (proxy->http_response,
    308                  "Proxy-Connection", "keep-alive"))
    309     PRINT_INFO("SPDY_name_value_add failed: ");
    310   if(MHD_NO == MHD_add_response_header (proxy->http_response,
    311                  "Connection", "Keep-Alive"))
    312     PRINT_INFO("SPDY_name_value_add failed: ");
    313   if(MHD_NO == MHD_add_response_header (proxy->http_response,
    314                  "Keep-Alive", "timeout=5, max=100"))
    315     PRINT_INFO("SPDY_name_value_add failed: ");
    316 
    317   proxy->spdy_active = true;
    318 
    319   return MHD_YES;
    320 }
    321 
    322 
    323 void
    324 http_create_response(struct Proxy* proxy,
    325                      char **nv)
    326 {
    327   size_t i;
    328 
    329   if(!proxy->http_active)
    330     return;
    331 
    332   for(i = 0; nv[i]; i += 2) {
    333     if(0 == strcmp(":status", nv[i]))
    334     {
    335       char tmp[4];
    336       memcpy(&tmp,nv[i+1],3);
    337       tmp[3]=0;
    338       proxy->status = atoi(tmp);
    339       continue;
    340     }
    341     else if(0 == strcmp(":version", nv[i]))
    342     {
    343       proxy->version = nv[i+1];
    344       continue;
    345     }
    346     else if(0 == strcmp("content-length", nv[i]))
    347     {
    348       continue;
    349     }
    350 
    351     char *header = *(nv+i);
    352     if(MHD_NO == MHD_add_response_header (proxy->http_response,
    353                    header, nv[i+1]))
    354     {
    355       PRINT_INFO2("SPDY_name_value_add failed: '%s' '%s'", header, nv[i+1]);
    356     }
    357     PRINT_INFO2("adding '%s: %s'",header, nv[i+1]);
    358   }
    359 
    360   if(MHD_NO == MHD_queue_response (proxy->http_connection, proxy->status, proxy->http_response)){
    361     PRINT_INFO("No queue");
    362     //TODO
    363     //abort();
    364     proxy->http_error = true;
    365   }
    366 
    367   MHD_destroy_response (proxy->http_response);
    368   proxy->http_response = NULL;
    369 }
    370 
    371 void
    372 http_cb_request_completed (void *cls,
    373                                    struct MHD_Connection *connection,
    374                                    void **con_cls,
    375                                    enum MHD_RequestTerminationCode toe)
    376 {
    377   (void)cls;
    378   (void)connection;
    379   struct HTTP_URI *http_uri;
    380   struct Proxy *proxy;
    381 
    382   http_uri = (struct HTTP_URI *)*con_cls;
    383   if(NULL == http_uri)
    384     return;
    385   proxy = (struct Proxy *)http_uri->proxy;
    386   assert(NULL != proxy);
    387 
    388   PRINT_INFO2("http_cb_request_completed %i for %s; id %i",toe, http_uri->uri, proxy->id);
    389 
    390   if(NULL != proxy->http_response)
    391   {
    392     MHD_destroy_response (proxy->http_response);
    393     proxy->http_response = NULL;
    394   }
    395 
    396   if(proxy->spdy_active)
    397   {
    398     proxy->http_active = false;
    399     if(MHD_REQUEST_TERMINATED_COMPLETED_OK != toe)
    400     {
    401       proxy->http_error = true;
    402       if(proxy->stream_id > 0 /*&& NULL != proxy->spdy_connection->session*/)
    403       {
    404         //send RST_STREAM_STATUS_CANCEL
    405         PRINT_INFO2("send rst_stream %i %i",proxy->spdy_active, proxy->stream_id );
    406         spdylay_submit_rst_stream(proxy->spdy_connection->session, proxy->stream_id, 5);
    407       }
    408       /*else
    409       {
    410         DLL_remove(proxy->spdy_connection->proxies_head, proxy->spdy_connection->proxies_tail, proxy);
    411         free_proxy(proxy);
    412       }*/
    413     }
    414   }
    415   else
    416   {
    417     PRINT_INFO2("proxy free http id %i ", proxy->id);
    418     free_proxy(proxy);
    419   }
    420 
    421   --glob_opt.responses_pending;
    422 }
    423