Home | History | Annotate | Download | only in examples
      1 /*
      2  *
      3  * Copyright (c) 2012 Tatsuhiro Tsujikawa
      4  *
      5  * Permission is hereby granted, free of charge, to any person obtaining
      6  * a copy of this software and associated documentation files (the
      7  * "Software"), to deal in the Software without restriction, including
      8  * without limitation the rights to use, copy, modify, merge, publish,
      9  * distribute, sublicense, and/or sell copies of the Software, and to
     10  * permit persons to whom the Software is furnished to do so, subject to
     11  * the following conditions:
     12  *
     13  * The above copyright notice and this permission notice shall be
     14  * included in all copies or substantial portions of the Software.
     15  *
     16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     19  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
     20  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
     21  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
     22  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     23  */
     24 
     25 /**
     26  * @file mhd2spdy_spdy.c
     27  * @brief  SPDY part of the proxy. libspdylay is used for the client side.
     28  *         The example spdycli.c from spdylay was used as basis;
     29  *         however, multiple changes were made.
     30  * @author Tatsuhiro Tsujikawa
     31  * @author Andrey Uzunov
     32  */
     33 
     34 #include "mhd2spdy_structures.h"
     35 #include "mhd2spdy_spdy.h"
     36 #include "mhd2spdy_http.h"
     37 
     38 
     39 /*
     40  * Prints error containing the function name |func| and message |msg|
     41  * and exit.
     42  */
     43 static void
     44 spdy_dief(const char *func,
     45           const char *msg)
     46 {
     47   fprintf(stderr, "FATAL: %s: %s\n", func, msg);
     48   exit(EXIT_FAILURE);
     49 }
     50 
     51 
     52 /*
     53  * Prints error containing the function name |func| and error code
     54  * |error_code| and exit.
     55  */
     56 void
     57 spdy_diec(const char *func,
     58           int error_code)
     59 {
     60   fprintf(stderr, "FATAL: %s: error_code=%d, msg=%s\n", func, error_code,
     61           spdylay_strerror(error_code));
     62   exit(EXIT_FAILURE);
     63 }
     64 
     65 
     66 static ssize_t
     67 spdy_cb_data_source_read(spdylay_session *session, int32_t stream_id, uint8_t *buf, size_t length, int *eof, spdylay_data_source *source, void *user_data)
     68 {
     69   (void)session;
     70   (void)stream_id;
     71   (void)user_data;
     72 
     73   ssize_t ret;
     74   assert(NULL != source);
     75   assert(NULL != source->ptr);
     76 	struct Proxy *proxy = (struct Proxy *)(source->ptr);
     77 	void *newbody;
     78 
     79 
     80   if(length < 1)
     81   {
     82     PRINT_INFO("spdy_cb_data_source_read: length is 0");
     83     return 0;
     84 	}
     85 
     86 	if(!proxy->received_body_size)//nothing to write now
     87   {
     88     if(proxy->receiving_done)
     89     {
     90       PRINT_INFO("POST spdy EOF");
     91       *eof = 1;
     92     }
     93       PRINT_INFO("POST SPDYLAY_ERR_DEFERRED");
     94 		return SPDYLAY_ERR_DEFERRED;//TODO SPDYLAY_ERR_DEFERRED should be used
     95   }
     96 
     97 	if(length >= proxy->received_body_size)
     98 	{
     99 		ret = proxy->received_body_size;
    100 		newbody = NULL;
    101 	}
    102 	else
    103 	{
    104 		ret = length;
    105 		if(NULL == (newbody = malloc(proxy->received_body_size - length)))
    106 		{
    107 			PRINT_INFO("no memory");
    108 			return SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE;
    109 		}
    110 		memcpy(newbody, proxy->received_body + length, proxy->received_body_size - length);
    111 	}
    112 	memcpy(buf, proxy->received_body, ret);
    113 	free(proxy->received_body);
    114 	proxy->received_body = newbody;
    115 	proxy->received_body_size -= ret;
    116 
    117   if(0 == proxy->received_body_size && proxy->receiving_done)
    118     {
    119       PRINT_INFO("POST spdy EOF");
    120     *eof = 1;
    121   }
    122 
    123   PRINT_INFO2("given POST bytes to spdylay: %zd", ret);
    124 
    125   return ret;
    126 }
    127 
    128 
    129 /*
    130  * The implementation of spdylay_send_callback type. Here we write
    131  * |data| with size |length| to the network and return the number of
    132  * bytes actually written. See the documentation of
    133  * spdylay_send_callback for the details.
    134  */
    135 static ssize_t
    136 spdy_cb_send(spdylay_session *session,
    137              const uint8_t *data,
    138              size_t length,
    139              int flags,
    140              void *user_data)
    141 {
    142   (void)session;
    143   (void)flags;
    144 
    145   //PRINT_INFO("spdy_cb_send called");
    146   struct SPDY_Connection *connection;
    147   ssize_t rv;
    148   connection = (struct SPDY_Connection*)user_data;
    149   connection->want_io = IO_NONE;
    150 
    151   if(glob_opt.ignore_rst_stream
    152     && 16 == length
    153     && 0x80 == data[0]
    154     && 0x00 == data[2]
    155     && 0x03 == data[3]
    156     )
    157   {
    158     PRINT_INFO2("ignoring RST_STREAM for stream_id %i %i %i %i", data[8], data[9], data[10], data[11]);
    159     glob_opt.ignore_rst_stream = false;
    160     return 16;
    161   }
    162   glob_opt.ignore_rst_stream = false;
    163 
    164   if(connection->is_tls)
    165   {
    166     ERR_clear_error();
    167     rv = SSL_write(connection->ssl, data, length);
    168     if(rv < 0) {
    169       int err = SSL_get_error(connection->ssl, rv);
    170       if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
    171         connection->want_io |= (err == SSL_ERROR_WANT_READ ?
    172                                WANT_READ : WANT_WRITE);
    173         rv = SPDYLAY_ERR_WOULDBLOCK;
    174       } else {
    175         rv = SPDYLAY_ERR_CALLBACK_FAILURE;
    176       }
    177     }
    178   }
    179   else
    180   {
    181     rv = write(connection->fd,
    182             data,
    183             length);
    184 
    185     if (rv < 0)
    186     {
    187       switch(errno)
    188       {
    189         case EAGAIN:
    190   #if EAGAIN != EWOULDBLOCK
    191         case EWOULDBLOCK:
    192   #endif
    193           connection->want_io |= WANT_WRITE;
    194           rv = SPDYLAY_ERR_WOULDBLOCK;
    195           break;
    196 
    197         default:
    198           rv = SPDYLAY_ERR_CALLBACK_FAILURE;
    199       }
    200     }
    201   }
    202 
    203   PRINT_INFO2("%zd bytes written by spdy", rv);
    204 
    205   if(rv > 0)
    206     UPDATE_STAT(glob_stat.spdy_bytes_sent, rv);
    207 
    208   return rv;
    209 }
    210 
    211 
    212 /*
    213  * The implementation of spdylay_recv_callback type. Here we read data
    214  * from the network and write them in |buf|. The capacity of |buf| is
    215  * |length| bytes. Returns the number of bytes stored in |buf|. See
    216  * the documentation of spdylay_recv_callback for the details.
    217  */
    218 static ssize_t
    219 spdy_cb_recv(spdylay_session *session,
    220              uint8_t *buf,
    221              size_t length,
    222              int flags,
    223              void *user_data)
    224 {
    225   (void)session;
    226   (void)flags;
    227 
    228   struct SPDY_Connection *connection;
    229   ssize_t rv;
    230 
    231   connection = (struct SPDY_Connection*)user_data;
    232   //prevent monopolizing everything
    233   if(!(++connection->counter % 10)) return SPDYLAY_ERR_WOULDBLOCK;
    234   connection->want_io = IO_NONE;
    235   if(connection->is_tls)
    236   {
    237     ERR_clear_error();
    238     rv = SSL_read(connection->ssl, buf, length);
    239     if(rv < 0) {
    240       int err = SSL_get_error(connection->ssl, rv);
    241       if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
    242         connection->want_io |= (err == SSL_ERROR_WANT_READ ?
    243                                WANT_READ : WANT_WRITE);
    244         rv = SPDYLAY_ERR_WOULDBLOCK;
    245       } else {
    246         rv = SPDYLAY_ERR_CALLBACK_FAILURE;
    247       }
    248     } else if(rv == 0) {
    249       rv = SPDYLAY_ERR_EOF;
    250     }
    251   }
    252   else
    253   {
    254     rv = read(connection->fd,
    255             buf,
    256             length);
    257 
    258     if (rv < 0)
    259     {
    260       switch(errno)
    261       {
    262         case EAGAIN:
    263   #if EAGAIN != EWOULDBLOCK
    264         case EWOULDBLOCK:
    265   #endif
    266           connection->want_io |= WANT_READ;
    267           rv = SPDYLAY_ERR_WOULDBLOCK;
    268           break;
    269 
    270         default:
    271           rv = SPDYLAY_ERR_CALLBACK_FAILURE;
    272       }
    273     }
    274     else if(rv == 0)
    275       rv = SPDYLAY_ERR_EOF;
    276   }
    277 
    278   if(rv > 0)
    279     UPDATE_STAT(glob_stat.spdy_bytes_received, rv);
    280 
    281   return rv;
    282 }
    283 
    284 
    285 static void
    286 spdy_cb_before_ctrl_send(spdylay_session *session,
    287                     spdylay_frame_type type,
    288                     spdylay_frame *frame,
    289                     void *user_data)
    290 {
    291   (void)user_data;
    292 
    293   int32_t stream_id;
    294   struct Proxy *proxy;
    295 
    296   switch(type) {
    297     case SPDYLAY_SYN_STREAM:
    298       stream_id = frame->syn_stream.stream_id;
    299       proxy = spdylay_session_get_stream_user_data(session, stream_id);
    300       proxy->stream_id = stream_id;
    301       ++glob_opt.streams_opened;
    302       ++proxy->spdy_connection->streams_opened;
    303       PRINT_INFO2("opening stream: str open %i; %s", glob_opt.streams_opened, proxy->url);
    304       break;
    305     case SPDYLAY_RST_STREAM:
    306       //try to ignore duplicate RST_STREAMs
    307       //TODO this will ignore RST_STREAMs also for bogus data
    308       glob_opt.ignore_rst_stream = NULL==spdylay_session_get_stream_user_data(session, frame->rst_stream.stream_id);
    309       PRINT_INFO2("sending RST_STREAM for %i; ignore %i; status %i",
    310         frame->rst_stream.stream_id,
    311         glob_opt.ignore_rst_stream,
    312         frame->rst_stream.status_code);
    313     break;
    314     default:
    315       break;
    316   }
    317 }
    318 
    319 
    320 void
    321 spdy_cb_on_ctrl_recv(spdylay_session *session,
    322                     spdylay_frame_type type,
    323                     spdylay_frame *frame,
    324                     void *user_data)
    325 {
    326   (void)user_data;
    327 
    328   char **nv;
    329   int32_t stream_id;
    330   struct Proxy * proxy;
    331 
    332   switch(type) {
    333     case SPDYLAY_SYN_REPLY:
    334       nv = frame->syn_reply.nv;
    335       stream_id = frame->syn_reply.stream_id;
    336     break;
    337     case SPDYLAY_RST_STREAM:
    338       stream_id = frame->rst_stream.stream_id;
    339     break;
    340     case SPDYLAY_HEADERS:
    341       nv = frame->headers.nv;
    342       stream_id = frame->headers.stream_id;
    343     break;
    344     default:
    345       return;
    346     break;
    347   }
    348 
    349   proxy = spdylay_session_get_stream_user_data(session, stream_id);
    350   if(NULL == proxy)
    351   {
    352     PRINT_INFO2("received frame type %i for unkonwn stream id %i", type, stream_id);
    353     return;
    354     //DIE("no proxy obj");
    355   }
    356 
    357   switch(type) {
    358     case SPDYLAY_SYN_REPLY:
    359       PRINT_INFO2("received headers for %s", proxy->url);
    360       http_create_response(proxy, nv);
    361     break;
    362     case SPDYLAY_RST_STREAM:
    363       PRINT_INFO2("received reset stream for %s", proxy->url);
    364       proxy->spdy_error = true;
    365     break;
    366     case SPDYLAY_HEADERS:
    367       PRINT_INFO2("received headers for %s", proxy->url);
    368       http_create_response(proxy, nv);
    369     break;
    370     default:
    371       return;
    372     break;
    373   }
    374 
    375   glob_opt.spdy_data_received = true;
    376 }
    377 
    378 
    379 /*
    380  * The implementation of spdylay_on_stream_close_callback type. We use
    381  * this function to know the response is fully received. Since we just
    382  * fetch 1 resource in this program, after reception of the response,
    383  * we submit GOAWAY and close the session.
    384  */
    385 static void
    386 spdy_cb_on_stream_close(spdylay_session *session,
    387                        int32_t stream_id,
    388                        spdylay_status_code status_code,
    389                        void *user_data)
    390 {
    391   (void)status_code;
    392   (void)user_data;
    393 
    394   struct Proxy * proxy = spdylay_session_get_stream_user_data(session, stream_id);
    395 
    396   assert(NULL != proxy);
    397 
    398   --glob_opt.streams_opened;
    399   --proxy->spdy_connection->streams_opened;
    400   PRINT_INFO2("closing stream: str opened %i; remove proxy %i", glob_opt.streams_opened, proxy->id);
    401 
    402   DLL_remove(proxy->spdy_connection->proxies_head, proxy->spdy_connection->proxies_tail, proxy);
    403   if(proxy->http_active)
    404   {
    405     proxy->spdy_active = false;
    406   }
    407   else
    408   {
    409     free_proxy(proxy);
    410   }
    411 }
    412 
    413 
    414 /*
    415  * The implementation of spdylay_on_data_chunk_recv_callback type. We
    416  * use this function to print the received response body.
    417  */
    418 static void
    419 spdy_cb_on_data_chunk_recv(spdylay_session *session,
    420                           uint8_t flags,
    421                           int32_t stream_id,
    422                           const uint8_t *data,
    423                           size_t len,
    424                           void *user_data)
    425 {
    426   (void)flags;
    427   (void)user_data;
    428 
    429   struct Proxy *proxy;
    430   proxy = spdylay_session_get_stream_user_data(session, stream_id);
    431 
    432   if(NULL == proxy)
    433   {
    434     PRINT_INFO("proxy in spdy_cb_on_data_chunk_recv is NULL)");
    435     return;
    436 	}
    437 
    438   if(!copy_buffer(data, len, &proxy->http_body, &proxy->http_body_size))
    439   {
    440     //TODO handle it better?
    441     PRINT_INFO("not enough memory (malloc/realloc returned NULL)");
    442     return;
    443   }
    444   /*
    445 	if(NULL == proxy->http_body)
    446 		proxy->http_body = au_malloc(len);
    447   else
    448 		proxy->http_body = realloc(proxy->http_body, proxy->http_body_size + len);
    449 	if(NULL == proxy->http_body)
    450 	{
    451 		PRINT_INFO("not enough memory (realloc returned NULL)");
    452 		return ;
    453 	}
    454 
    455 	memcpy(proxy->http_body + proxy->http_body_size, data, len);
    456 	proxy->http_body_size += len;
    457   */
    458   PRINT_INFO2("received data for %s; %zu bytes", proxy->url, len);
    459   glob_opt.spdy_data_received = true;
    460 }
    461 
    462 
    463 static void
    464 spdy_cb_on_data_recv(spdylay_session *session,
    465 		                 uint8_t flags,
    466                      int32_t stream_id,
    467                      int32_t length,
    468                      void *user_data)
    469 {
    470   (void)length;
    471   (void)user_data;
    472 
    473 	if(flags & SPDYLAY_DATA_FLAG_FIN)
    474 	{
    475     struct Proxy *proxy;
    476     proxy = spdylay_session_get_stream_user_data(session, stream_id);
    477     proxy->done = true;
    478     PRINT_INFO2("last data frame received for %s", proxy->url);
    479 	}
    480 }
    481 
    482 
    483 /*
    484  * Setup callback functions. Spdylay API offers many callback
    485  * functions, but most of them are optional. The send_callback is
    486  * always required. Since we use spdylay_session_recv(), the
    487  * recv_callback is also required.
    488  */
    489 static void
    490 spdy_setup_spdylay_callbacks(spdylay_session_callbacks *callbacks)
    491 {
    492   memset(callbacks, 0, sizeof(spdylay_session_callbacks));
    493   callbacks->send_callback = spdy_cb_send;
    494   callbacks->recv_callback = spdy_cb_recv;
    495   callbacks->before_ctrl_send_callback = spdy_cb_before_ctrl_send;
    496   callbacks->on_ctrl_recv_callback = spdy_cb_on_ctrl_recv;
    497   callbacks->on_stream_close_callback = spdy_cb_on_stream_close;
    498   callbacks->on_data_chunk_recv_callback = spdy_cb_on_data_chunk_recv;
    499   callbacks->on_data_recv_callback = spdy_cb_on_data_recv;
    500 }
    501 
    502 
    503 /*
    504  * Callback function for SSL/TLS NPN. Since this program only supports
    505  * SPDY protocol, if server does not offer SPDY protocol the Spdylay
    506  * library supports, we terminate program.
    507  */
    508 static int
    509 spdy_cb_ssl_select_next_proto(SSL* ssl,
    510                                 unsigned char **out,
    511                                 unsigned char *outlen,
    512                                 const unsigned char *in,
    513                                 unsigned int inlen,
    514                                 void *arg)
    515 {
    516   (void)ssl;
    517 
    518   int rv;
    519   uint16_t *spdy_proto_version;
    520 
    521   /* spdylay_select_next_protocol() selects SPDY protocol version the
    522      Spdylay library supports. */
    523   rv = spdylay_select_next_protocol(out, outlen, in, inlen);
    524   if(rv <= 0) {
    525     PRINT_INFO("Server did not advertise spdy/2 or spdy/3 protocol.");
    526     return rv;
    527   }
    528   spdy_proto_version = (uint16_t*)arg;
    529   *spdy_proto_version = rv;
    530   return SSL_TLSEXT_ERR_OK;
    531 }
    532 
    533 
    534 /*
    535  * Setup SSL context. We pass |spdy_proto_version| to get negotiated
    536  * SPDY protocol version in NPN callback.
    537  */
    538 void
    539 spdy_ssl_init_ssl_ctx(SSL_CTX *ssl_ctx,
    540                       uint16_t *spdy_proto_version)
    541 {
    542   /* Disable SSLv2 and enable all workarounds for buggy servers */
    543   SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2 | SSL_OP_NO_COMPRESSION);
    544   SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
    545   SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
    546   /* Set NPN callback */
    547   SSL_CTX_set_next_proto_select_cb(ssl_ctx, spdy_cb_ssl_select_next_proto,
    548                                    spdy_proto_version);
    549 }
    550 
    551 
    552 static int
    553 spdy_ssl_handshake(SSL *ssl,
    554                    int fd)
    555 {
    556   int rv;
    557 
    558   if(SSL_set_fd(ssl, fd) == 0)
    559     spdy_dief("SSL_set_fd", ERR_error_string(ERR_get_error(), NULL));
    560 
    561   ERR_clear_error();
    562   rv = SSL_connect(ssl);
    563   if(rv <= 0)
    564     PRINT_INFO2("SSL_connect %s", ERR_error_string(ERR_get_error(), NULL));
    565 
    566   return rv;
    567 }
    568 
    569 
    570 /*
    571  * Connects to the host |host| and port |port|.  This function returns
    572  * the file descriptor of the client socket.
    573  */
    574 static int
    575 spdy_socket_connect_to(const char *host,
    576                        uint16_t port)
    577 {
    578   struct addrinfo hints;
    579   int fd = -1;
    580   int rv;
    581   char service[NI_MAXSERV];
    582   struct addrinfo *res, *rp;
    583 
    584   //TODO checks
    585   snprintf(service, sizeof(service), "%u", port);
    586   memset(&hints, 0, sizeof(struct addrinfo));
    587   hints.ai_family = AF_UNSPEC;
    588   hints.ai_socktype = SOCK_STREAM;
    589   rv = getaddrinfo(host, service, &hints, &res);
    590   if(rv != 0)
    591   {
    592 	  printf("%s\n",host);
    593     spdy_dief("getaddrinfo", gai_strerror(rv));
    594   }
    595   for(rp = res; rp; rp = rp->ai_next)
    596   {
    597     fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
    598     if(fd == -1)
    599       continue;
    600     while((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 &&
    601           errno == EINTR);
    602     if(rv == 0)
    603       break;
    604     close(fd);
    605     fd = -1;
    606   }
    607   freeaddrinfo(res);
    608 
    609   return fd;
    610 }
    611 
    612 
    613 static void
    614 spdy_socket_make_non_block(int fd)
    615 {
    616   int flags;
    617   int rv;
    618 
    619   while((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR);
    620 
    621   if(flags == -1)
    622     spdy_dief("fcntl", strerror(errno));
    623 
    624   while((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR);
    625 
    626   if(rv == -1)
    627     spdy_dief("fcntl", strerror(errno));
    628 }
    629 
    630 
    631 /*
    632  * Setting TCP_NODELAY is not mandatory for the SPDY protocol.
    633  */
    634 static void
    635 spdy_socket_set_tcp_nodelay(int fd)
    636 {
    637   int val = 1;
    638   int rv;
    639 
    640   rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val));
    641   if(rv == -1)
    642     spdy_dief("setsockopt", strerror(errno));
    643 }
    644 
    645 /*
    646  * Update |pollfd| based on the state of |connection|.
    647  */
    648  /*
    649 void
    650 spdy_ctl_poll(struct pollfd *pollfd,
    651               struct SPDY_Connection *connection)
    652 {
    653   pollfd->events = 0;
    654   if(spdylay_session_want_read(connection->session) ||
    655      connection->want_io & WANT_READ)
    656   {
    657     pollfd->events |= POLLIN;
    658   }
    659   if(spdylay_session_want_write(connection->session) ||
    660      connection->want_io & WANT_WRITE)
    661   {
    662     pollfd->events |= POLLOUT;
    663   }
    664 }*/
    665 
    666 
    667 /*
    668  * Update |selectfd| based on the state of |connection|.
    669  */
    670 bool
    671 spdy_ctl_select(fd_set * read_fd_set,
    672                 fd_set * write_fd_set,
    673                 fd_set * except_fd_set,
    674                 struct SPDY_Connection *connection)
    675 {
    676   (void)except_fd_set;
    677 
    678   bool ret = false;
    679 
    680   if(spdylay_session_want_read(connection->session) ||
    681      connection->want_io & WANT_READ)
    682   {
    683     FD_SET(connection->fd, read_fd_set);
    684     ret = true;
    685   }
    686   if(spdylay_session_want_write(connection->session) ||
    687      connection->want_io & WANT_WRITE)
    688   {
    689     FD_SET(connection->fd, write_fd_set);
    690     ret = true;
    691   }
    692 
    693   return ret;
    694 }
    695 
    696 
    697 /*
    698  * Performs the network I/O.
    699  */
    700 int
    701 spdy_exec_io(struct SPDY_Connection *connection)
    702 {
    703   int rv;
    704 
    705   rv = spdylay_session_recv(connection->session);
    706   if(rv != 0)
    707   {
    708     PRINT_INFO2("spdylay_session_recv %i", rv);
    709     return rv;
    710   }
    711   rv = spdylay_session_send(connection->session);
    712   if(rv != 0)
    713     PRINT_INFO2("spdylay_session_send %i", rv);
    714 
    715   return rv;
    716 }
    717 
    718 
    719 /*
    720  * Fetches the resource denoted by |uri|.
    721  */
    722 struct SPDY_Connection *
    723 spdy_connect(const struct URI *uri,
    724              uint16_t port,
    725              bool is_tls)
    726 {
    727   spdylay_session_callbacks callbacks;
    728   int fd;
    729   SSL *ssl=NULL;
    730   struct SPDY_Connection * connection = NULL;
    731   int rv;
    732 
    733   spdy_setup_spdylay_callbacks(&callbacks);
    734 
    735   /* Establish connection and setup SSL */
    736   PRINT_INFO2("connecting to %s:%i", uri->host, port);
    737   fd = spdy_socket_connect_to(uri->host, port);
    738   if(fd == -1)
    739   {
    740     PRINT_INFO("Could not open file descriptor");
    741     return NULL;
    742   }
    743 
    744   if(is_tls)
    745   {
    746     ssl = SSL_new(glob_opt.ssl_ctx);
    747     if(ssl == NULL) {
    748       spdy_dief("SSL_new", ERR_error_string(ERR_get_error(), NULL));
    749     }
    750 
    751     //TODO non-blocking
    752     /* To simplify the program, we perform SSL/TLS handshake in blocking
    753        I/O. */
    754     glob_opt.spdy_proto_version = 0;
    755     rv = spdy_ssl_handshake(ssl, fd);
    756     if(rv <= 0 || (glob_opt.spdy_proto_version != 3 && glob_opt.spdy_proto_version != 2))
    757     {
    758       PRINT_INFO("Closing SSL");
    759       //no spdy on the other side
    760       goto free_and_fail;
    761     }
    762   }
    763   else
    764   {
    765     glob_opt.spdy_proto_version = 3;
    766   }
    767 
    768   if(NULL == (connection = au_malloc(sizeof(struct SPDY_Connection))))
    769     goto free_and_fail;
    770 
    771   connection->is_tls = is_tls;
    772   connection->ssl = ssl;
    773   connection->want_io = IO_NONE;
    774   if(NULL == (connection->host = strdup(uri->host)))
    775     goto free_and_fail;
    776 
    777   /* Here make file descriptor non-block */
    778   spdy_socket_make_non_block(fd);
    779   spdy_socket_set_tcp_nodelay(fd);
    780 
    781   PRINT_INFO2("[INFO] SPDY protocol version = %d\n", glob_opt.spdy_proto_version);
    782   rv = spdylay_session_client_new(&(connection->session), glob_opt.spdy_proto_version,
    783                                   &callbacks, connection);
    784   if(rv != 0) {
    785     spdy_diec("spdylay_session_client_new", rv);
    786   }
    787 
    788   connection->fd = fd;
    789 
    790 	return connection;
    791 
    792 	//for GOTO
    793 	free_and_fail:
    794   if(NULL != connection)
    795   {
    796     free(connection->host);
    797     free(connection);
    798   }
    799 
    800   if(is_tls)
    801     SSL_shutdown(ssl);
    802 
    803   close(fd);
    804 
    805   if(is_tls)
    806     SSL_free(ssl);
    807 
    808   return NULL;
    809 }
    810 
    811 
    812 void
    813 spdy_free_connection(struct SPDY_Connection * connection)
    814 {
    815   struct Proxy *proxy;
    816   struct Proxy *proxy_next;
    817 
    818   if(NULL != connection)
    819   {
    820     for(proxy = connection->proxies_head; NULL != proxy; proxy=proxy_next)
    821     {
    822       proxy_next = proxy->next;
    823       DLL_remove(connection->proxies_head, connection->proxies_tail, proxy);
    824       proxy->spdy_active = false;
    825       proxy->spdy_error = true;
    826       PRINT_INFO2("spdy_free_connection for id %i", proxy->id);
    827       if(!proxy->http_active)
    828       {
    829         free_proxy(proxy);
    830       }
    831     }
    832     spdylay_session_del(connection->session);
    833     SSL_free(connection->ssl);
    834     free(connection->host);
    835     free(connection);
    836     //connection->session = NULL;
    837   }
    838 }
    839 
    840 
    841 int
    842 spdy_request(const char **nv,
    843              struct Proxy *proxy,
    844              bool with_body)
    845 {
    846   int ret;
    847   uint16_t port;
    848   struct SPDY_Connection *connection;
    849   spdylay_data_provider post_data;
    850 
    851   if(glob_opt.only_proxy)
    852   {
    853     connection = glob_opt.spdy_connection;
    854   }
    855   else
    856   {
    857     connection = glob_opt.spdy_connections_head;
    858     while(NULL != connection)
    859     {
    860       if(0 == strcasecmp(proxy->uri->host, connection->host))
    861         break;
    862       connection = connection->next;
    863     }
    864 
    865     if(NULL == connection)
    866     {
    867       //connect to host
    868       port = proxy->uri->port;
    869       if(0 == port) port = 443;
    870       connection = spdy_connect(proxy->uri, port, true);
    871       if(NULL != connection)
    872       {
    873         DLL_insert(glob_opt.spdy_connections_head, glob_opt.spdy_connections_tail, connection);
    874         glob_opt.total_spdy_connections++;
    875       }
    876       else
    877         connection = glob_opt.spdy_connection;
    878     }
    879   }
    880 
    881   if(NULL == connection)
    882   {
    883     PRINT_INFO("there is no proxy!");
    884     return -1;
    885   }
    886 
    887   proxy->spdy_connection = connection;
    888   if(with_body)
    889   {
    890     post_data.source.ptr = proxy;
    891     post_data.read_callback = &spdy_cb_data_source_read;
    892     ret = spdylay_submit_request(connection->session, 0, nv, &post_data, proxy);
    893   }
    894   else
    895     ret = spdylay_submit_request(connection->session, 0, nv, NULL, proxy);
    896 
    897   if(ret != 0) {
    898     spdy_diec("spdylay_spdy_submit_request", ret);
    899   }
    900   PRINT_INFO2("adding proxy %i", proxy->id);
    901   if(NULL != connection->proxies_head)
    902     PRINT_INFO2("before proxy %i", connection->proxies_head->id);
    903   DLL_insert(connection->proxies_head, connection->proxies_tail, proxy);
    904 
    905   return ret;
    906 }
    907 
    908 /*
    909 void
    910 spdy_get_pollfdset(struct pollfd fds[],
    911                    struct SPDY_Connection *connections[],
    912                    unsigned int max_size,
    913                    nfds_t *real_size)
    914 {
    915   struct SPDY_Connection *connection;
    916   struct Proxy *proxy;
    917 
    918   *real_size = 0;
    919   if(max_size<1)
    920     return;
    921 
    922   if(NULL != glob_opt.spdy_connection)
    923   {
    924     spdy_ctl_poll(&(fds[*real_size]), glob_opt.spdy_connection);
    925     if(!fds[*real_size].events)
    926     {
    927       //PRINT_INFO("TODO drop connection");
    928       glob_opt.streams_opened -= glob_opt.spdy_connection->streams_opened;
    929 
    930       for(proxy = glob_opt.spdy_connection->proxies_head; NULL != proxy; proxy=proxy->next)
    931       {
    932         abort();
    933         DLL_remove(glob_opt.spdy_connection->proxies_head, glob_opt.spdy_connection->proxies_tail, proxy);
    934         proxy->spdy_active = false;
    935       }
    936       spdy_free_connection(glob_opt.spdy_connection);
    937       glob_opt.spdy_connection = NULL;
    938     }
    939     else
    940     {
    941       fds[*real_size].fd = glob_opt.spdy_connection->fd;
    942       connections[*real_size] = glob_opt.spdy_connection;
    943       ++(*real_size);
    944     }
    945   }
    946 
    947   connection = glob_opt.spdy_connections_head;
    948 
    949   while(NULL != connection && *real_size < max_size)
    950   {
    951     assert(!glob_opt.only_proxy);
    952     spdy_ctl_poll(&(fds[*real_size]), connection);
    953     if(!fds[*real_size].events)
    954     {
    955       //PRINT_INFO("TODO drop connection");
    956       glob_opt.streams_opened -= connection->streams_opened;
    957       DLL_remove(glob_opt.spdy_connections_head, glob_opt.spdy_connections_tail, connection);
    958       glob_opt.total_spdy_connections--;
    959 
    960       for(proxy = connection->proxies_head; NULL != proxy; proxy=proxy->next)
    961       {
    962         abort();
    963         DLL_remove(connection->proxies_head, connection->proxies_tail, proxy);
    964         proxy->spdy_active = false;
    965       }
    966       spdy_free_connection(connection);
    967     }
    968     else
    969     {
    970       fds[*real_size].fd = connection->fd;
    971       connections[*real_size] = connection;
    972       ++(*real_size);
    973     }
    974     connection = connection->next;
    975   }
    976 
    977   //, "TODO max num of conn reached; close something"
    978   assert(NULL == connection);
    979 }
    980 */
    981 
    982 int
    983 spdy_get_selectfdset(fd_set * read_fd_set,
    984                       fd_set * write_fd_set,
    985                       fd_set * except_fd_set,
    986                       struct SPDY_Connection *connections[],
    987                       unsigned int max_size,
    988                       nfds_t *real_size)
    989 {
    990   struct SPDY_Connection *connection;
    991   struct SPDY_Connection *next_connection;
    992   bool ret;
    993   int maxfd = 0;
    994 
    995   *real_size = 0;
    996   if(max_size<1)
    997     return 0;
    998 
    999   if(NULL != glob_opt.spdy_connection)
   1000   {
   1001     ret = spdy_ctl_select(read_fd_set,
   1002 				 write_fd_set,
   1003 				 except_fd_set, glob_opt.spdy_connection);
   1004     if(!ret)
   1005     {
   1006       glob_opt.streams_opened -= glob_opt.spdy_connection->streams_opened;
   1007 
   1008       PRINT_INFO("spdy_free_connection in spdy_get_selectfdset");
   1009       spdy_free_connection(glob_opt.spdy_connection);
   1010       glob_opt.spdy_connection = NULL;
   1011     }
   1012     else
   1013     {
   1014       connections[*real_size] = glob_opt.spdy_connection;
   1015       ++(*real_size);
   1016       if(maxfd < glob_opt.spdy_connection->fd) maxfd = glob_opt.spdy_connection->fd;
   1017     }
   1018   }
   1019 
   1020   connection = glob_opt.spdy_connections_head;
   1021 
   1022   while(NULL != connection && *real_size < max_size)
   1023   {
   1024     assert(!glob_opt.only_proxy);
   1025     ret = spdy_ctl_select(read_fd_set,
   1026 				 write_fd_set,
   1027 				 except_fd_set, connection);
   1028 
   1029     next_connection = connection->next;
   1030     if(!ret)
   1031     {
   1032       glob_opt.streams_opened -= connection->streams_opened;
   1033       DLL_remove(glob_opt.spdy_connections_head, glob_opt.spdy_connections_tail, connection);
   1034       glob_opt.total_spdy_connections--;
   1035 
   1036       PRINT_INFO("spdy_free_connection in spdy_get_selectfdset");
   1037       spdy_free_connection(connection);
   1038     }
   1039     else
   1040     {
   1041       connections[*real_size] = connection;
   1042       ++(*real_size);
   1043       if(maxfd < connection->fd) maxfd = connection->fd;
   1044     }
   1045     connection = next_connection;
   1046   }
   1047 
   1048   //, "TODO max num of conn reached; close something"
   1049   assert(NULL == connection);
   1050 
   1051   return maxfd;
   1052 }
   1053 
   1054 /*
   1055 void
   1056 spdy_run(struct pollfd fds[],
   1057          struct SPDY_Connection *connections[],
   1058          int size)
   1059 {
   1060   int i;
   1061   int ret;
   1062   struct Proxy *proxy;
   1063 
   1064   for(i=0; i<size; ++i)
   1065   {
   1066     //  PRINT_INFO2("exec about to be called for %s", connections[i]->host);
   1067     if(fds[i].revents & (POLLIN | POLLOUT))
   1068     {
   1069       ret = spdy_exec_io(connections[i]);
   1070       //PRINT_INFO2("%i",ret);
   1071       //if((spdy_pollfds[i].revents & POLLHUP) || (spdy_pollfds[0].revents & POLLERR))
   1072       //  PRINT_INFO("SPDY SPDY_Connection error");
   1073 
   1074       //TODO POLLRDHUP
   1075       // always close on ret != 0?
   1076 
   1077       if(0 != ret)
   1078       {
   1079         glob_opt.streams_opened -= connections[i]->streams_opened;
   1080         if(connections[i] == glob_opt.spdy_connection)
   1081         {
   1082           glob_opt.spdy_connection = NULL;
   1083         }
   1084         else
   1085         {
   1086           DLL_remove(glob_opt.spdy_connections_head, glob_opt.spdy_connections_tail, connections[i]);
   1087           glob_opt.total_spdy_connections--;
   1088         }
   1089         for(proxy = connections[i]->proxies_head; NULL != proxy; proxy=proxy->next)
   1090         {
   1091         abort();
   1092           DLL_remove(connections[i]->proxies_head, connections[i]->proxies_tail, proxy);
   1093           proxy->spdy_active = false;
   1094           proxy->spdy_error = true;
   1095           PRINT_INFO2("spdy_free_connection for id %i", proxy->id);
   1096         }
   1097         PRINT_INFO("spdy_free_connection in loop");
   1098         spdy_free_connection(connections[i]);
   1099       }
   1100     }
   1101     else
   1102       PRINT_INFO("not called");
   1103   }
   1104 }
   1105 */
   1106 
   1107 void
   1108 spdy_run_select(fd_set * read_fd_set,
   1109                 fd_set * write_fd_set,
   1110                 fd_set * except_fd_set,
   1111                 struct SPDY_Connection *connections[],
   1112                 int size)
   1113 {
   1114   int i;
   1115   int ret;
   1116 
   1117   for(i=0; i<size; ++i)
   1118   {
   1119     //  PRINT_INFO2("exec about to be called for %s", connections[i]->host);
   1120     if(FD_ISSET(connections[i]->fd, read_fd_set) || FD_ISSET(connections[i]->fd, write_fd_set) || FD_ISSET(connections[i]->fd, except_fd_set))
   1121     {
   1122       //raise(SIGINT);
   1123       ret = spdy_exec_io(connections[i]);
   1124 
   1125       if(0 != ret)
   1126       {
   1127         glob_opt.streams_opened -= connections[i]->streams_opened;
   1128         if(connections[i] == glob_opt.spdy_connection)
   1129         {
   1130           glob_opt.spdy_connection = NULL;
   1131         }
   1132         else
   1133         {
   1134           DLL_remove(glob_opt.spdy_connections_head, glob_opt.spdy_connections_tail, connections[i]);
   1135           glob_opt.total_spdy_connections--;
   1136         }
   1137         PRINT_INFO("in spdy_run_select");
   1138         spdy_free_connection(connections[i]);
   1139       }
   1140     }
   1141     else
   1142     {
   1143       PRINT_INFO("not called");
   1144       //PRINT_INFO2("connection->want_io %i",connections[i]->want_io);
   1145       //PRINT_INFO2("read %i",spdylay_session_want_read(connections[i]->session));
   1146       //PRINT_INFO2("write %i",spdylay_session_want_write(connections[i]->session));
   1147       //raise(SIGINT);
   1148     }
   1149   }
   1150 }
   1151