• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
killchild(int pid,char * message)44 killchild(int pid, char *message)
45 {
46 	printf("%s\n",message);
47 	kill(pid, SIGKILL);
48 	exit(1);
49 }
50 
51 void
killparent(int pid,char * message)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  */
strcopy(const char * s,size_t len)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  */
die(const char * msg)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  */
dief(const char * func,const char * msg)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  */
diec(const char * func,int error_code)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  */
check_gzip(struct Request * req,char ** nv)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  */
send_callback(spdylay_session * session,const uint8_t * data,size_t length,int flags,void * user_data)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  */
recv_callback(spdylay_session * session,uint8_t * buf,size_t length,int flags,void * user_data)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  */
before_ctrl_send_callback(spdylay_session * session,spdylay_frame_type type,spdylay_frame * frame,void * user_data)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 
on_ctrl_send_callback(spdylay_session * session,spdylay_frame_type type,spdylay_frame * frame,void * user_data)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 
on_ctrl_recv_callback(spdylay_session * session,spdylay_frame_type type,spdylay_frame * frame,void * user_data)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  */
on_stream_close_callback(spdylay_session * session,int32_t stream_id,spdylay_status_code status_code,void * user_data)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  */
on_data_chunk_recv_callback(spdylay_session * session,uint8_t flags,int32_t stream_id,const uint8_t * data,size_t len,void * user_data)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  */
setup_spdylay_callbacks(spdylay_session_callbacks * callbacks)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  */
connect_to(const char * host,uint16_t port)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 
make_non_block(int fd)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  */
set_tcp_nodelay(int fd)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  */
ctl_poll(struct pollfd * pollfd,struct Connection * connection)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  */
submit_request(struct Connection * connection,struct Request * req)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  */
exec_io(struct Connection * connection)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 
request_init(struct Request * req,const struct URI * uri)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 
request_free(struct Request * req)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  */
fetch_uri(const struct URI * uri)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 
parse_uri(struct URI * res,const char * uri)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
standard_request_handler(void * cls,struct SPDY_Request * request,uint8_t priority,const char * method,const char * path,const char * version,const char * host,const char * scheme,struct SPDY_NameValue * headers,bool more)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
session_closed_handler(void * cls,struct SPDY_Session * session,int by_client)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
childproc(int port)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
parentproc(int port)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 
main()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