aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/include/microspdy.h7
-rw-r--r--src/microspdy/applicationlayer.c3
-rw-r--r--src/microspdy/daemon.c49
-rw-r--r--src/microspdy/io_raw.c13
-rw-r--r--src/microspdy/session.c28
-rw-r--r--src/microspdy/structures.h5
-rw-r--r--src/testspdy/Makefile.am7
-rw-r--r--src/testspdy/test_notls.c940
8 files changed, 1015 insertions, 37 deletions
diff --git a/src/include/microspdy.h b/src/include/microspdy.h
index d34d6af0..8b31b2ca 100644
--- a/src/include/microspdy.h
+++ b/src/include/microspdy.h
@@ -360,6 +360,13 @@ enum SPDY_DAEMON_OPTION
360 * which is the result of bitwise OR of desired flags. 360 * which is the result of bitwise OR of desired flags.
361 */ 361 */
362 SPDY_DAEMON_OPTION_FLAGS = 4, 362 SPDY_DAEMON_OPTION_FLAGS = 4,
363
364 /**
365 * IO subsystem type used by daemon and all its sessions. If not set,
366 * TLS provided by openssl is used. Must be followed by a
367 * SPDY_IO_SUBSYSTEM value.
368 */
369 SPDY_DAEMON_OPTION_IO_SUBSYSTEM = 8,
363}; 370};
364 371
365 372
diff --git a/src/microspdy/applicationlayer.c b/src/microspdy/applicationlayer.c
index 02a5a674..5be3934d 100644
--- a/src/microspdy/applicationlayer.c
+++ b/src/microspdy/applicationlayer.c
@@ -344,6 +344,8 @@ SPDY_start_daemon (uint16_t port,
344 SPDYF_DEBUG("library not initialized"); 344 SPDYF_DEBUG("library not initialized");
345 return NULL; 345 return NULL;
346 } 346 }
347 /*
348 * for now make this checks in framing layer
347 if(NULL == certfile) 349 if(NULL == certfile)
348 { 350 {
349 SPDYF_DEBUG("certfile is NULL"); 351 SPDYF_DEBUG("certfile is NULL");
@@ -354,6 +356,7 @@ SPDY_start_daemon (uint16_t port,
354 SPDYF_DEBUG("keyfile is NULL"); 356 SPDYF_DEBUG("keyfile is NULL");
355 return NULL; 357 return NULL;
356 } 358 }
359 */
357 360
358 va_start(valist, cls); 361 va_start(valist, cls);
359 daemon = SPDYF_start_daemon_va ( port, 362 daemon = SPDYF_start_daemon_va ( port,
diff --git a/src/microspdy/daemon.c b/src/microspdy/daemon.c
index f89ee962..d0e61742 100644
--- a/src/microspdy/daemon.c
+++ b/src/microspdy/daemon.c
@@ -142,6 +142,9 @@ spdyf_parse_options_va (struct SPDY_Daemon *daemon,
142 case SPDY_DAEMON_OPTION_FLAGS: 142 case SPDY_DAEMON_OPTION_FLAGS:
143 daemon->flags = va_arg (valist, enum SPDY_DAEMON_FLAG); 143 daemon->flags = va_arg (valist, enum SPDY_DAEMON_FLAG);
144 break; 144 break;
145 case SPDY_DAEMON_OPTION_IO_SUBSYSTEM:
146 daemon->io_subsystem = va_arg (valist, enum SPDY_IO_SUBSYSTEM);
147 break;
145 default: 148 default:
146 SPDYF_DEBUG("Wrong option for the daemon %i",opt); 149 SPDYF_DEBUG("Wrong option for the daemon %i",opt);
147 return SPDY_NO; 150 return SPDY_NO;
@@ -191,18 +194,40 @@ SPDYF_start_daemon_va (uint16_t port,
191 memset (daemon, 0, sizeof (struct SPDY_Daemon)); 194 memset (daemon, 0, sizeof (struct SPDY_Daemon));
192 daemon->socket_fd = -1; 195 daemon->socket_fd = -1;
193 daemon->port = port; 196 daemon->port = port;
194 SPDYF_io_set_daemon(daemon, SPDY_IO_SUBSYSTEM_OPENSSL); 197
195 198 if(SPDY_YES != spdyf_parse_options_va (daemon, valist))
196 if (NULL == (daemon->certfile = strdup (certfile)))
197 { 199 {
198 SPDYF_DEBUG("str"); 200 SPDYF_DEBUG("parse");
199 goto free_and_fail; 201 goto free_and_fail;
200 } 202 }
201 if (NULL == (daemon->keyfile = strdup (keyfile))) 203
204 if(!port && NULL == daemon->address)
202 { 205 {
203 SPDYF_DEBUG("str"); 206 SPDYF_DEBUG("Port is 0");
204 goto free_and_fail; 207 goto free_and_fail;
205 } 208 }
209 if(0 == daemon->io_subsystem)
210 daemon->io_subsystem = SPDY_IO_SUBSYSTEM_OPENSSL;
211
212 if(SPDY_YES != SPDYF_io_set_daemon(daemon, daemon->io_subsystem))
213 goto free_and_fail;
214
215 if(SPDY_IO_SUBSYSTEM_RAW != daemon->io_subsystem)
216 {
217 if (NULL == certfile
218 || NULL == (daemon->certfile = strdup (certfile)))
219 {
220 SPDYF_DEBUG("strdup (certfile)");
221 goto free_and_fail;
222 }
223 if (NULL == keyfile
224 || NULL == (daemon->keyfile = strdup (keyfile)))
225 {
226 SPDYF_DEBUG("strdup (keyfile)");
227 goto free_and_fail;
228 }
229 }
230
206 daemon->new_session_cb = nscb; 231 daemon->new_session_cb = nscb;
207 daemon->session_closed_cb = sccb; 232 daemon->session_closed_cb = sccb;
208 daemon->new_request_cb = nrcb; 233 daemon->new_request_cb = nrcb;
@@ -211,18 +236,6 @@ SPDYF_start_daemon_va (uint16_t port,
211 daemon->fcls = fcls; 236 daemon->fcls = fcls;
212 daemon->fnew_stream_cb = fnscb; 237 daemon->fnew_stream_cb = fnscb;
213 238
214 if(SPDY_YES != spdyf_parse_options_va (daemon, valist))
215 {
216 SPDYF_DEBUG("parse");
217 goto free_and_fail;
218 }
219
220 if(!port && NULL == daemon->address)
221 {
222 SPDYF_DEBUG("Port is 0");
223 goto free_and_fail;
224 }
225
226#if HAVE_INET6 239#if HAVE_INET6
227 //handling IPv6 240 //handling IPv6
228 if((daemon->flags & SPDY_DAEMON_FLAG_ONLY_IPV6) 241 if((daemon->flags & SPDY_DAEMON_FLAG_ONLY_IPV6)
diff --git a/src/microspdy/io_raw.c b/src/microspdy/io_raw.c
index 52868505..75ec20d5 100644
--- a/src/microspdy/io_raw.c
+++ b/src/microspdy/io_raw.c
@@ -61,6 +61,19 @@ SPDYF_raw_new_session(struct SPDY_Session *session)
61{ 61{
62 (void)session; 62 (void)session;
63 63
64 //TODO
65 //setting the socket to be non-blocking
66 /*
67 * different handling is needed by libssl if non-blocking is used
68 *
69 fd_flags = fcntl (new_socket_fd, F_GETFL);
70 if ( -1 == fd_flags
71 || 0 != fcntl (new_socket_fd, F_SETFL, fd_flags | O_NONBLOCK))
72 {
73 SPDYF_DEBUG("WARNING: Couldn't set the new connection to be non-blocking");
74 }
75 */
76
64 return SPDY_YES; 77 return SPDY_YES;
65} 78}
66 79
diff --git a/src/microspdy/session.c b/src/microspdy/session.c
index 93e4cc49..d2a34de5 100644
--- a/src/microspdy/session.c
+++ b/src/microspdy/session.c
@@ -1264,10 +1264,11 @@ int
1264SPDYF_session_accept(struct SPDY_Daemon *daemon) 1264SPDYF_session_accept(struct SPDY_Daemon *daemon)
1265{ 1265{
1266 int new_socket_fd; 1266 int new_socket_fd;
1267 //int fd_flags; 1267 int ret;
1268 struct SPDY_Session *session = NULL; 1268 struct SPDY_Session *session = NULL;
1269 socklen_t addr_len; 1269 socklen_t addr_len;
1270 struct sockaddr *addr; 1270 struct sockaddr *addr;
1271
1271#if HAVE_INET6 1272#if HAVE_INET6
1272 struct sockaddr_in6 addr6; 1273 struct sockaddr_in6 addr6;
1273 1274
@@ -1280,25 +1281,13 @@ SPDYF_session_accept(struct SPDY_Daemon *daemon)
1280 addr_len = sizeof(addr6); 1281 addr_len = sizeof(addr6);
1281#endif 1282#endif
1282 1283
1283 new_socket_fd = accept (daemon->socket_fd, addr, &addr_len); 1284 new_socket_fd = accept (daemon->socket_fd, addr, &addr_len);
1284 1285
1285 if(new_socket_fd < 1) 1286 if(new_socket_fd < 1)
1286 return SPDY_NO; 1287 return SPDY_NO;
1287
1288 //setting the socket to be non-blocking
1289 /*
1290 * different handling is needed by libssl if non-blocking is used
1291 *
1292 fd_flags = fcntl (new_socket_fd, F_GETFL);
1293 if ( -1 == fd_flags
1294 || 0 != fcntl (new_socket_fd, F_SETFL, fd_flags | O_NONBLOCK))
1295 {
1296 SPDYF_DEBUG("WARNING: Couldn't set the new connection to be non-blocking");
1297 }
1298 */
1299 1288
1300 if (NULL == (session = malloc (sizeof (struct SPDY_Session)))) 1289 if (NULL == (session = malloc (sizeof (struct SPDY_Session))))
1301 { 1290 {
1302 goto free_and_fail; 1291 goto free_and_fail;
1303 } 1292 }
1304 memset (session, 0, sizeof (struct SPDY_Session)); 1293 memset (session, 0, sizeof (struct SPDY_Session));
@@ -1306,7 +1295,8 @@ SPDYF_session_accept(struct SPDY_Daemon *daemon)
1306 session->daemon = daemon; 1295 session->daemon = daemon;
1307 session->socket_fd = new_socket_fd; 1296 session->socket_fd = new_socket_fd;
1308 1297
1309 SPDYF_io_set_session(session, SPDY_IO_SUBSYSTEM_OPENSSL); 1298 ret = SPDYF_io_set_session(session, daemon->io_subsystem);
1299 SPDYF_ASSERT(SPDY_YES == ret, "Somehow daemon->io_subsystem iswrong here");
1310 1300
1311 //init TLS context, handshake will be done 1301 //init TLS context, handshake will be done
1312 if(SPDY_YES != session->fio_new_session(session)) 1302 if(SPDY_YES != session->fio_new_session(session))
diff --git a/src/microspdy/structures.h b/src/microspdy/structures.h
index a53e12a9..45fd917a 100644
--- a/src/microspdy/structures.h
+++ b/src/microspdy/structures.h
@@ -920,6 +920,11 @@ struct SPDY_Daemon
920 enum SPDY_DAEMON_FLAG flags; 920 enum SPDY_DAEMON_FLAG flags;
921 921
922 /** 922 /**
923 * IO subsystem type used by daemon and all its sessions.
924 */
925 enum SPDY_IO_SUBSYSTEM io_subsystem;
926
927 /**
923 * Listen port. 928 * Listen port.
924 */ 929 */
925 uint16_t port; 930 uint16_t port;
diff --git a/src/testspdy/Makefile.am b/src/testspdy/Makefile.am
index 42076e43..712ea020 100644
--- a/src/testspdy/Makefile.am
+++ b/src/testspdy/Makefile.am
@@ -27,6 +27,7 @@ check_PROGRAMS = \
27 test_struct_namevalue \ 27 test_struct_namevalue \
28 test_new_connection \ 28 test_new_connection \
29 test_request_response \ 29 test_request_response \
30 test_notls \
30 test_request_response_with_callback \ 31 test_request_response_with_callback \
31 test_requests_with_assets \ 32 test_requests_with_assets \
32 test_misc \ 33 test_misc \
@@ -71,6 +72,12 @@ test_request_response_SOURCES = \
71test_request_response_LDADD = $(SPDY_LDADD) \ 72test_request_response_LDADD = $(SPDY_LDADD) \
72 -lspdylay 73 -lspdylay
73 74
75test_notls_SOURCES = \
76 test_notls.c \
77 $(SPDY_SOURCES)
78test_notls_LDADD = $(SPDY_LDADD) \
79 -lspdylay
80
74test_request_response_with_callback_SOURCES = \ 81test_request_response_with_callback_SOURCES = \
75 test_request_response_with_callback.c \ 82 test_request_response_with_callback.c \
76 $(SPDY_SOURCES) 83 $(SPDY_SOURCES)
diff --git a/src/testspdy/test_notls.c b/src/testspdy/test_notls.c
new file mode 100644
index 00000000..2337d486
--- /dev/null
+++ b/src/testspdy/test_notls.c
@@ -0,0 +1,940 @@
1/*
2 This file is part of libmicrospdy
3 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 memcpy(dst, s, len);
132 dst[len] = '\0';
133 return dst;
134}
135
136/*
137 * Prints error message |msg| and exit.
138 */
139static void die(const char *msg)
140{
141 fprintf(stderr, "FATAL: %s\n", msg);
142 exit(EXIT_FAILURE);
143}
144
145/*
146 * Prints error containing the function name |func| and message |msg|
147 * and exit.
148 */
149static void dief(const char *func, const char *msg)
150{
151 fprintf(stderr, "FATAL: %s: %s\n", func, msg);
152 exit(EXIT_FAILURE);
153}
154
155/*
156 * Prints error containing the function name |func| and error code
157 * |error_code| and exit.
158 */
159static void diec(const char *func, int error_code)
160{
161 fprintf(stderr, "FATAL: %s: error_code=%d, msg=%s\n", func, error_code,
162 spdylay_strerror(error_code));
163 exit(EXIT_FAILURE);
164}
165
166/*
167 * Check response is content-encoding: gzip. We need this because SPDY
168 * client is required to support gzip.
169 */
170static void check_gzip(struct Request *req, char **nv)
171{
172 int gzip = 0;
173 size_t i;
174 for(i = 0; nv[i]; i += 2) {
175 if(strcmp("content-encoding", nv[i]) == 0) {
176 gzip = strcmp("gzip", nv[i+1]) == 0;
177 break;
178 }
179 }
180 if(gzip) {
181 int rv;
182 if(req->inflater) {
183 return;
184 }
185 rv = spdylay_gzip_inflate_new(&req->inflater);
186 if(rv != 0) {
187 die("Can't allocate inflate stream.");
188 }
189 }
190}
191
192/*
193 * The implementation of spdylay_send_callback type. Here we write
194 * |data| with size |length| to the network and return the number of
195 * bytes actually written. See the documentation of
196 * spdylay_send_callback for the details.
197 */
198static ssize_t send_callback(spdylay_session *session,
199 const uint8_t *data, size_t length, int flags,
200 void *user_data)
201{
202 struct Connection *connection;
203 ssize_t rv;
204 connection = (struct Connection*)user_data;
205 connection->want_io = IO_NONE;
206
207 rv = write(connection->fd,
208 data,
209 length);
210
211 if (rv < 0)
212 {
213 switch(errno)
214 {
215 case EAGAIN:
216 #if EAGAIN != EWOULDBLOCK
217 case EWOULDBLOCK:
218 #endif
219 connection->want_io = WANT_WRITE;
220 rv = SPDYLAY_ERR_WOULDBLOCK;
221 break;
222
223 default:
224 rv = SPDYLAY_ERR_CALLBACK_FAILURE;
225 }
226 }
227 return rv;
228}
229
230/*
231 * The implementation of spdylay_recv_callback type. Here we read data
232 * from the network and write them in |buf|. The capacity of |buf| is
233 * |length| bytes. Returns the number of bytes stored in |buf|. See
234 * the documentation of spdylay_recv_callback for the details.
235 */
236static ssize_t recv_callback(spdylay_session *session,
237 uint8_t *buf, size_t length, int flags,
238 void *user_data)
239{
240 struct Connection *connection;
241 ssize_t rv;
242 connection = (struct Connection*)user_data;
243 connection->want_io = IO_NONE;
244
245 rv = read(connection->fd,
246 buf,
247 length);
248
249 if (rv < 0)
250 {
251 switch(errno)
252 {
253 case EAGAIN:
254 #if EAGAIN != EWOULDBLOCK
255 case EWOULDBLOCK:
256 #endif
257 connection->want_io = WANT_READ;
258 rv = SPDYLAY_ERR_WOULDBLOCK;
259 break;
260
261 default:
262 rv = SPDYLAY_ERR_CALLBACK_FAILURE;
263 }
264 }
265 else if(rv == 0)
266 rv = SPDYLAY_ERR_EOF;
267 return rv;
268}
269
270/*
271 * The implementation of spdylay_before_ctrl_send_callback type. We
272 * use this function to get stream ID of the request. This is because
273 * stream ID is not known when we submit the request
274 * (spdylay_submit_request).
275 */
276static void before_ctrl_send_callback(spdylay_session *session,
277 spdylay_frame_type type,
278 spdylay_frame *frame,
279 void *user_data)
280{
281 if(type == SPDYLAY_SYN_STREAM) {
282 struct Request *req;
283 int stream_id = frame->syn_stream.stream_id;
284 req = spdylay_session_get_stream_user_data(session, stream_id);
285 if(req && req->stream_id == -1) {
286 req->stream_id = stream_id;
287 printf("[INFO] Stream ID = %d\n", stream_id);
288 }
289 }
290}
291
292static void on_ctrl_send_callback(spdylay_session *session,
293 spdylay_frame_type type,
294 spdylay_frame *frame, void *user_data)
295{
296 char **nv;
297 const char *name = NULL;
298 int32_t stream_id;
299 size_t i;
300 switch(type) {
301 case SPDYLAY_SYN_STREAM:
302 nv = frame->syn_stream.nv;
303 name = "SYN_STREAM";
304 stream_id = frame->syn_stream.stream_id;
305 break;
306 default:
307 break;
308 }
309 if(name && spdylay_session_get_stream_user_data(session, stream_id)) {
310 printf("[INFO] C ----------------------------> S (%s)\n", name);
311 for(i = 0; nv[i]; i += 2) {
312 printf(" %s: %s\n", nv[i], nv[i+1]);
313 }
314 }
315}
316
317static void on_ctrl_recv_callback(spdylay_session *session,
318 spdylay_frame_type type,
319 spdylay_frame *frame, void *user_data)
320{
321 struct Request *req;
322 char **nv;
323 const char *name = NULL;
324 int32_t stream_id;
325 size_t i;
326 switch(type) {
327 case SPDYLAY_SYN_REPLY:
328 nv = frame->syn_reply.nv;
329 name = "SYN_REPLY";
330 stream_id = frame->syn_reply.stream_id;
331 break;
332 case SPDYLAY_HEADERS:
333 nv = frame->headers.nv;
334 name = "HEADERS";
335 stream_id = frame->headers.stream_id;
336 break;
337 default:
338 break;
339 }
340 if(!name) {
341 return;
342 }
343 req = spdylay_session_get_stream_user_data(session, stream_id);
344 if(req) {
345 check_gzip(req, nv);
346 printf("[INFO] C <---------------------------- S (%s)\n", name);
347 for(i = 0; nv[i]; i += 2) {
348 printf(" %s: %s\n", nv[i], nv[i+1]);
349 }
350 }
351}
352
353/*
354 * The implementation of spdylay_on_stream_close_callback type. We use
355 * this function to know the response is fully received. Since we just
356 * fetch 1 resource in this program, after reception of the response,
357 * we submit GOAWAY and close the session.
358 */
359static void on_stream_close_callback(spdylay_session *session,
360 int32_t stream_id,
361 spdylay_status_code status_code,
362 void *user_data)
363{
364 struct Request *req;
365 req = spdylay_session_get_stream_user_data(session, stream_id);
366 if(req) {
367 int rv;
368 rv = spdylay_submit_goaway(session, SPDYLAY_GOAWAY_OK);
369 if(rv != 0) {
370 diec("spdylay_submit_goaway", rv);
371 }
372 }
373}
374
375#define MAX_OUTLEN 4096
376
377/*
378 * The implementation of spdylay_on_data_chunk_recv_callback type. We
379 * use this function to print the received response body.
380 */
381static void on_data_chunk_recv_callback(spdylay_session *session, uint8_t flags,
382 int32_t stream_id,
383 const uint8_t *data, size_t len,
384 void *user_data)
385{
386 struct Request *req;
387 req = spdylay_session_get_stream_user_data(session, stream_id);
388 if(req) {
389 printf("[INFO] C <---------------------------- S (DATA)\n");
390 printf(" %lu bytes\n", (unsigned long int)len);
391 if(req->inflater) {
392 while(len > 0) {
393 uint8_t out[MAX_OUTLEN];
394 size_t outlen = MAX_OUTLEN;
395 size_t tlen = len;
396 int rv;
397 rv = spdylay_gzip_inflate(req->inflater, out, &outlen, data, &tlen);
398 if(rv == -1) {
399 spdylay_submit_rst_stream(session, stream_id, SPDYLAY_INTERNAL_ERROR);
400 break;
401 }
402 fwrite(out, 1, outlen, stdout);
403 data += tlen;
404 len -= tlen;
405 }
406 } else {
407 /* TODO add support gzip */
408 fwrite(data, 1, len, stdout);
409
410 //check if the data is correct
411 //if(strcmp(RESPONSE_BODY, data) != 0)
412 //killparent(parent, "\nreceived data is not the same");
413 if(len + rcvbuf_c > strlen(RESPONSE_BODY))
414 killparent(parent, "\nreceived data is not the same");
415
416 strcpy(rcvbuf + rcvbuf_c,(char*)data);
417 rcvbuf_c+=len;
418 }
419 printf("\n");
420 }
421}
422
423/*
424 * Setup callback functions. Spdylay API offers many callback
425 * functions, but most of them are optional. The send_callback is
426 * always required. Since we use spdylay_session_recv(), the
427 * recv_callback is also required.
428 */
429static void setup_spdylay_callbacks(spdylay_session_callbacks *callbacks)
430{
431 memset(callbacks, 0, sizeof(spdylay_session_callbacks));
432 callbacks->send_callback = send_callback;
433 callbacks->recv_callback = recv_callback;
434 callbacks->before_ctrl_send_callback = before_ctrl_send_callback;
435 callbacks->on_ctrl_send_callback = on_ctrl_send_callback;
436 callbacks->on_ctrl_recv_callback = on_ctrl_recv_callback;
437 callbacks->on_stream_close_callback = on_stream_close_callback;
438 callbacks->on_data_chunk_recv_callback = on_data_chunk_recv_callback;
439}
440
441
442/*
443 * Connects to the host |host| and port |port|. This function returns
444 * the file descriptor of the client socket.
445 */
446static int connect_to(const char *host, uint16_t port)
447{
448 struct addrinfo hints;
449 int fd = -1;
450 int rv;
451 char service[NI_MAXSERV];
452 struct addrinfo *res, *rp;
453 snprintf(service, sizeof(service), "%u", port);
454 memset(&hints, 0, sizeof(struct addrinfo));
455 hints.ai_family = AF_UNSPEC;
456 hints.ai_socktype = SOCK_STREAM;
457 rv = getaddrinfo(host, service, &hints, &res);
458 if(rv != 0) {
459 dief("getaddrinfo", gai_strerror(rv));
460 }
461 for(rp = res; rp; rp = rp->ai_next) {
462 fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
463 if(fd == -1) {
464 continue;
465 }
466 while((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 &&
467 errno == EINTR);
468 if(rv == 0) {
469 break;
470 }
471 close(fd);
472 fd = -1;
473 }
474 freeaddrinfo(res);
475 return fd;
476}
477
478static void make_non_block(int fd)
479{
480 int flags, rv;
481 while((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR);
482 if(flags == -1) {
483 dief("fcntl", strerror(errno));
484 }
485 while((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR);
486 if(rv == -1) {
487 dief("fcntl", strerror(errno));
488 }
489}
490
491/*
492 * Setting TCP_NODELAY is not mandatory for the SPDY protocol.
493 */
494static void set_tcp_nodelay(int fd)
495{
496 int val = 1;
497 int rv;
498 rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val));
499 if(rv == -1) {
500 dief("setsockopt", strerror(errno));
501 }
502}
503
504/*
505 * Update |pollfd| based on the state of |connection|.
506 */
507static void ctl_poll(struct pollfd *pollfd, struct Connection *connection)
508{
509 pollfd->events = 0;
510 if(spdylay_session_want_read(connection->session) ||
511 connection->want_io == WANT_READ) {
512 pollfd->events |= POLLIN;
513 }
514 if(spdylay_session_want_write(connection->session) ||
515 connection->want_io == WANT_WRITE) {
516 pollfd->events |= POLLOUT;
517 }
518}
519
520/*
521 * Submits the request |req| to the connection |connection|. This
522 * function does not send packets; just append the request to the
523 * internal queue in |connection->session|.
524 */
525static void submit_request(struct Connection *connection, struct Request *req)
526{
527 int pri = 0;
528 int rv;
529 const char *nv[15];
530 /* We always use SPDY/3 style header even if the negotiated protocol
531 version is SPDY/2. The library translates the header name as
532 necessary. Make sure that the last item is NULL! */
533 nv[0] = ":method"; nv[1] = "GET";
534 nv[2] = ":path"; nv[3] = req->path;
535 nv[4] = ":version"; nv[5] = "HTTP/1.1";
536 nv[6] = ":scheme"; nv[7] = "https";
537 nv[8] = ":host"; nv[9] = req->hostport;
538 nv[10] = "accept"; nv[11] = "*/*";
539 nv[12] = "user-agent"; nv[13] = "spdylay/"SPDYLAY_VERSION;
540 nv[14] = NULL;
541 rv = spdylay_submit_request(connection->session, pri, nv, NULL, req);
542 if(rv != 0) {
543 diec("spdylay_submit_request", rv);
544 }
545}
546
547/*
548 * Performs the network I/O.
549 */
550static void exec_io(struct Connection *connection)
551{
552 int rv;
553 rv = spdylay_session_recv(connection->session);
554 if(rv != 0) {
555 diec("spdylay_session_recv", rv);
556 }
557 rv = spdylay_session_send(connection->session);
558 if(rv != 0) {
559 diec("spdylay_session_send", rv);
560 }
561}
562
563static void request_init(struct Request *req, const struct URI *uri)
564{
565 req->host = strcopy(uri->host, uri->hostlen);
566 req->port = uri->port;
567 req->path = strcopy(uri->path, uri->pathlen);
568 req->hostport = strcopy(uri->hostport, uri->hostportlen);
569 req->stream_id = -1;
570 req->inflater = NULL;
571}
572
573static void request_free(struct Request *req)
574{
575 free(req->host);
576 free(req->path);
577 free(req->hostport);
578 spdylay_gzip_inflate_del(req->inflater);
579}
580
581/*
582 * Fetches the resource denoted by |uri|.
583 */
584static void fetch_uri(const struct URI *uri)
585{
586 spdylay_session_callbacks callbacks;
587 int fd;
588 struct Request req;
589 struct Connection connection;
590 int rv;
591 nfds_t npollfds = 1;
592 struct pollfd pollfds[1];
593 uint16_t spdy_proto_version = 3;
594
595 request_init(&req, uri);
596
597 setup_spdylay_callbacks(&callbacks);
598
599 /* Establish connection and setup SSL */
600 fd = connect_to(req.host, req.port);
601
602 connection.fd = fd;
603 connection.want_io = IO_NONE;
604
605 /* Here make file descriptor non-block */
606 make_non_block(fd);
607 set_tcp_nodelay(fd);
608
609 printf("[INFO] SPDY protocol version = %d\n", spdy_proto_version);
610 rv = spdylay_session_client_new(&connection.session, spdy_proto_version,
611 &callbacks, &connection);
612 if(rv != 0) {
613 diec("spdylay_session_client_new", rv);
614 }
615
616 /* Submit the HTTP request to the outbound queue. */
617 submit_request(&connection, &req);
618
619 pollfds[0].fd = fd;
620 ctl_poll(pollfds, &connection);
621
622 /* Event loop */
623 while(spdylay_session_want_read(connection.session) ||
624 spdylay_session_want_write(connection.session)) {
625 int nfds = poll(pollfds, npollfds, -1);
626 if(nfds == -1) {
627 dief("poll", strerror(errno));
628 }
629 if(pollfds[0].revents & (POLLIN | POLLOUT)) {
630 exec_io(&connection);
631 }
632 if((pollfds[0].revents & POLLHUP) || (pollfds[0].revents & POLLERR)) {
633 die("Connection error");
634 }
635 ctl_poll(pollfds, &connection);
636 }
637
638 /* Resource cleanup */
639 spdylay_session_del(connection.session);
640 shutdown(fd, SHUT_WR);
641 close(fd);
642 request_free(&req);
643}
644
645static int parse_uri(struct URI *res, const char *uri)
646{
647 /* We only interested in https */
648 size_t len, i, offset;
649 memset(res, 0, sizeof(struct URI));
650 len = strlen(uri);
651 if(len < 9 || memcmp("https://", uri, 8) != 0) {
652 return -1;
653 }
654 offset = 8;
655 res->host = res->hostport = &uri[offset];
656 res->hostlen = 0;
657 if(uri[offset] == '[') {
658 /* IPv6 literal address */
659 ++offset;
660 ++res->host;
661 for(i = offset; i < len; ++i) {
662 if(uri[i] == ']') {
663 res->hostlen = i-offset;
664 offset = i+1;
665 break;
666 }
667 }
668 } else {
669 const char delims[] = ":/?#";
670 for(i = offset; i < len; ++i) {
671 if(strchr(delims, uri[i]) != NULL) {
672 break;
673 }
674 }
675 res->hostlen = i-offset;
676 offset = i;
677 }
678 if(res->hostlen == 0) {
679 return -1;
680 }
681 /* Assuming https */
682 res->port = 443;
683 if(offset < len) {
684 if(uri[offset] == ':') {
685 /* port */
686 const char delims[] = "/?#";
687 int port = 0;
688 ++offset;
689 for(i = offset; i < len; ++i) {
690 if(strchr(delims, uri[i]) != NULL) {
691 break;
692 }
693 if('0' <= uri[i] && uri[i] <= '9') {
694 port *= 10;
695 port += uri[i]-'0';
696 if(port > 65535) {
697 return -1;
698 }
699 } else {
700 return -1;
701 }
702 }
703 if(port == 0) {
704 return -1;
705 }
706 offset = i;
707 res->port = port;
708 }
709 }
710 res->hostportlen = uri+offset-res->host;
711 for(i = offset; i < len; ++i) {
712 if(uri[i] == '#') {
713 break;
714 }
715 }
716 if(i-offset == 0) {
717 res->path = "/";
718 res->pathlen = 1;
719 } else {
720 res->path = &uri[offset];
721 res->pathlen = i-offset;
722 }
723 return 0;
724}
725
726
727/*****
728 * end of code needed to utilize spdylay
729 */
730
731
732/*****
733 * start of code needed to utilize microspdy
734 */
735
736
737void
738standard_request_handler(void *cls,
739 struct SPDY_Request * request,
740 uint8_t priority,
741 const char *method,
742 const char *path,
743 const char *version,
744 const char *host,
745 const char *scheme,
746 struct SPDY_NameValue * headers)
747{
748 struct SPDY_Response *response=NULL;
749
750 if(strcmp(CLS,cls)!=0)
751 {
752 killchild(child,"wrong cls");
753 }
754
755 response = SPDY_build_response(200,NULL,SPDY_HTTP_VERSION_1_1,NULL,RESPONSE_BODY,strlen(RESPONSE_BODY));
756
757 if(NULL==response){
758 fprintf(stdout,"no response obj\n");
759 exit(3);
760 }
761
762 if(SPDY_queue_response(request,response,true,false,NULL,(void*)strdup(path))!=SPDY_YES)
763 {
764 fprintf(stdout,"queue\n");
765 exit(4);
766 }
767}
768
769void
770session_closed_handler (void *cls,
771 struct SPDY_Session * session,
772 int by_client)
773{
774 printf("session_closed_handler called\n");
775
776 if(strcmp(CLS,cls)!=0)
777 {
778 killchild(child,"wrong cls");
779 }
780
781 if(SPDY_YES != by_client)
782 {
783 //killchild(child,"wrong by_client");
784 printf("session closed by server\n");
785 }
786 else
787 {
788 printf("session closed by client\n");
789 }
790
791 if(NULL == session)
792 {
793 killchild(child,"session is NULL");
794 }
795
796 session_closed_called = 1;
797}
798
799
800/*****
801 * end of code needed to utilize microspdy
802 */
803
804//child process
805void
806childproc(int port)
807{
808 struct URI uri;
809 struct sigaction act;
810 int rv;
811 char *uristr;
812
813 memset(&act, 0, sizeof(struct sigaction));
814 act.sa_handler = SIG_IGN;
815 sigaction(SIGPIPE, &act, 0);
816
817 asprintf(&uristr, "https://127.0.0.1:%i/",port);
818 if(NULL == (rcvbuf = malloc(strlen(RESPONSE_BODY)+1)))
819 killparent(parent,"no memory");
820
821 SSL_load_error_strings();
822 SSL_library_init();
823
824 rv = parse_uri(&uri, uristr);
825 if(rv != 0) {
826 killparent(parent,"parse_uri failed");
827 }
828 fetch_uri(&uri);
829
830 if(strcmp(rcvbuf, RESPONSE_BODY))
831 killparent(parent,"received data is different");
832}
833
834//parent proc
835int
836parentproc( int port)
837{
838 int childstatus;
839 unsigned long long timeoutlong=0;
840 struct timeval timeout;
841 int ret;
842 fd_set read_fd_set;
843 fd_set write_fd_set;
844 fd_set except_fd_set;
845 int maxfd = -1;
846 struct SPDY_Daemon *daemon;
847
848 SPDY_init();
849
850 daemon = SPDY_start_daemon(port,
851 NULL,
852 NULL,
853 NULL,&session_closed_handler,&standard_request_handler,NULL,CLS,
854 SPDY_DAEMON_OPTION_IO_SUBSYSTEM, SPDY_IO_SUBSYSTEM_RAW,
855 SPDY_DAEMON_OPTION_END);
856
857 if(NULL==daemon){
858 printf("no daemon\n");
859 return 1;
860 }
861
862 timeout.tv_usec = 0;
863
864 do
865 {
866 FD_ZERO(&read_fd_set);
867 FD_ZERO(&write_fd_set);
868 FD_ZERO(&except_fd_set);
869
870 ret = SPDY_get_timeout(daemon, &timeoutlong);
871 if(SPDY_NO == ret || timeoutlong > 1)
872 {
873 timeout.tv_sec = 1;
874 }
875 else
876 {
877 timeout.tv_sec = timeoutlong;
878 }
879
880 maxfd = SPDY_get_fdset (daemon,
881 &read_fd_set,
882 &write_fd_set,
883 &except_fd_set);
884
885 ret = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout);
886
887 switch(ret) {
888 case -1:
889 printf("select error: %i\n", errno);
890 killchild(child, "select error");
891 break;
892 case 0:
893
894 break;
895 default:
896 SPDY_run(daemon);
897
898 break;
899 }
900 }
901 while(waitpid(child,&childstatus,WNOHANG) != child);
902
903 //give chance to the client to close socket and handle this in run
904 usleep(100000);
905 SPDY_run(daemon);
906
907 SPDY_stop_daemon(daemon);
908
909 SPDY_deinit();
910
911 return WEXITSTATUS(childstatus);
912}
913
914int main(int argc, char **argv)
915{
916 int port = get_port(12123);
917 parent = getpid();
918
919 child = fork();
920 if (child == -1)
921 {
922 fprintf(stderr, "can't fork, error %d\n", errno);
923 exit(EXIT_FAILURE);
924 }
925
926 if (child == 0)
927 {
928 childproc(port);
929 _exit(0);
930 }
931 else
932 {
933 int ret = parentproc(port);
934 if(1 == session_closed_called && 0 == ret)
935 exit(0);
936 else
937 exit(ret ? ret : 21);
938 }
939 return 1;
940}