1 /*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2012 Andrey Uzunov
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /**
20 * @file request_response.c
21 * @brief tests new connection callback. spdycli.c
22 * code is reused here
23 * @author Andrey Uzunov
24 * @author Tatsuhiro Tsujikawa
25 */
26
27 //TODO child exits with ret val 1 sometimes
28
29 #include "platform.h"
30 #include "microspdy.h"
31 #include <sys/wait.h>
32 #include "common.h"
33
34 #define RESPONSE_BODY "<html><body><b>Hi, this is libmicrospdy!</b></body></html>"
35
36 #define CLS "anything"
37
38 int port;
39 int loop = 1;
40
41 pid_t parent;
42 pid_t child;
43
44 int
spdylay_printf(const char * format,...)45 spdylay_printf(const char *format, ...)
46 {
47 (void)format;
48
49 return 0;
50 }
51
52 int
spdylay_fprintf(FILE * stream,const char * format,...)53 spdylay_fprintf(FILE *stream, const char *format, ...)
54 {
55 (void)stream;
56 (void)format;
57
58 return 0;
59 }
60
61 void
killchild(int pid,char * message)62 killchild(int pid, char *message)
63 {
64 printf("%s\n",message);
65 kill(pid, SIGKILL);
66 exit(1);
67 }
68
69 void
killparent(int pid,char * message)70 killparent(int pid, char *message)
71 {
72 printf("%s\n",message);
73 kill(pid, SIGKILL);
74 _exit(2);
75 }
76
77
78 /*****
79 * start of code needed to utilize spdylay
80 */
81
82 #include <stdint.h>
83 #include <stdlib.h>
84 #include <unistd.h>
85 #include <fcntl.h>
86 #include <sys/types.h>
87 #include <sys/socket.h>
88 #include <netdb.h>
89 #include <netinet/in.h>
90 #include <netinet/tcp.h>
91 #include <poll.h>
92 #include <signal.h>
93 #include <stdio.h>
94 #include <assert.h>
95
96 #include <spdylay/spdylay.h>
97
98 #include <openssl/ssl.h>
99 #include <openssl/err.h>
100
101 enum {
102 IO_NONE,
103 WANT_READ,
104 WANT_WRITE
105 };
106
107 struct Connection {
108 SSL *ssl;
109 spdylay_session *session;
110 /* WANT_READ if SSL connection needs more input; or WANT_WRITE if it
111 needs more output; or IO_NONE. This is necessary because SSL/TLS
112 re-negotiation is possible at any time. Spdylay API offers
113 similar functions like spdylay_session_want_read() and
114 spdylay_session_want_write() but they do not take into account
115 SSL connection. */
116 int want_io;
117 };
118
119 struct Request {
120 char *host;
121 uint16_t port;
122 /* In this program, path contains query component as well. */
123 char *path;
124 /* This is the concatenation of host and port with ":" in
125 between. */
126 char *hostport;
127 /* Stream ID for this request. */
128 int32_t stream_id;
129 /* The gzip stream inflater for the compressed response. */
130 spdylay_gzip *inflater;
131 };
132
133 struct URI {
134 const char *host;
135 size_t hostlen;
136 uint16_t port;
137 /* In this program, path contains query component as well. */
138 const char *path;
139 size_t pathlen;
140 const char *hostport;
141 size_t hostportlen;
142 };
143
144 /*
145 * Returns copy of string |s| with the length |len|. The returned
146 * string is NULL-terminated.
147 */
strcopy(const char * s,size_t len)148 static char* strcopy(const char *s, size_t len)
149 {
150 char *dst;
151 dst = malloc(len+1);
152 if (NULL == dst)
153 abort ();
154 memcpy(dst, s, len);
155 dst[len] = '\0';
156 return dst;
157 }
158
159 /*
160 * Prints error message |msg| and exit.
161 */
die(const char * msg)162 static void die(const char *msg)
163 {
164 fprintf(stderr, "FATAL: %s\n", msg);
165 exit(EXIT_FAILURE);
166 }
167
168 /*
169 * Prints error containing the function name |func| and message |msg|
170 * and exit.
171 */
dief(const char * func,const char * msg)172 static void dief(const char *func, const char *msg)
173 {
174 fprintf(stderr, "FATAL: %s: %s\n", func, msg);
175 exit(EXIT_FAILURE);
176 }
177
178 /*
179 * Prints error containing the function name |func| and error code
180 * |error_code| and exit.
181 */
diec(const char * func,int error_code)182 static void diec(const char *func, int error_code)
183 {
184 fprintf(stderr, "FATAL: %s: error_code=%d, msg=%s\n", func, error_code,
185 spdylay_strerror(error_code));
186 exit(EXIT_FAILURE);
187 }
188
189 /*
190 * Check response is content-encoding: gzip. We need this because SPDY
191 * client is required to support gzip.
192 */
check_gzip(struct Request * req,char ** nv)193 static void check_gzip(struct Request *req, char **nv)
194 {
195 int gzip = 0;
196 size_t i;
197 for(i = 0; nv[i]; i += 2) {
198 if(strcmp("content-encoding", nv[i]) == 0) {
199 gzip = strcmp("gzip", nv[i+1]) == 0;
200 break;
201 }
202 }
203 if(gzip) {
204 int rv;
205 if(req->inflater) {
206 return;
207 }
208 rv = spdylay_gzip_inflate_new(&req->inflater);
209 if(rv != 0) {
210 die("Can't allocate inflate stream.");
211 }
212 }
213 }
214
215 /*
216 * The implementation of spdylay_send_callback type. Here we write
217 * |data| with size |length| to the network and return the number of
218 * bytes actually written. See the documentation of
219 * spdylay_send_callback for the details.
220 */
send_callback(spdylay_session * session,const uint8_t * data,size_t length,int flags,void * user_data)221 static ssize_t send_callback(spdylay_session *session,
222 const uint8_t *data, size_t length, int flags,
223 void *user_data)
224 {
225 (void)session;
226 (void)flags;
227
228 struct Connection *connection;
229 ssize_t rv;
230 connection = (struct Connection*)user_data;
231 connection->want_io = IO_NONE;
232 ERR_clear_error();
233 rv = SSL_write(connection->ssl, data, length);
234 if(rv < 0) {
235 int err = SSL_get_error(connection->ssl, rv);
236 if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
237 connection->want_io = (err == SSL_ERROR_WANT_READ ?
238 WANT_READ : WANT_WRITE);
239 rv = SPDYLAY_ERR_WOULDBLOCK;
240 } else {
241 rv = SPDYLAY_ERR_CALLBACK_FAILURE;
242 }
243 }
244 return rv;
245 }
246
247 /*
248 * The implementation of spdylay_recv_callback type. Here we read data
249 * from the network and write them in |buf|. The capacity of |buf| is
250 * |length| bytes. Returns the number of bytes stored in |buf|. See
251 * the documentation of spdylay_recv_callback for the details.
252 */
recv_callback(spdylay_session * session,uint8_t * buf,size_t length,int flags,void * user_data)253 static ssize_t recv_callback(spdylay_session *session,
254 uint8_t *buf, size_t length, int flags,
255 void *user_data)
256 {
257 (void)session;
258 (void)flags;
259
260 struct Connection *connection;
261 ssize_t rv;
262 connection = (struct Connection*)user_data;
263 connection->want_io = IO_NONE;
264 ERR_clear_error();
265 rv = SSL_read(connection->ssl, buf, length);
266 if(rv < 0) {
267 int err = SSL_get_error(connection->ssl, rv);
268 if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
269 connection->want_io = (err == SSL_ERROR_WANT_READ ?
270 WANT_READ : WANT_WRITE);
271 rv = SPDYLAY_ERR_WOULDBLOCK;
272 } else {
273 rv = SPDYLAY_ERR_CALLBACK_FAILURE;
274 }
275 } else if(rv == 0) {
276 rv = SPDYLAY_ERR_EOF;
277 }
278 return rv;
279 }
280
281 /*
282 * The implementation of spdylay_before_ctrl_send_callback type. We
283 * use this function to get stream ID of the request. This is because
284 * stream ID is not known when we submit the request
285 * (spdylay_submit_request).
286 */
before_ctrl_send_callback(spdylay_session * session,spdylay_frame_type type,spdylay_frame * frame,void * user_data)287 static void before_ctrl_send_callback(spdylay_session *session,
288 spdylay_frame_type type,
289 spdylay_frame *frame,
290 void *user_data)
291 {
292 (void)user_data;
293
294 if(type == SPDYLAY_SYN_STREAM) {
295 struct Request *req;
296 int stream_id = frame->syn_stream.stream_id;
297 req = spdylay_session_get_stream_user_data(session, stream_id);
298 if(req && req->stream_id == -1) {
299 req->stream_id = stream_id;
300 spdylay_printf("[INFO] Stream ID = %d\n", stream_id);
301 }
302 }
303 }
304
on_ctrl_send_callback(spdylay_session * session,spdylay_frame_type type,spdylay_frame * frame,void * user_data)305 static void on_ctrl_send_callback(spdylay_session *session,
306 spdylay_frame_type type,
307 spdylay_frame *frame, void *user_data)
308 {
309 (void)user_data;
310
311 char **nv;
312 const char *name = NULL;
313 int32_t stream_id;
314 size_t i;
315 switch(type) {
316 case SPDYLAY_SYN_STREAM:
317 nv = frame->syn_stream.nv;
318 name = "SYN_STREAM";
319 stream_id = frame->syn_stream.stream_id;
320 break;
321 default:
322 break;
323 }
324 if(name && spdylay_session_get_stream_user_data(session, stream_id)) {
325 spdylay_printf("[INFO] C ----------------------------> S (%s)\n", name);
326 for(i = 0; nv[i]; i += 2) {
327 spdylay_printf(" %s: %s\n", nv[i], nv[i+1]);
328 }
329 }
330 }
331
on_ctrl_recv_callback(spdylay_session * session,spdylay_frame_type type,spdylay_frame * frame,void * user_data)332 static void on_ctrl_recv_callback(spdylay_session *session,
333 spdylay_frame_type type,
334 spdylay_frame *frame, void *user_data)
335 {
336 (void)user_data;
337
338 struct Request *req;
339 char **nv;
340 const char *name = NULL;
341 int32_t stream_id;
342 size_t i;
343 switch(type) {
344 case SPDYLAY_SYN_REPLY:
345 nv = frame->syn_reply.nv;
346 name = "SYN_REPLY";
347 stream_id = frame->syn_reply.stream_id;
348 break;
349 case SPDYLAY_HEADERS:
350 nv = frame->headers.nv;
351 name = "HEADERS";
352 stream_id = frame->headers.stream_id;
353 break;
354 default:
355 break;
356 }
357 if(!name) {
358 return;
359 }
360 req = spdylay_session_get_stream_user_data(session, stream_id);
361 if(req) {
362 check_gzip(req, nv);
363 spdylay_printf("[INFO] C <---------------------------- S (%s)\n", name);
364 for(i = 0; nv[i]; i += 2) {
365 spdylay_printf(" %s: %s\n", nv[i], nv[i+1]);
366 }
367 }
368 }
369
370 /*
371 * The implementation of spdylay_on_stream_close_callback type. We use
372 * this function to know the response is fully received. Since we just
373 * fetch 1 resource in this program, after reception of the response,
374 * we submit GOAWAY and close the session.
375 */
on_stream_close_callback(spdylay_session * session,int32_t stream_id,spdylay_status_code status_code,void * user_data)376 static void on_stream_close_callback(spdylay_session *session,
377 int32_t stream_id,
378 spdylay_status_code status_code,
379 void *user_data)
380 {
381 (void)user_data;
382 (void)status_code;
383 struct Request *req;
384 req = spdylay_session_get_stream_user_data(session, stream_id);
385 if(req) {
386 int rv;
387 rv = spdylay_submit_goaway(session, SPDYLAY_GOAWAY_OK);
388 if(rv != 0) {
389 diec("spdylay_submit_goaway", rv);
390 }
391 }
392 }
393
394 #define MAX_OUTLEN 4096
395
396 /*
397 * The implementation of spdylay_on_data_chunk_recv_callback type. We
398 * use this function to print the received response body.
399 */
on_data_chunk_recv_callback(spdylay_session * session,uint8_t flags,int32_t stream_id,const uint8_t * data,size_t len,void * user_data)400 static void on_data_chunk_recv_callback(spdylay_session *session, uint8_t flags,
401 int32_t stream_id,
402 const uint8_t *data, size_t len,
403 void *user_data)
404 {
405 (void)user_data;
406 (void)flags;
407
408 struct Request *req;
409 req = spdylay_session_get_stream_user_data(session, stream_id);
410 if(req) {
411 spdylay_printf("[INFO] C <---------------------------- S (DATA)\n");
412 spdylay_printf(" %lu bytes\n", (unsigned long int)len);
413 if(req->inflater) {
414 while(len > 0) {
415 uint8_t out[MAX_OUTLEN];
416 size_t outlen = MAX_OUTLEN;
417 size_t tlen = len;
418 int rv;
419 rv = spdylay_gzip_inflate(req->inflater, out, &outlen, data, &tlen);
420 if(rv == -1) {
421 spdylay_submit_rst_stream(session, stream_id, SPDYLAY_INTERNAL_ERROR);
422 break;
423 }
424 fwrite(out, 1, outlen, stdout);
425 data += tlen;
426 len -= tlen;
427 }
428 } else {
429 /* TODO add support gzip */
430 fwrite(data, 1, len, stdout);
431
432 //check if the data is correct
433 if(strcmp(RESPONSE_BODY, (char *)data) != 0)
434 killparent(parent, "\nreceived data is not the same");
435 }
436 spdylay_printf("\n");
437 }
438 }
439
440 /*
441 * Setup callback functions. Spdylay API offers many callback
442 * functions, but most of them are optional. The send_callback is
443 * always required. Since we use spdylay_session_recv(), the
444 * recv_callback is also required.
445 */
setup_spdylay_callbacks(spdylay_session_callbacks * callbacks)446 static void setup_spdylay_callbacks(spdylay_session_callbacks *callbacks)
447 {
448 memset(callbacks, 0, sizeof(spdylay_session_callbacks));
449 callbacks->send_callback = send_callback;
450 callbacks->recv_callback = recv_callback;
451 callbacks->before_ctrl_send_callback = before_ctrl_send_callback;
452 callbacks->on_ctrl_send_callback = on_ctrl_send_callback;
453 callbacks->on_ctrl_recv_callback = on_ctrl_recv_callback;
454 callbacks->on_stream_close_callback = on_stream_close_callback;
455 callbacks->on_data_chunk_recv_callback = on_data_chunk_recv_callback;
456 }
457
458 /*
459 * Callback function for SSL/TLS NPN. Since this program only supports
460 * SPDY protocol, if server does not offer SPDY protocol the Spdylay
461 * library supports, we terminate program.
462 */
select_next_proto_cb(SSL * ssl,unsigned char ** out,unsigned char * outlen,const unsigned char * in,unsigned int inlen,void * arg)463 static int select_next_proto_cb(SSL* ssl,
464 unsigned char **out, unsigned char *outlen,
465 const unsigned char *in, unsigned int inlen,
466 void *arg)
467 {
468 (void)ssl;
469
470 int rv;
471 uint16_t *spdy_proto_version;
472 /* spdylay_select_next_protocol() selects SPDY protocol version the
473 Spdylay library supports. */
474 rv = spdylay_select_next_protocol(out, outlen, in, inlen);
475 if(rv <= 0) {
476 die("Server did not advertise spdy/2 or spdy/3 protocol.");
477 }
478 spdy_proto_version = (uint16_t*)arg;
479 *spdy_proto_version = rv;
480 return SSL_TLSEXT_ERR_OK;
481 }
482
483 /*
484 * Setup SSL context. We pass |spdy_proto_version| to get negotiated
485 * SPDY protocol version in NPN callback.
486 */
init_ssl_ctx(SSL_CTX * ssl_ctx,uint16_t * spdy_proto_version)487 static void init_ssl_ctx(SSL_CTX *ssl_ctx, uint16_t *spdy_proto_version)
488 {
489 /* Disable SSLv2 and enable all workarounds for buggy servers */
490 SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2);
491 SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
492 SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
493 /* Set NPN callback */
494 SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb,
495 spdy_proto_version);
496 }
497
ssl_handshake(SSL * ssl,int fd)498 static void ssl_handshake(SSL *ssl, int fd)
499 {
500 int rv;
501 if(SSL_set_fd(ssl, fd) == 0) {
502 dief("SSL_set_fd", ERR_error_string(ERR_get_error(), NULL));
503 }
504 ERR_clear_error();
505 rv = SSL_connect(ssl);
506 if(rv <= 0) {
507 dief("SSL_connect", ERR_error_string(ERR_get_error(), NULL));
508 }
509 }
510
511 /*
512 * Connects to the host |host| and port |port|. This function returns
513 * the file descriptor of the client socket.
514 */
connect_to(const char * host,uint16_t port)515 static int connect_to(const char *host, uint16_t port)
516 {
517 struct addrinfo hints;
518 int fd = -1;
519 int rv;
520 char service[NI_MAXSERV];
521 struct addrinfo *res, *rp;
522 snprintf(service, sizeof(service), "%u", port);
523 memset(&hints, 0, sizeof(struct addrinfo));
524 hints.ai_family = AF_UNSPEC;
525 hints.ai_socktype = SOCK_STREAM;
526 rv = getaddrinfo(host, service, &hints, &res);
527 if(rv != 0) {
528 dief("getaddrinfo", gai_strerror(rv));
529 }
530 for(rp = res; rp; rp = rp->ai_next) {
531 fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
532 if(fd == -1) {
533 continue;
534 }
535 while((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 &&
536 errno == EINTR);
537 if(rv == 0) {
538 break;
539 }
540 close(fd);
541 fd = -1;
542 }
543 freeaddrinfo(res);
544 return fd;
545 }
546
make_non_block(int fd)547 static void make_non_block(int fd)
548 {
549 int flags, rv;
550 while((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR);
551 if(flags == -1) {
552 dief("fcntl", strerror(errno));
553 }
554 while((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR);
555 if(rv == -1) {
556 dief("fcntl", strerror(errno));
557 }
558 }
559
560 /*
561 * Setting TCP_NODELAY is not mandatory for the SPDY protocol.
562 */
set_tcp_nodelay(int fd)563 static void set_tcp_nodelay(int fd)
564 {
565 int val = 1;
566 int rv;
567 rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val));
568 if(rv == -1) {
569 dief("setsockopt", strerror(errno));
570 }
571 }
572
573 /*
574 * Update |pollfd| based on the state of |connection|.
575 */
ctl_poll(struct pollfd * pollfd,struct Connection * connection)576 static void ctl_poll(struct pollfd *pollfd, struct Connection *connection)
577 {
578 pollfd->events = 0;
579 if(spdylay_session_want_read(connection->session) ||
580 connection->want_io == WANT_READ) {
581 pollfd->events |= POLLIN;
582 }
583 if(spdylay_session_want_write(connection->session) ||
584 connection->want_io == WANT_WRITE) {
585 pollfd->events |= POLLOUT;
586 }
587 }
588
589 /*
590 * Submits the request |req| to the connection |connection|. This
591 * function does not send packets; just append the request to the
592 * internal queue in |connection->session|.
593 */
submit_request(struct Connection * connection,struct Request * req)594 static void submit_request(struct Connection *connection, struct Request *req)
595 {
596 int pri = 0;
597 int rv;
598 const char *nv[15];
599 /* We always use SPDY/3 style header even if the negotiated protocol
600 version is SPDY/2. The library translates the header name as
601 necessary. Make sure that the last item is NULL! */
602 nv[0] = ":method"; nv[1] = "GET";
603 nv[2] = ":path"; nv[3] = req->path;
604 nv[4] = ":version"; nv[5] = "HTTP/1.1";
605 nv[6] = ":scheme"; nv[7] = "https";
606 nv[8] = ":host"; nv[9] = req->hostport;
607 nv[10] = "accept"; nv[11] = "*/*";
608 nv[12] = "user-agent"; nv[13] = "spdylay/"SPDYLAY_VERSION;
609 nv[14] = NULL;
610 rv = spdylay_submit_request(connection->session, pri, nv, NULL, req);
611 if(rv != 0) {
612 diec("spdylay_submit_request", rv);
613 }
614 }
615
616 /*
617 * Performs the network I/O.
618 */
exec_io(struct Connection * connection)619 static void exec_io(struct Connection *connection)
620 {
621 int rv;
622 rv = spdylay_session_recv(connection->session);
623 if(rv != 0) {
624 diec("spdylay_session_recv", rv);
625 }
626 rv = spdylay_session_send(connection->session);
627 if(rv != 0) {
628 diec("spdylay_session_send", rv);
629 }
630 }
631
request_init(struct Request * req,const struct URI * uri)632 static void request_init(struct Request *req, const struct URI *uri)
633 {
634 req->host = strcopy(uri->host, uri->hostlen);
635 req->port = uri->port;
636 req->path = strcopy(uri->path, uri->pathlen);
637 req->hostport = strcopy(uri->hostport, uri->hostportlen);
638 req->stream_id = -1;
639 req->inflater = NULL;
640 }
641
request_free(struct Request * req)642 static void request_free(struct Request *req)
643 {
644 free(req->host);
645 free(req->path);
646 free(req->hostport);
647 spdylay_gzip_inflate_del(req->inflater);
648 }
649
650 /*
651 * Fetches the resource denoted by |uri|.
652 */
fetch_uri(const struct URI * uri)653 static void fetch_uri(const struct URI *uri)
654 {
655 spdylay_session_callbacks callbacks;
656 int fd;
657 SSL_CTX *ssl_ctx;
658 SSL *ssl;
659 struct Request req;
660 struct Connection connection;
661 int rv;
662 nfds_t npollfds = 1;
663 struct pollfd pollfds[1];
664 uint16_t spdy_proto_version;
665
666 request_init(&req, uri);
667
668 setup_spdylay_callbacks(&callbacks);
669
670 /* Establish connection and setup SSL */
671 fd = connect_to(req.host, req.port);
672 if (-1 == fd)
673 abort ();
674 ssl_ctx = SSL_CTX_new(SSLv23_client_method());
675 if(ssl_ctx == NULL) {
676 dief("SSL_CTX_new", ERR_error_string(ERR_get_error(), NULL));
677 }
678 init_ssl_ctx(ssl_ctx, &spdy_proto_version);
679 ssl = SSL_new(ssl_ctx);
680 if(ssl == NULL) {
681 dief("SSL_new", ERR_error_string(ERR_get_error(), NULL));
682 }
683 /* To simplify the program, we perform SSL/TLS handshake in blocking
684 I/O. */
685 ssl_handshake(ssl, fd);
686
687 connection.ssl = ssl;
688 connection.want_io = IO_NONE;
689
690 /* Here make file descriptor non-block */
691 make_non_block(fd);
692 set_tcp_nodelay(fd);
693
694 spdylay_printf("[INFO] SPDY protocol version = %d\n", spdy_proto_version);
695 rv = spdylay_session_client_new(&connection.session, spdy_proto_version,
696 &callbacks, &connection);
697 if(rv != 0) {
698 diec("spdylay_session_client_new", rv);
699 }
700
701 /* Submit the HTTP request to the outbound queue. */
702 submit_request(&connection, &req);
703
704 pollfds[0].fd = fd;
705 ctl_poll(pollfds, &connection);
706
707 /* Event loop */
708 while(spdylay_session_want_read(connection.session) ||
709 spdylay_session_want_write(connection.session)) {
710 int nfds = poll(pollfds, npollfds, -1);
711 if(nfds == -1) {
712 dief("poll", strerror(errno));
713 }
714 if(pollfds[0].revents & (POLLIN | POLLOUT)) {
715 exec_io(&connection);
716 }
717 if((pollfds[0].revents & POLLHUP) || (pollfds[0].revents & POLLERR)) {
718 die("Connection error");
719 }
720 ctl_poll(pollfds, &connection);
721 }
722
723 /* Resource cleanup */
724 spdylay_session_del(connection.session);
725 SSL_shutdown(ssl);
726 SSL_free(ssl);
727 SSL_CTX_free(ssl_ctx);
728 shutdown(fd, SHUT_WR);
729 close(fd);
730 request_free(&req);
731 }
732
parse_uri(struct URI * res,const char * uri)733 static int parse_uri(struct URI *res, const char *uri)
734 {
735 /* We only interested in https */
736 size_t len, i, offset;
737 memset(res, 0, sizeof(struct URI));
738 len = strlen(uri);
739 if(len < 9 || memcmp("https://", uri, 8) != 0) {
740 return -1;
741 }
742 offset = 8;
743 res->host = res->hostport = &uri[offset];
744 res->hostlen = 0;
745 if(uri[offset] == '[') {
746 /* IPv6 literal address */
747 ++offset;
748 ++res->host;
749 for(i = offset; i < len; ++i) {
750 if(uri[i] == ']') {
751 res->hostlen = i-offset;
752 offset = i+1;
753 break;
754 }
755 }
756 } else {
757 const char delims[] = ":/?#";
758 for(i = offset; i < len; ++i) {
759 if(strchr(delims, uri[i]) != NULL) {
760 break;
761 }
762 }
763 res->hostlen = i-offset;
764 offset = i;
765 }
766 if(res->hostlen == 0) {
767 return -1;
768 }
769 /* Assuming https */
770 res->port = 443;
771 if(offset < len) {
772 if(uri[offset] == ':') {
773 /* port */
774 const char delims[] = "/?#";
775 int port = 0;
776 ++offset;
777 for(i = offset; i < len; ++i) {
778 if(strchr(delims, uri[i]) != NULL) {
779 break;
780 }
781 if('0' <= uri[i] && uri[i] <= '9') {
782 port *= 10;
783 port += uri[i]-'0';
784 if(port > 65535) {
785 return -1;
786 }
787 } else {
788 return -1;
789 }
790 }
791 if(port == 0) {
792 return -1;
793 }
794 offset = i;
795 res->port = port;
796 }
797 }
798 res->hostportlen = uri+offset-res->host;
799 for(i = offset; i < len; ++i) {
800 if(uri[i] == '#') {
801 break;
802 }
803 }
804 if(i-offset == 0) {
805 res->path = "/";
806 res->pathlen = 1;
807 } else {
808 res->path = &uri[offset];
809 res->pathlen = i-offset;
810 }
811 return 0;
812 }
813
814
815 /*****
816 * end of code needed to utilize spdylay
817 */
818
819
820 /*****
821 * start of code needed to utilize microspdy
822 */
823
824 void
new_session_callback(void * cls,struct SPDY_Session * session)825 new_session_callback (void *cls,
826 struct SPDY_Session * session)
827 {
828 char ipstr[1024];
829
830 struct sockaddr *addr;
831 socklen_t addr_len = SPDY_get_remote_addr(session, &addr);
832
833 if(!addr_len)
834 {
835 printf("SPDY_get_remote_addr");
836 abort();
837 }
838
839 if(strcmp(CLS,cls)!=0)
840 {
841 killchild(child,"wrong cls");
842 }
843
844 if(AF_INET == addr->sa_family)
845 {
846 struct sockaddr_in * addr4 = (struct sockaddr_in *) addr;
847 if(NULL == inet_ntop(AF_INET, &(addr4->sin_addr), ipstr, sizeof(ipstr)))
848 {
849 killchild(child,"inet_ntop");
850 }
851 printf("New connection from: %s:%i\n", ipstr, ntohs(addr4->sin_port));
852
853 loop = 0;
854 }
855 #if HAVE_INET6
856 else if(AF_INET6 == addr->sa_family)
857 {
858 struct sockaddr_in6 * addr6 = (struct sockaddr_in6 *) addr;
859 if(NULL == inet_ntop(AF_INET6, &(addr6->sin6_addr), ipstr, sizeof(ipstr)))
860 {
861 killchild(child,"inet_ntop");
862 }
863 printf("New connection from: %s:%i\n", ipstr, ntohs(addr6->sin6_port));
864
865 loop = 0;
866 }
867 #endif
868 else
869 {
870 killchild(child,"wrong family");
871 }
872 }
873
874 /*****
875 * end of code needed to utilize microspdy
876 */
877
878 //child process
879 void
childproc(int parent)880 childproc(int parent)
881 {
882 struct URI uri;
883 struct sigaction act;
884 int rv;
885 char *uristr;
886
887 memset(&act, 0, sizeof(struct sigaction));
888 act.sa_handler = SIG_IGN;
889 sigaction(SIGPIPE, &act, 0);
890
891 asprintf(&uristr, "https://127.0.0.1:%i/",port);
892
893 SSL_load_error_strings();
894 SSL_library_init();
895
896 rv = parse_uri(&uri, uristr);
897 if(rv != 0) {
898 killparent(parent,"parse_uri failed");
899 }
900 fetch_uri(&uri);
901 }
902
903 //parent proc
904 int
parentproc(int child)905 parentproc(int child)
906 {
907 int childstatus = 0;
908 unsigned long long timeoutlong=0;
909 struct timeval timeout;
910 int ret;
911 fd_set read_fd_set;
912 fd_set write_fd_set;
913 fd_set except_fd_set;
914 int maxfd = -1;
915 struct SPDY_Daemon *daemon;
916
917 SPDY_init();
918
919 daemon = SPDY_start_daemon(port,
920 DATA_DIR "cert-and-key.pem",
921 DATA_DIR "cert-and-key.pem",
922 &new_session_callback,NULL,NULL,NULL,CLS,SPDY_DAEMON_OPTION_END);
923
924 if(NULL==daemon){
925 printf("no daemon\n");
926 return 1;
927 }
928
929 do
930 {
931 FD_ZERO(&read_fd_set);
932 FD_ZERO(&write_fd_set);
933 FD_ZERO(&except_fd_set);
934
935 ret = SPDY_get_timeout(daemon, &timeoutlong);
936 if(SPDY_NO == ret || timeoutlong > 1000)
937 {
938 timeout.tv_sec = 1;
939 timeout.tv_usec = 0;
940 }
941 else
942 {
943 timeout.tv_sec = timeoutlong / 1000;
944 timeout.tv_usec = (timeoutlong % 1000) * 1000;
945 }
946
947 maxfd = SPDY_get_fdset (daemon,
948 &read_fd_set,
949 &write_fd_set,
950 &except_fd_set);
951
952 ret = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout);
953
954 switch(ret) {
955 case -1:
956 printf("select error: %i\n", errno);
957 killchild(child, "select error");
958 break;
959 case 0:
960
961 break;
962 default:
963 SPDY_run(daemon);
964
965 break;
966 }
967 }
968 while(loop && waitpid(child,&childstatus,WNOHANG) != child);
969
970 SPDY_stop_daemon(daemon);
971
972 SPDY_deinit();
973
974 if(loop)
975 return WEXITSTATUS(childstatus);
976 if(waitpid(child,&childstatus,WNOHANG) == child)
977 return WEXITSTATUS(childstatus);
978
979 kill(child,SIGKILL);
980
981 waitpid(child,&childstatus,0);
982
983 return 0;
984 }
985
main()986 int main()
987 {
988 port = get_port(14123);
989 parent = getpid();
990
991 child = fork();
992 if (child == -1)
993 {
994 fprintf(stderr, "can't fork, error %d\n", errno);
995 exit(EXIT_FAILURE);
996 }
997
998 if (child == 0)
999 {
1000 childproc(parent);
1001 _exit(0);
1002 }
1003 else
1004 {
1005 int ret = parentproc(child);
1006 exit(ret);
1007 }
1008 return 1;
1009 }
1010