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