Home | History | Annotate | Download | only in testspdy
      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