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