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
80enum {
81  IO_NONE,
82  WANT_READ,
83  WANT_WRITE
84};
85
86struct 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
98struct 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
112struct 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 */
127static 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 */
141static 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 */
151static 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 */
161static 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 */
172static 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 */
200static 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 */
241static 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 */
284static 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
302static 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
329static 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 */
373static 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 */
398static 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 */
449static 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 */
466static 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
499static 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 */
515static 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 */
528static 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 */
546static 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 */
571static 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
584static 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
594static 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 */
605static 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
668static 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
760void
761standard_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
803void
804session_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
839void
840childproc(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
867int
868parentproc( 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
947int 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