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 45 spdylay_printf(const char *format, ...) 46 { 47 (void)format; 48 49 return 0; 50 } 51 52 int 53 spdylay_fprintf(FILE *stream, const char *format, ...) 54 { 55 (void)stream; 56 (void)format; 57 58 return 0; 59 } 60 61 void 62 killchild(int pid, char *message) 63 { 64 printf("%s\n",message); 65 kill(pid, SIGKILL); 66 exit(1); 67 } 68 69 void 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 */ 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 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 */ 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 */ 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 */ 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 */ 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 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 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 */ 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 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 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 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 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 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