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 44 killchild(int pid, char *message) 45 { 46 printf("%s\n",message); 47 kill(pid, SIGKILL); 48 exit(1); 49 } 50 51 void 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 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 */ 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 */ 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 */ 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 */ 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 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 */ 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 */ 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 */ 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 */ 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 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 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 */ 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 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 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 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 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 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 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