• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
spdy_dief(const char * func,const char * msg)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
spdy_diec(const char * func,int error_code)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
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)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
spdy_cb_send(spdylay_session * session,const uint8_t * data,size_t length,int flags,void * user_data)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
spdy_cb_recv(spdylay_session * session,uint8_t * buf,size_t length,int flags,void * user_data)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
spdy_cb_before_ctrl_send(spdylay_session * session,spdylay_frame_type type,spdylay_frame * frame,void * user_data)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
spdy_cb_on_ctrl_recv(spdylay_session * session,spdylay_frame_type type,spdylay_frame * frame,void * user_data)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
spdy_cb_on_stream_close(spdylay_session * session,int32_t stream_id,spdylay_status_code status_code,void * user_data)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
spdy_cb_on_data_chunk_recv(spdylay_session * session,uint8_t flags,int32_t stream_id,const uint8_t * data,size_t len,void * user_data)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
spdy_cb_on_data_recv(spdylay_session * session,uint8_t flags,int32_t stream_id,int32_t length,void * user_data)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
spdy_setup_spdylay_callbacks(spdylay_session_callbacks * callbacks)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
spdy_cb_ssl_select_next_proto(SSL * ssl,unsigned char ** out,unsigned char * outlen,const unsigned char * in,unsigned int inlen,void * arg)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
spdy_ssl_init_ssl_ctx(SSL_CTX * ssl_ctx,uint16_t * spdy_proto_version)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
spdy_ssl_handshake(SSL * ssl,int fd)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
spdy_socket_connect_to(const char * host,uint16_t port)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
spdy_socket_make_non_block(int fd)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
spdy_socket_set_tcp_nodelay(int fd)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
spdy_ctl_select(fd_set * read_fd_set,fd_set * write_fd_set,fd_set * except_fd_set,struct SPDY_Connection * connection)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
spdy_exec_io(struct SPDY_Connection * connection)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 *
spdy_connect(const struct URI * uri,uint16_t port,bool is_tls)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
spdy_free_connection(struct SPDY_Connection * connection)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
spdy_request(const char ** nv,struct Proxy * proxy,bool with_body)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
spdy_get_selectfdset(fd_set * read_fd_set,fd_set * write_fd_set,fd_set * except_fd_set,struct SPDY_Connection * connections[],unsigned int max_size,nfds_t * real_size)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
spdy_run_select(fd_set * read_fd_set,fd_set * write_fd_set,fd_set * except_fd_set,struct SPDY_Connection * connections[],int size)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