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