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