aboutsummaryrefslogtreecommitdiff
path: root/src/microspdy
diff options
context:
space:
mode:
Diffstat (limited to 'src/microspdy')
-rw-r--r--src/microspdy/EXPORT.sym2
-rw-r--r--src/microspdy/Makefile.am38
-rw-r--r--src/microspdy/alstructures.c41
-rw-r--r--src/microspdy/alstructures.h79
-rw-r--r--src/microspdy/applicationlayer.c679
-rw-r--r--src/microspdy/applicationlayer.h31
-rw-r--r--src/microspdy/compression.c441
-rw-r--r--src/microspdy/compression.h117
-rw-r--r--src/microspdy/daemon.c515
-rw-r--r--src/microspdy/daemon.h121
-rw-r--r--src/microspdy/internal.c38
-rw-r--r--src/microspdy/internal.h189
-rw-r--r--src/microspdy/session.c1554
-rw-r--r--src/microspdy/session.h248
-rw-r--r--src/microspdy/stream.c151
-rw-r--r--src/microspdy/stream.h65
-rw-r--r--src/microspdy/structures.c612
-rw-r--r--src/microspdy/structures.h1128
-rw-r--r--src/microspdy/tls.c255
-rw-r--r--src/microspdy/tls.h171
20 files changed, 6475 insertions, 0 deletions
diff --git a/src/microspdy/EXPORT.sym b/src/microspdy/EXPORT.sym
new file mode 100644
index 00000000..eb6604bc
--- /dev/null
+++ b/src/microspdy/EXPORT.sym
@@ -0,0 +1,2 @@
1SPDY_start_daemon
2SPDY_stop_daemon
diff --git a/src/microspdy/Makefile.am b/src/microspdy/Makefile.am
new file mode 100644
index 00000000..0509134a
--- /dev/null
+++ b/src/microspdy/Makefile.am
@@ -0,0 +1,38 @@
1if USE_PRIVATE_PLIBC_H
2 PLIBC_INCLUDE = -I$(top_srcdir)/src/include/plibc
3endif
4
5AM_CPPFLAGS = \
6 $(PLIBC_INCLUDE) \
7 -I$(top_srcdir)/src/include \
8 -I$(top_srcdir)/src/microspdy
9
10
11EXTRA_DIST = EXPORT.sym
12
13
14lib_LTLIBRARIES = \
15 libmicrospdy.la
16
17libmicrospdy_la_SOURCES = \
18 tls.h tls.c \
19 structures.h structures.c \
20 internal.h internal.c \
21 daemon.h daemon.c \
22 stream.h stream.c \
23 compression.h compression.c \
24 session.h session.c \
25 applicationlayer.c applicationlayer.h \
26 alstructures.c alstructures.h
27
28
29libmicrospdy_la_LDFLAGS = \
30 $(SPDY_LIB_LDFLAGS)
31
32libmicrospdy_la_CFLAGS = -Wextra \
33 $(SPDY_LIB_CFLAGS)
34
35
36if USE_COVERAGE
37 AM_CFLAGS = --coverage
38endif
diff --git a/src/microspdy/alstructures.c b/src/microspdy/alstructures.c
new file mode 100644
index 00000000..3495b052
--- /dev/null
+++ b/src/microspdy/alstructures.c
@@ -0,0 +1,41 @@
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 alstructures.c
21 * @brief structures only for the application layer
22 * @author Andrey Uzunov
23 */
24
25#include "platform.h"
26#include "alstructures.h"
27#include "internal.h"
28
29void
30SPDY_destroy_request (struct SPDY_Request *request)
31{
32 if(NULL == request)
33 {
34 SPDYF_DEBUG("request is NULL");
35 return;
36 }
37 //strings into request struct are just references to strings in
38 //headers, so no need to free them twice
39 SPDY_name_value_destroy(request->headers);
40 free(request);
41}
diff --git a/src/microspdy/alstructures.h b/src/microspdy/alstructures.h
new file mode 100644
index 00000000..61eb8cb6
--- /dev/null
+++ b/src/microspdy/alstructures.h
@@ -0,0 +1,79 @@
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 alstructures.h
21 * @brief structures only for the application layer
22 * @author Andrey Uzunov
23 */
24
25#ifndef ALSTRUCTURES_H
26#define ALSTRUCTURES_H
27
28#include "platform.h"
29
30
31/**
32 * Represents a SPDY request.
33 */
34struct SPDY_Request
35{
36 /**
37 * SPDY stream in whose context the request was received
38 */
39 struct SPDYF_Stream *stream;
40
41 /**
42 * Other HTTP headers from the request
43 */
44 struct SPDY_NameValue *headers;
45
46 /**
47 * HTTP method
48 */
49 char *method;
50
51 /**
52 * HTTP path
53 */
54 char *path;
55
56 /**
57 * HTTP version just like in HTTP request/response:
58 * "HTTP/1.0" or "HTTP/1.1" currently
59 */
60 char *version;
61
62 /**
63 * called host as in HTTP
64 */
65 char *host;
66
67 /**
68 * The scheme used ("http" or "https")
69 */
70 char *scheme;
71
72 /**
73 * Extra field to be used by the user with set/get func for whatever
74 * purpose he wants.
75 */
76 void *user_cls;
77};
78
79#endif
diff --git a/src/microspdy/applicationlayer.c b/src/microspdy/applicationlayer.c
new file mode 100644
index 00000000..cbe484bc
--- /dev/null
+++ b/src/microspdy/applicationlayer.c
@@ -0,0 +1,679 @@
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 applicationlayer.c
21 * @brief SPDY application or HTTP layer
22 * @author Andrey Uzunov
23 */
24
25#include "platform.h"
26#include "applicationlayer.h"
27#include "alstructures.h"
28#include "structures.h"
29#include "internal.h"
30#include "daemon.h"
31#include "session.h"
32
33
34/**
35 * Callback called when new stream is created. It extracts the info from
36 * the stream to create (HTTP) request object and pass it to the client.
37 *
38 * @param cls
39 * @param stream the new SPDY stream
40 * @return SPDY_YES on success, SPDY_NO on memomry error
41 */
42static int
43spdy_handler_new_stream (void *cls,
44 struct SPDYF_Stream * stream)
45{
46 (void)cls;
47 uint i;
48 char *method = NULL;
49 char *path = NULL;
50 char *version = NULL;
51 char *host = NULL;
52 char *scheme = NULL;
53 struct SPDY_Request * request = NULL;
54 struct SPDY_NameValue * headers = NULL;
55 struct SPDY_NameValue * iterator = stream->headers;
56 struct SPDY_Daemon *daemon;
57
58 daemon = stream->session->daemon;
59
60 //if the user doesn't care, ignore it
61 if(NULL == daemon->new_request_cb)
62 return SPDY_YES;
63
64 if(NULL == (headers=SPDY_name_value_create()))
65 goto free_and_fail;
66
67 if(NULL==(request = malloc(sizeof(struct SPDY_Request))))
68 goto free_and_fail;
69
70 memset(request, 0, sizeof(struct SPDY_Request));
71 request->stream = stream;
72
73 /* extract the mandatory fields from stream->headers' structure
74 * to pass them to the client */
75 while(iterator != NULL)
76 {
77 if(strcmp(":method",iterator->name) == 0)
78 {
79 if(1 != iterator->num_values)
80 break;
81 method = iterator->value[0];
82 }
83 else if(strcmp(":path",iterator->name) == 0)
84 {
85 if(1 != iterator->num_values)
86 break;
87 path = iterator->value[0];
88 }
89 else if(strcmp(":version",iterator->name) == 0)
90 {
91 if(1 != iterator->num_values)
92 break;
93 version = iterator->value[0];
94 }
95 else if(strcmp(":host",iterator->name) == 0)
96 {
97 //TODO can it have more values?
98 if(1 != iterator->num_values)
99 break;
100 host = iterator->value[0];
101 }
102 else if(strcmp(":scheme",iterator->name) == 0)
103 {
104 if(1 != iterator->num_values)
105 break;
106 scheme = iterator->value[0];
107 }
108 else
109 for(i=0; i<iterator->num_values; ++i)
110 if (SPDY_YES != SPDY_name_value_add(headers,iterator->name,iterator->value[i]))
111 goto free_and_fail;
112
113 iterator = iterator->next;
114 }
115
116 request->method=method;
117 request->path=path;
118 request->version=version;
119 request->host=host;
120 request->scheme=scheme;
121 request->headers=headers;
122
123 //check request validity, all these fields are mandatory for a request
124 if(NULL == method || strlen(method) == 0
125 || NULL == path || strlen(path) == 0
126 || NULL == version || strlen(version) == 0
127 || NULL == host || strlen(host) == 0
128 || NULL == scheme || strlen(scheme) == 0
129 )
130 {
131 //TODO HTTP 400 Bad Request must be answered
132
133 SPDYF_DEBUG("Bad request");
134
135 SPDY_destroy_request(request);
136
137 return SPDY_YES;
138 }
139
140 //call client's callback function to notify
141 daemon->new_request_cb(daemon->cls,
142 request,
143 stream->priority,
144 method,
145 path,
146 version,
147 host,
148 scheme,
149 headers);
150
151 return SPDY_YES;
152
153 //for GOTO
154 free_and_fail:
155
156 SPDY_name_value_destroy(headers);
157 return SPDY_NO;
158}
159
160
161/**
162 * Callback to be called when the response queue object was handled and
163 * the data was already sent or discarded.
164 *
165 * @param cls
166 * @param response_queue the object which is being handled
167 * @param status shows if actually the response was sent or it was
168 * discarded by the lib for any reason (e.g., closing session,
169 * closing stream, stopping daemon, etc.). It is possible that
170 * status indicates an error but parts of the response headers
171 * and/or body (in one
172 * or several frames) were already sent to the client.
173 */
174static void
175spdy_handler_response_queue_result(void * cls,
176 struct SPDYF_Response_Queue *response_queue,
177 enum SPDY_RESPONSE_RESULT status)
178{
179 int streamopened;
180 struct SPDY_Request *request = (struct SPDY_Request *)cls;
181
182 SPDYF_ASSERT(NULL == response_queue->data_frame
183 && NULL != response_queue->control_frame
184 || NULL != response_queue->data_frame
185 && NULL == response_queue->control_frame,
186 "response queue must have either control frame or data frame");
187
188 streamopened = !response_queue->stream->is_out_closed;
189
190 response_queue->rrcb(response_queue->rrcb_cls, response_queue->response, request, status, streamopened);
191}
192
193
194int
195SPDY_init ()
196{
197 SPDYF_ASSERT(SPDYF_BUFFER_SIZE >= SPDY_MAX_SUPPORTED_FRAME_SIZE,
198 "Buffer size is less than max supported frame size!");
199 SPDYF_ASSERT(SPDY_MAX_SUPPORTED_FRAME_SIZE >= 32,
200 "Max supported frame size must be bigger than the minimal value!");
201 SPDYF_tls_global_init();
202 return SPDY_YES;
203}
204
205
206void
207SPDY_deinit ()
208{
209 //currently nothing to be freed/deinited
210 //SPDYF_tls_global_deinit doesn't do anything now
211 //SPDYF_tls_global_deinit();
212}
213
214
215void
216SPDY_run (struct SPDY_Daemon *daemon)
217{
218 if(NULL == daemon)
219 {
220 SPDYF_DEBUG("daemon is NULL");
221 return;
222 }
223
224 SPDYF_run(daemon);
225}
226
227
228int
229SPDY_get_timeout (struct SPDY_Daemon *daemon,
230 unsigned long long *timeout)
231{
232 if(NULL == daemon)
233 {
234 SPDYF_DEBUG("daemon is NULL");
235 return SPDY_INPUT_ERROR;
236 }
237
238 return SPDYF_get_timeout(daemon,timeout);
239}
240
241
242int
243SPDY_get_fdset (struct SPDY_Daemon *daemon,
244 fd_set *read_fd_set,
245 fd_set *write_fd_set,
246 fd_set *except_fd_set)
247{
248 if(NULL == daemon
249 || NULL == read_fd_set
250 || NULL == write_fd_set
251 || NULL == except_fd_set)
252 {
253 SPDYF_DEBUG("a parameter is NULL");
254 return SPDY_INPUT_ERROR;
255 }
256
257 return SPDYF_get_fdset(daemon,
258 read_fd_set,
259 write_fd_set,
260 except_fd_set,
261 false);
262}
263
264
265struct SPDY_Daemon *
266SPDY_start_daemon (uint16_t port,
267 const char *certfile,
268 const char *keyfile,
269 SPDY_NewSessionCallback nscb,
270 SPDY_SessionClosedCallback sccb,
271 SPDY_NewRequestCallback nrcb,
272 SPDY_NewPOSTDataCallback npdcb,
273 void * cls,
274 ...)
275{
276 struct SPDY_Daemon *daemon;
277 va_list valist;
278
279 if(NULL == certfile)
280 {
281 SPDYF_DEBUG("certfile is NULL");
282 return NULL;
283 }
284 if(NULL == keyfile)
285 {
286 SPDYF_DEBUG("keyfile is NULL");
287 return NULL;
288 }
289
290 va_start(valist, cls);
291 daemon = SPDYF_start_daemon_va ( port,
292 certfile,
293 keyfile,
294 nscb,
295 sccb,
296 nrcb,
297 npdcb,
298 &spdy_handler_new_stream,
299 cls,
300 NULL,
301 valist
302 );
303 va_end(valist);
304
305 return daemon;
306}
307
308
309void
310SPDY_stop_daemon (struct SPDY_Daemon *daemon)
311{
312 if(NULL == daemon)
313 {
314 SPDYF_DEBUG("daemon is NULL");
315 return;
316 }
317
318 SPDYF_stop_daemon(daemon);
319}
320
321
322struct SPDY_Response *
323SPDY_build_response(int status,
324 const char * statustext,
325 const char * version,
326 struct SPDY_NameValue * headers,
327 const void * data,
328 size_t size)
329{
330 struct SPDY_Response *response = NULL;
331 struct SPDY_NameValue ** all_headers = NULL;
332 char *fullstatus = NULL;
333 int ret;
334 int num_hdr_containers = 1;
335
336 if(NULL == version)
337 {
338 SPDYF_DEBUG("version is NULL");
339 return NULL;
340 }
341
342 if(NULL == (response = malloc(sizeof(struct SPDY_Response))))
343 goto free_and_fail;
344 memset(response, 0, sizeof(struct SPDY_Response));
345
346 if(NULL != headers)
347 num_hdr_containers = 2;
348
349 if(NULL == (all_headers = malloc(num_hdr_containers * sizeof(struct SPDY_NameValue *))))
350 goto free_and_fail;
351 memset(all_headers, 0, num_hdr_containers * sizeof(struct SPDY_NameValue *));
352
353 if(2 == num_hdr_containers)
354 all_headers[1] = headers;
355
356 if(NULL == (all_headers[0] = SPDY_name_value_create()))
357 goto free_and_fail;
358
359 if(NULL == statustext)
360 ret = asprintf(&fullstatus, "%i", status);
361 else
362 ret = asprintf(&fullstatus, "%i %s", status, statustext);
363 if(-1 == ret)
364 goto free_and_fail;
365
366 if(SPDY_YES != SPDY_name_value_add(all_headers[0], ":status", fullstatus))
367 goto free_and_fail;
368
369 free(fullstatus);
370 fullstatus = NULL;
371
372 if(SPDY_YES != SPDY_name_value_add(all_headers[0], ":version", version))
373 goto free_and_fail;
374
375 if(0 >= (response->headers_size = SPDYF_name_value_to_stream(all_headers,
376 num_hdr_containers,
377 &(response->headers))))
378 goto free_and_fail;
379
380 SPDY_name_value_destroy(all_headers[0]);
381 free(all_headers);
382
383 if(size > 0)
384 {
385 //copy the data to the response object
386 if(NULL == (response->data = malloc(size)))
387 {
388 free(response->headers);
389 goto free_and_fail;
390 }
391 memcpy(response->data, data, size);
392 response->data_size = size;
393 }
394
395 return response;
396
397 //for GOTO
398 free_and_fail:
399
400 free(fullstatus);
401 if(NULL != all_headers)
402 SPDY_name_value_destroy(all_headers[0]);
403 free(all_headers);
404 free(response);
405
406 return NULL;
407}
408
409
410struct SPDY_Response *
411SPDY_build_response_with_callback(int status,
412 const char * statustext,
413 const char * version,
414 struct SPDY_NameValue * headers,
415 SPDY_ResponseCallback rcb,
416 void *rcb_cls,
417 uint32_t block_size)
418{
419 struct SPDY_Response *response;
420
421 if(NULL == rcb)
422 {
423 SPDYF_DEBUG("rcb is NULL");
424 return NULL;
425 }
426 if(block_size > SPDY_MAX_SUPPORTED_FRAME_SIZE)
427 {
428 SPDYF_DEBUG("block_size is wrong");
429 return NULL;
430 }
431
432 if(0 == block_size)
433 block_size = SPDY_MAX_SUPPORTED_FRAME_SIZE;
434
435 response = SPDY_build_response(status,
436 statustext,
437 version,
438 headers,
439 NULL,
440 0);
441
442 if(NULL == response)
443 {
444 return NULL;
445 }
446
447 response->rcb = rcb;
448 response->rcb_cls = rcb_cls;
449 response->rcb_block_size = block_size;
450
451 return response;
452}
453
454
455int
456SPDY_queue_response (struct SPDY_Request * request,
457 struct SPDY_Response *response,
458 bool closestream,
459 bool consider_priority,
460 SPDY_ResponseResultCallback rrcb,
461 void * rrcb_cls)
462{
463 struct SPDYF_Response_Queue *headers_to_queue;
464 struct SPDYF_Response_Queue *body_to_queue;
465 SPDYF_ResponseQueueResultCallback frqcb = NULL;
466 void *frqcb_cls = NULL;
467 int int_consider_priority = consider_priority ? SPDY_YES : SPDY_NO;
468
469 if(NULL == request)
470 {
471 SPDYF_DEBUG("request is NULL");
472 return SPDY_INPUT_ERROR;
473 }
474 if(NULL == response)
475 {
476 SPDYF_DEBUG("request is NULL");
477 return SPDY_INPUT_ERROR;
478 }
479
480 if(request->stream->is_out_closed
481 || SPDY_SESSION_STATUS_CLOSING == request->stream->session->status)
482 return SPDY_NO;
483
484 if(NULL != rrcb)
485 {
486 frqcb_cls = request;
487 frqcb = &spdy_handler_response_queue_result;
488 }
489
490 if(response->data_size > 0)
491 {
492 //SYN_REPLY and DATA will be queued
493
494 if(NULL == (headers_to_queue = SPDYF_response_queue_create(false,
495 response->headers,
496 response->headers_size,
497 response,
498 request->stream,
499 false,
500 NULL,
501 NULL,
502 NULL,
503 NULL)))
504 {
505 return SPDY_NO;
506 }
507
508 if(NULL == (body_to_queue = SPDYF_response_queue_create(true,
509 response->data,
510 response->data_size,
511 response,
512 request->stream,
513 closestream,
514 frqcb,
515 frqcb_cls,
516 rrcb,
517 rrcb_cls)))
518 {
519 SPDYF_response_queue_destroy(headers_to_queue);
520 return SPDY_NO;
521 }
522
523 SPDYF_queue_response (headers_to_queue,
524 request->stream->session,
525 int_consider_priority);
526
527 SPDYF_queue_response (body_to_queue,
528 request->stream->session,
529 int_consider_priority);
530 }
531 else if(NULL == response->rcb)
532 {
533 //no "body" will be queued, e.g. HTTP 404 without body
534
535 if(NULL == (headers_to_queue = SPDYF_response_queue_create(false,
536 response->headers,
537 response->headers_size,
538 response,
539 request->stream,
540 closestream,
541 frqcb,
542 frqcb_cls,
543 rrcb,
544 rrcb_cls)))
545 {
546 return SPDY_NO;
547 }
548
549 SPDYF_queue_response (headers_to_queue,
550 request->stream->session,
551 int_consider_priority);
552 }
553 else
554 {
555 //response with callbacks
556
557 if(NULL == (headers_to_queue = SPDYF_response_queue_create(false,
558 response->headers,
559 response->headers_size,
560 response,
561 request->stream,
562 false,
563 NULL,
564 NULL,
565 NULL,
566 NULL)))
567 {
568 return SPDY_NO;
569 }
570
571 if(NULL == (body_to_queue = SPDYF_response_queue_create(true,
572 response->data,
573 response->data_size,
574 response,
575 request->stream,
576 closestream,
577 frqcb,
578 frqcb_cls,
579 rrcb,
580 rrcb_cls)))
581 {
582 SPDYF_response_queue_destroy(headers_to_queue);
583 return SPDY_NO;
584 }
585
586 SPDYF_queue_response (headers_to_queue,
587 request->stream->session,
588 int_consider_priority);
589
590 SPDYF_queue_response (body_to_queue,
591 request->stream->session,
592 int_consider_priority);
593 }
594
595 return SPDY_YES;
596}
597
598
599socklen_t
600SPDY_get_remote_addr(struct SPDY_Session * session,
601 struct sockaddr ** addr)
602{
603 if(NULL == session)
604 {
605 SPDYF_DEBUG("session is NULL");
606 return 0;
607 }
608
609 *addr = session->addr;
610
611 return session->addr_len;
612}
613
614
615struct SPDY_Session *
616SPDY_get_session_for_request(const struct SPDY_Request * request)
617{
618 if(NULL == request)
619 {
620 SPDYF_DEBUG("request is NULL");
621 return NULL;
622 }
623
624 return request->stream->session;
625}
626
627
628void *
629SPDY_get_cls_from_session(struct SPDY_Session * session)
630{
631 if(NULL == session)
632 {
633 SPDYF_DEBUG("session is NULL");
634 return NULL;
635 }
636
637 return session->user_cls;
638}
639
640
641void
642SPDY_set_cls_to_session(struct SPDY_Session * session,
643 void * cls)
644{
645 if(NULL == session)
646 {
647 SPDYF_DEBUG("session is NULL");
648 return;
649 }
650
651 session->user_cls = cls;
652}
653
654
655void *
656SPDY_get_cls_from_request(struct SPDY_Request * request)
657{
658 if(NULL == request)
659 {
660 SPDYF_DEBUG("request is NULL");
661 return NULL;
662 }
663
664 return request->user_cls;
665}
666
667
668void
669SPDY_set_cls_to_request(struct SPDY_Request * request,
670 void * cls)
671{
672 if(NULL == request)
673 {
674 SPDYF_DEBUG("request is NULL");
675 return;
676 }
677
678 request->user_cls = cls;
679}
diff --git a/src/microspdy/applicationlayer.h b/src/microspdy/applicationlayer.h
new file mode 100644
index 00000000..53e3be0a
--- /dev/null
+++ b/src/microspdy/applicationlayer.h
@@ -0,0 +1,31 @@
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 applicationlayer.h
21 * @brief SPDY application or HTTP layer
22 * @author Andrey Uzunov
23 */
24
25#ifndef APPLICATIONLAYER_H
26#define APPLICATIONLAYER_H
27
28#include "platform.h"
29
30
31#endif
diff --git a/src/microspdy/compression.c b/src/microspdy/compression.c
new file mode 100644
index 00000000..5b212d30
--- /dev/null
+++ b/src/microspdy/compression.c
@@ -0,0 +1,441 @@
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 compression.c
21 * @brief zlib handling functions
22 * @author Andrey Uzunov
23 */
24
25#include "platform.h"
26#include "structures.h"
27#include "internal.h"
28#include "compression.h"
29
30/* spdy ver 3 specific dictionary used by zlib */
31static const unsigned char
32spdyf_zlib_dictionary[] = {
33 0x00, 0x00, 0x00, 0x07, 0x6f, 0x70, 0x74, 0x69, // - - - - o p t i
34 0x6f, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x04, 0x68, // o n s - - - - h
35 0x65, 0x61, 0x64, 0x00, 0x00, 0x00, 0x04, 0x70, // e a d - - - - p
36 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x03, 0x70, // o s t - - - - p
37 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x64, 0x65, // u t - - - - d e
38 0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x00, 0x05, // l e t e - - - -
39 0x74, 0x72, 0x61, 0x63, 0x65, 0x00, 0x00, 0x00, // t r a c e - - -
40 0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x00, // - a c c e p t -
41 0x00, 0x00, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, // - - - a c c e p
42 0x74, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, // t - c h a r s e
43 0x74, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x63, 0x63, // t - - - - a c c
44 0x65, 0x70, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, // e p t - e n c o
45 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x0f, // d i n g - - - -
46 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c, // a c c e p t - l
47 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, // a n g u a g e -
48 0x00, 0x00, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, // - - - a c c e p
49 0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, // t - r a n g e s
50 0x00, 0x00, 0x00, 0x03, 0x61, 0x67, 0x65, 0x00, // - - - - a g e -
51 0x00, 0x00, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77, // - - - a l l o w
52 0x00, 0x00, 0x00, 0x0d, 0x61, 0x75, 0x74, 0x68, // - - - - a u t h
53 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, // o r i z a t i o
54 0x6e, 0x00, 0x00, 0x00, 0x0d, 0x63, 0x61, 0x63, // n - - - - c a c
55 0x68, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, // h e - c o n t r
56 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f, // o l - - - - c o
57 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, // n n e c t i o n
58 0x00, 0x00, 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, // - - - - c o n t
59 0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65, // e n t - b a s e
60 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74, // - - - - c o n t
61 0x65, 0x6e, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, // e n t - e n c o
62 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, // d i n g - - - -
63 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, // c o n t e n t -
64 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, // l a n g u a g e
65 0x00, 0x00, 0x00, 0x0e, 0x63, 0x6f, 0x6e, 0x74, // - - - - c o n t
66 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, // e n t - l e n g
67 0x74, 0x68, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, // t h - - - - c o
68 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f, // n t e n t - l o
69 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, // c a t i o n - -
70 0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, // - - c o n t e n
71 0x74, 0x2d, 0x6d, 0x64, 0x35, 0x00, 0x00, 0x00, // t - m d 5 - - -
72 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, // - c o n t e n t
73 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, // - r a n g e - -
74 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, // - - c o n t e n
75 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00, // t - t y p e - -
76 0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x00, 0x00, // - - d a t e - -
77 0x00, 0x04, 0x65, 0x74, 0x61, 0x67, 0x00, 0x00, // - - e t a g - -
78 0x00, 0x06, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, // - - e x p e c t
79 0x00, 0x00, 0x00, 0x07, 0x65, 0x78, 0x70, 0x69, // - - - - e x p i
80 0x72, 0x65, 0x73, 0x00, 0x00, 0x00, 0x04, 0x66, // r e s - - - - f
81 0x72, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x68, // r o m - - - - h
82 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x69, // o s t - - - - i
83 0x66, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, // f - m a t c h -
84 0x00, 0x00, 0x11, 0x69, 0x66, 0x2d, 0x6d, 0x6f, // - - - i f - m o
85 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73, // d i f i e d - s
86 0x69, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x0d, // i n c e - - - -
87 0x69, 0x66, 0x2d, 0x6e, 0x6f, 0x6e, 0x65, 0x2d, // i f - n o n e -
88 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00, // m a t c h - - -
89 0x08, 0x69, 0x66, 0x2d, 0x72, 0x61, 0x6e, 0x67, // - i f - r a n g
90 0x65, 0x00, 0x00, 0x00, 0x13, 0x69, 0x66, 0x2d, // e - - - - i f -
91 0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, // u n m o d i f i
92 0x65, 0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65, // e d - s i n c e
93 0x00, 0x00, 0x00, 0x0d, 0x6c, 0x61, 0x73, 0x74, // - - - - l a s t
94 0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, // - m o d i f i e
95 0x64, 0x00, 0x00, 0x00, 0x08, 0x6c, 0x6f, 0x63, // d - - - - l o c
96 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, // a t i o n - - -
97 0x0c, 0x6d, 0x61, 0x78, 0x2d, 0x66, 0x6f, 0x72, // - m a x - f o r
98 0x77, 0x61, 0x72, 0x64, 0x73, 0x00, 0x00, 0x00, // w a r d s - - -
99 0x06, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x00, // - p r a g m a -
100 0x00, 0x00, 0x12, 0x70, 0x72, 0x6f, 0x78, 0x79, // - - - p r o x y
101 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, // - a u t h e n t
102 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, // i c a t e - - -
103 0x13, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61, // - p r o x y - a
104 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, // u t h o r i z a
105 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x05, // t i o n - - - -
106 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00, // r a n g e - - -
107 0x07, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72, // - r e f e r e r
108 0x00, 0x00, 0x00, 0x0b, 0x72, 0x65, 0x74, 0x72, // - - - - r e t r
109 0x79, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x00, // y - a f t e r -
110 0x00, 0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, // - - - s e r v e
111 0x72, 0x00, 0x00, 0x00, 0x02, 0x74, 0x65, 0x00, // r - - - - t e -
112 0x00, 0x00, 0x07, 0x74, 0x72, 0x61, 0x69, 0x6c, // - - - t r a i l
113 0x65, 0x72, 0x00, 0x00, 0x00, 0x11, 0x74, 0x72, // e r - - - - t r
114 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x65, // a n s f e r - e
115 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00, // n c o d i n g -
116 0x00, 0x00, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61, // - - - u p g r a
117 0x64, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x73, // d e - - - - u s
118 0x65, 0x72, 0x2d, 0x61, 0x67, 0x65, 0x6e, 0x74, // e r - a g e n t
119 0x00, 0x00, 0x00, 0x04, 0x76, 0x61, 0x72, 0x79, // - - - - v a r y
120 0x00, 0x00, 0x00, 0x03, 0x76, 0x69, 0x61, 0x00, // - - - - v i a -
121 0x00, 0x00, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69, // - - - w a r n i
122 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, 0x77, 0x77, // n g - - - - w w
123 0x77, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, // w - a u t h e n
124 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, // t i c a t e - -
125 0x00, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, // - - m e t h o d
126 0x00, 0x00, 0x00, 0x03, 0x67, 0x65, 0x74, 0x00, // - - - - g e t -
127 0x00, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, // - - - s t a t u
128 0x73, 0x00, 0x00, 0x00, 0x06, 0x32, 0x30, 0x30, // s - - - - 2 0 0
129 0x20, 0x4f, 0x4b, 0x00, 0x00, 0x00, 0x07, 0x76, // - O K - - - - v
130 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00, // e r s i o n - -
131 0x00, 0x08, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, // - - H T T P - 1
132 0x2e, 0x31, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72, // - 1 - - - - u r
133 0x6c, 0x00, 0x00, 0x00, 0x06, 0x70, 0x75, 0x62, // l - - - - p u b
134 0x6c, 0x69, 0x63, 0x00, 0x00, 0x00, 0x0a, 0x73, // l i c - - - - s
135 0x65, 0x74, 0x2d, 0x63, 0x6f, 0x6f, 0x6b, 0x69, // e t - c o o k i
136 0x65, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x65, 0x65, // e - - - - k e e
137 0x70, 0x2d, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x00, // p - a l i v e -
138 0x00, 0x00, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, // - - - o r i g i
139 0x6e, 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x32, // n 1 0 0 1 0 1 2
140 0x30, 0x31, 0x32, 0x30, 0x32, 0x32, 0x30, 0x35, // 0 1 2 0 2 2 0 5
141 0x32, 0x30, 0x36, 0x33, 0x30, 0x30, 0x33, 0x30, // 2 0 6 3 0 0 3 0
142 0x32, 0x33, 0x30, 0x33, 0x33, 0x30, 0x34, 0x33, // 2 3 0 3 3 0 4 3
143 0x30, 0x35, 0x33, 0x30, 0x36, 0x33, 0x30, 0x37, // 0 5 3 0 6 3 0 7
144 0x34, 0x30, 0x32, 0x34, 0x30, 0x35, 0x34, 0x30, // 4 0 2 4 0 5 4 0
145 0x36, 0x34, 0x30, 0x37, 0x34, 0x30, 0x38, 0x34, // 6 4 0 7 4 0 8 4
146 0x30, 0x39, 0x34, 0x31, 0x30, 0x34, 0x31, 0x31, // 0 9 4 1 0 4 1 1
147 0x34, 0x31, 0x32, 0x34, 0x31, 0x33, 0x34, 0x31, // 4 1 2 4 1 3 4 1
148 0x34, 0x34, 0x31, 0x35, 0x34, 0x31, 0x36, 0x34, // 4 4 1 5 4 1 6 4
149 0x31, 0x37, 0x35, 0x30, 0x32, 0x35, 0x30, 0x34, // 1 7 5 0 2 5 0 4
150 0x35, 0x30, 0x35, 0x32, 0x30, 0x33, 0x20, 0x4e, // 5 0 5 2 0 3 - N
151 0x6f, 0x6e, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x6f, // o n - A u t h o
152 0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, // r i t a t i v e
153 0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, // - I n f o r m a
154 0x74, 0x69, 0x6f, 0x6e, 0x32, 0x30, 0x34, 0x20, // t i o n 2 0 4 -
155 0x4e, 0x6f, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65, // N o - C o n t e
156 0x6e, 0x74, 0x33, 0x30, 0x31, 0x20, 0x4d, 0x6f, // n t 3 0 1 - M o
157 0x76, 0x65, 0x64, 0x20, 0x50, 0x65, 0x72, 0x6d, // v e d - P e r m
158 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x34, // a n e n t l y 4
159 0x30, 0x30, 0x20, 0x42, 0x61, 0x64, 0x20, 0x52, // 0 0 - B a d - R
160 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x34, 0x30, // e q u e s t 4 0
161 0x31, 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68, // 1 - U n a u t h
162 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x34, 0x30, // o r i z e d 4 0
163 0x33, 0x20, 0x46, 0x6f, 0x72, 0x62, 0x69, 0x64, // 3 - F o r b i d
164 0x64, 0x65, 0x6e, 0x34, 0x30, 0x34, 0x20, 0x4e, // d e n 4 0 4 - N
165 0x6f, 0x74, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, // o t - F o u n d
166 0x35, 0x30, 0x30, 0x20, 0x49, 0x6e, 0x74, 0x65, // 5 0 0 - I n t e
167 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72, // r n a l - S e r
168 0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6f, // v e r - E r r o
169 0x72, 0x35, 0x30, 0x31, 0x20, 0x4e, 0x6f, 0x74, // r 5 0 1 - N o t
170 0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, // - I m p l e m e
171 0x6e, 0x74, 0x65, 0x64, 0x35, 0x30, 0x33, 0x20, // n t e d 5 0 3 -
172 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, // S e r v i c e -
173 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, // U n a v a i l a
174 0x62, 0x6c, 0x65, 0x4a, 0x61, 0x6e, 0x20, 0x46, // b l e J a n - F
175 0x65, 0x62, 0x20, 0x4d, 0x61, 0x72, 0x20, 0x41, // e b - M a r - A
176 0x70, 0x72, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x4a, // p r - M a y - J
177 0x75, 0x6e, 0x20, 0x4a, 0x75, 0x6c, 0x20, 0x41, // u n - J u l - A
178 0x75, 0x67, 0x20, 0x53, 0x65, 0x70, 0x74, 0x20, // u g - S e p t -
179 0x4f, 0x63, 0x74, 0x20, 0x4e, 0x6f, 0x76, 0x20, // O c t - N o v -
180 0x44, 0x65, 0x63, 0x20, 0x30, 0x30, 0x3a, 0x30, // D e c - 0 0 - 0
181 0x30, 0x3a, 0x30, 0x30, 0x20, 0x4d, 0x6f, 0x6e, // 0 - 0 0 - M o n
182 0x2c, 0x20, 0x54, 0x75, 0x65, 0x2c, 0x20, 0x57, // - - T u e - - W
183 0x65, 0x64, 0x2c, 0x20, 0x54, 0x68, 0x75, 0x2c, // e d - - T h u -
184 0x20, 0x46, 0x72, 0x69, 0x2c, 0x20, 0x53, 0x61, // - F r i - - S a
185 0x74, 0x2c, 0x20, 0x53, 0x75, 0x6e, 0x2c, 0x20, // t - - S u n - -
186 0x47, 0x4d, 0x54, 0x63, 0x68, 0x75, 0x6e, 0x6b, // G M T c h u n k
187 0x65, 0x64, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, // e d - t e x t -
188 0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x69, 0x6d, 0x61, // h t m l - i m a
189 0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67, 0x2c, 0x69, // g e - p n g - i
190 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x6a, 0x70, 0x67, // m a g e - j p g
191 0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, // - i m a g e - g
192 0x69, 0x66, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, // i f - a p p l i
193 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, // c a t i o n - x
194 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, // m l - a p p l i
195 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, // c a t i o n - x
196 0x68, 0x74, 0x6d, 0x6c, 0x2b, 0x78, 0x6d, 0x6c, // h t m l - x m l
197 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, // - t e x t - p l
198 0x61, 0x69, 0x6e, 0x2c, 0x74, 0x65, 0x78, 0x74, // a i n - t e x t
199 0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, // - j a v a s c r
200 0x69, 0x70, 0x74, 0x2c, 0x70, 0x75, 0x62, 0x6c, // i p t - p u b l
201 0x69, 0x63, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, // i c p r i v a t
202 0x65, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65, // e m a x - a g e
203 0x3d, 0x67, 0x7a, 0x69, 0x70, 0x2c, 0x64, 0x65, // - g z i p - d e
204 0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64, // f l a t e - s d
205 0x63, 0x68, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, // c h c h a r s e
206 0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x63, // t - u t f - 8 c
207 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x69, // h a r s e t - i
208 0x73, 0x6f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d, // s o - 8 8 5 9 -
209 0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x2c, 0x2a, // 1 - u t f - - -
210 0x2c, 0x65, 0x6e, 0x71, 0x3d, 0x30, 0x2e // - e n q - 0 -
211};
212
213
214int
215SPDYF_zlib_deflate_init(z_stream *strm)
216{
217 int ret;
218
219 strm->zalloc = Z_NULL;
220 strm->zfree = Z_NULL;
221 strm->opaque = Z_NULL;
222 //the second argument is "level of compression"
223 //use 0 for no compression; 9 for best compression
224 ret = deflateInit(strm, Z_DEFAULT_COMPRESSION);
225 if(ret != Z_OK)
226 {
227 SPDYF_DEBUG("deflate init");
228 return SPDY_NO;
229 }
230 ret = deflateSetDictionary(strm,
231 spdyf_zlib_dictionary,
232 sizeof(spdyf_zlib_dictionary));
233 if(ret != Z_OK)
234 {
235 SPDYF_DEBUG("deflate set dict");
236 deflateEnd(strm);
237 return SPDY_NO;
238 }
239 return SPDY_YES;
240}
241
242
243void
244SPDYF_zlib_deflate_end(z_stream *strm)
245{
246 deflateEnd(strm);
247}
248
249int
250SPDYF_zlib_deflate(z_stream *strm,
251 const void *src,
252 size_t src_size,
253 size_t *data_used,
254 void **dest,
255 size_t *dest_size)
256{
257 int ret;
258 int flush;
259 uint have;
260 Bytef out[SPDYF_ZLIB_CHUNK];
261
262 *dest = NULL;
263 *dest_size = 0;
264
265 do
266 {
267 /* check for big data bigger than the buffer used */
268 if(src_size > SPDYF_ZLIB_CHUNK)
269 {
270 strm->avail_in = SPDYF_ZLIB_CHUNK;
271 src_size -= SPDYF_ZLIB_CHUNK;
272 /* flush is used for the loop to detect if we still
273 * need to supply additional
274 * data to the stream via avail_in and next_in. */
275 flush = Z_NO_FLUSH;
276 }
277 else
278 {
279 strm->avail_in = src_size;
280 flush = Z_SYNC_FLUSH;
281 }
282 *data_used += strm->avail_in;
283
284 strm->next_in = (Bytef *)src;
285
286 /* Loop while output data is available */
287 do
288 {
289 strm->avail_out = SPDYF_ZLIB_CHUNK;
290 strm->next_out = out;
291
292 /* No need to check return value of deflate.
293 * (See zlib documentation at http://www.zlib.net/zlib_how.html */
294 ret = deflate(strm, flush);
295 have = SPDYF_ZLIB_CHUNK - strm->avail_out;
296
297 /* (Re)allocate memory for dest and keep track of it's size. */
298 *dest_size += have;
299 *dest = realloc(*dest, *dest_size);
300 if(!*dest)
301 {
302 SPDYF_DEBUG("realloc data for result");
303 deflateEnd(strm);
304 return SPDY_NO;
305 }
306 memcpy((*dest) + ((*dest_size) - have), out, have);
307 }
308 while(strm->avail_out == 0);
309 /* At this point, all of the input data should already
310 * have been used. */
311 SPDYF_ASSERT(strm->avail_in == 0,"compressing bug");
312 }
313 while(flush != Z_SYNC_FLUSH);
314
315 return Z_OK == ret ? SPDY_YES : SPDY_NO;
316}
317
318
319int
320SPDYF_zlib_inflate_init(z_stream *strm)
321{
322 int ret;
323
324 strm->zalloc = Z_NULL;
325 strm->zfree = Z_NULL;
326 strm->opaque = Z_NULL;
327 strm->avail_in = 0;
328 strm->next_in = Z_NULL;
329 //change 15 to lower value for performance and benchmark
330 //"The windowBits parameter is the base two logarithm of the
331 // maximum window size (the size of the history buffer)."
332 ret = inflateInit2(strm, 15);
333 if(ret != Z_OK)
334 {
335 SPDYF_DEBUG("Cannot inflateInit2 the stream");
336 return SPDY_NO;
337 }
338 return SPDY_YES;
339}
340
341
342void
343SPDYF_zlib_inflate_end(z_stream *strm)
344{
345 inflateEnd(strm);
346}
347
348
349int
350SPDYF_zlib_inflate(z_stream *strm,
351 const void *src,
352 size_t src_size,
353 void **dest,
354 size_t *dest_size)
355{
356 int ret = Z_OK;
357 uint32_t have;
358 Bytef out[SPDYF_ZLIB_CHUNK];
359
360 *dest = NULL;
361 *dest_size = 0;
362
363 /* decompress until deflate stream ends or end of file */
364 do
365 {
366 if(src_size > SPDYF_ZLIB_CHUNK)
367 {
368 strm->avail_in = SPDYF_ZLIB_CHUNK;
369 src_size -= SPDYF_ZLIB_CHUNK;
370 }
371 else
372 {
373 strm->avail_in = src_size;
374 src_size = 0;
375 }
376
377 if(strm->avail_in == 0){
378 //the loop breaks always here as the stream never ends
379 break;
380 }
381
382 strm->next_in = (Bytef *) src;
383 /* run inflate() on input until output buffer not full */
384 do {
385 strm->avail_out = SPDYF_ZLIB_CHUNK;
386 strm->next_out = out;
387 ret = inflate(strm, Z_SYNC_FLUSH);
388
389 switch (ret)
390 {
391 case Z_STREAM_ERROR:
392 SPDYF_DEBUG("Error on inflate");
393 //no inflateEnd here, same in zlib example
394 return SPDY_NO;
395
396 case Z_NEED_DICT:
397 ret = inflateSetDictionary(strm,
398 spdyf_zlib_dictionary,
399 sizeof(spdyf_zlib_dictionary));
400 if(ret != Z_OK)
401 {
402 SPDYF_DEBUG("Error on inflateSetDictionary");
403 inflateEnd(strm);
404 return SPDY_NO;
405 }
406 ret = inflate(strm, Z_SYNC_FLUSH);
407 if(Z_STREAM_ERROR == ret)
408 {
409 SPDYF_DEBUG("Error on inflate");
410 return SPDY_NO;
411 }
412 break;
413
414 case Z_DATA_ERROR:
415 SPDYF_DEBUG("Z_DATA_ERROR");
416 inflateEnd(strm);
417 return SPDY_NO;
418
419 case Z_MEM_ERROR:
420 SPDYF_DEBUG("Z_MEM_ERROR");
421 inflateEnd(strm);
422 return SPDY_NO;
423 }
424 have = SPDYF_ZLIB_CHUNK - strm->avail_out;
425 *dest_size += have;
426 /* (re)alloc memory for the output buffer */
427 *dest = realloc(*dest, *dest_size);
428 if(!*dest)
429 {
430 SPDYF_DEBUG("Cannot realloc memory");
431 inflateEnd(strm);
432 return SPDY_NO;
433 }
434 memcpy((*dest) + ((*dest_size) - have), out, have);
435 }
436 while (0 == strm->avail_out);
437 }
438 while (Z_STREAM_END != ret);
439
440 return Z_OK == ret || Z_STREAM_END == ret ? SPDY_YES : SPDY_NO;
441}
diff --git a/src/microspdy/compression.h b/src/microspdy/compression.h
new file mode 100644
index 00000000..ac37f115
--- /dev/null
+++ b/src/microspdy/compression.h
@@ -0,0 +1,117 @@
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 compression.h
21 * @brief zlib handling functions
22 * @author Andrey Uzunov
23 */
24
25#ifndef COMPRESSION_H
26#define COMPRESSION_H
27
28#include "platform.h"
29
30/* size of buffers used by zlib on (de)compressing */
31#define SPDYF_ZLIB_CHUNK 16384
32
33
34/**
35 * Initializes the zlib stream for compression. Must be called once
36 * for a session on initialization.
37 *
38 * @param strm Zlib stream on which we work
39 * @return SPDY_NO if zlib failed. SPDY_YES otherwise
40 */
41int
42SPDYF_zlib_deflate_init(z_stream *strm);
43
44
45/**
46 * Deinitializes the zlib stream for compression. Should be called once
47 * for a session on cleaning up.
48 *
49 * @param strm Zlib stream on which we work
50 */
51void
52SPDYF_zlib_deflate_end(z_stream *strm);
53
54
55/**
56 * Compressing stream with zlib.
57 *
58 * @param strm Zlib stream on which we work
59 * @param src stream of the data to be compressed
60 * @param src_size size of the data
61 * @param data_used the number of bytes from src_stream that were used
62 * TODO do we need
63 * @param dest the resulting compressed stream. Should be NULL. Must be
64 * freed later manually.
65 * @param dest_size size of the data after compression
66 * @return SPDY_NO if malloc or zlib failed. SPDY_YES otherwise
67 */
68int
69SPDYF_zlib_deflate(z_stream *strm,
70 const void *src,
71 size_t src_size,
72 size_t *data_used,
73 void **dest,
74 size_t *dest_size);
75
76
77/**
78 * Initializes the zlib stream for decompression. Must be called once
79 * for a session.
80 *
81 * @param strm Zlib stream on which we work
82 * @return SPDY_NO if zlib failed. SPDY_YES otherwise
83 */
84int
85SPDYF_zlib_inflate_init(z_stream *strm);
86
87
88/**
89 * Deinitializes the zlib stream for decompression. Should be called once
90 * for a session on cleaning up.
91 *
92 * @param strm Zlib stream on which we work
93 */
94void
95SPDYF_zlib_inflate_end(z_stream *strm);
96
97
98/**
99 * Decompressing stream with zlib.
100 *
101 * @param strm Zlib stream on which we work
102 * @param src stream of the data to be decompressed
103 * @param src_size size of the data
104 * @param dest the resulting decompressed stream. Should be NULL. Must
105 * be freed manually.
106 * @param dest_size size of the data after decompression
107 * @return SPDY_NO if malloc or zlib failed. SPDY_YES otherwise. If the
108 * function fails, the SPDY session must be closed
109 */
110int
111SPDYF_zlib_inflate(z_stream *strm,
112 const void *src,
113 size_t src_size,
114 void **dest,
115 size_t *dest_size);
116
117#endif
diff --git a/src/microspdy/daemon.c b/src/microspdy/daemon.c
new file mode 100644
index 00000000..47e49ea1
--- /dev/null
+++ b/src/microspdy/daemon.c
@@ -0,0 +1,515 @@
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 daemon.c
21 * @brief daemon functionality
22 * @author Andrey Uzunov
23 */
24
25#include "platform.h"
26#include "structures.h"
27#include "internal.h"
28#include "session.h"
29#include "tls.h"
30
31
32/**
33 * Default implementation of the panic function,
34 * prints an error message and aborts.
35 *
36 * @param cls unused
37 * @param file name of the file with the problem
38 * @param line line number with the problem
39 * @param reason error message with details
40 */
41static void
42spdyf_panic_std (void *cls,
43 const char *file,
44 unsigned int line,
45 const char *reason)
46{
47 (void)cls;
48 fprintf (stdout, "Fatal error in libmicrospdy %s:%u: %s\n",
49 file, line, reason);
50 //raise(SIGINT); //used for gdb
51 abort ();
52}
53
54
55/**
56 * Global handler for fatal errors.
57 */
58SPDY_PanicCallback spdyf_panic = &spdyf_panic_std;
59
60
61/**
62 * Global closure argument for "spdyf_panic".
63 */
64void *spdyf_panic_cls;
65
66
67/**
68 * Free resources associated with all closed connections.
69 * (destroy responses, free buffers, etc.).
70 *
71 * @param daemon daemon to clean up
72 */
73static void
74spdyf_cleanup_sessions (struct SPDY_Daemon *daemon)
75{
76 struct SPDY_Session *session;
77
78 while (NULL != (session = daemon->cleanup_head))
79 {
80 DLL_remove (daemon->cleanup_head,
81 daemon->cleanup_tail,
82 session);
83
84 SPDYF_session_destroy(session);
85 }
86}
87
88
89/**
90 * Closing of all connections handled by the daemon.
91 *
92 * @param daemon SPDY daemon
93 */
94static void
95spdyf_close_all_sessions (struct SPDY_Daemon *daemon)
96{
97 struct SPDY_Session *session;
98
99 while (NULL != (session = daemon->sessions_head))
100 {
101 //prepare GOAWAY frame
102 SPDYF_prepare_goaway(session, SPDY_GOAWAY_STATUS_OK, true);
103 //try to send the frame (it is best effort, so it will maybe sent)
104 SPDYF_session_write(session,true);
105 SPDYF_session_close(session);
106 }
107
108 spdyf_cleanup_sessions(daemon);
109}
110
111
112/**
113 * Parse a list of options given as varargs.
114 *
115 * @param daemon the daemon to initialize
116 * @param valist the options
117 * @return SPDY_YES on success, SPDY_NO on error
118 */
119static int
120spdyf_parse_options_va (struct SPDY_Daemon *daemon,
121 va_list valist)
122{
123 enum SPDY_DAEMON_OPTION opt;
124
125 while (SPDY_DAEMON_OPTION_END != (opt = (enum SPDY_DAEMON_OPTION) va_arg (valist, int)))
126 {
127 if(opt & daemon->options)
128 {
129 SPDYF_DEBUG("Daemon option %i used twice",opt);
130 return SPDY_NO;
131 }
132 daemon->options |= opt;
133
134 switch (opt)
135 {
136 case SPDY_DAEMON_OPTION_SESSION_TIMEOUT:
137 daemon->session_timeout = va_arg (valist, unsigned int);
138 break;
139 case SPDY_DAEMON_OPTION_SOCK_ADDR:
140 daemon->address = va_arg (valist, struct sockaddr *);
141 break;
142 case SPDY_DAEMON_OPTION_FLAGS:
143 daemon->flags = va_arg (valist, enum SPDY_DAEMON_FLAG);
144 break;
145 default:
146 SPDYF_DEBUG("Wrong option for the daemon %i",opt);
147 return SPDY_NO;
148 }
149 }
150 return SPDY_YES;
151}
152
153
154void
155SPDY_set_panic_func (SPDY_PanicCallback cb,
156 void *cls)
157{
158 spdyf_panic = cb;
159 spdyf_panic_cls = cls;
160}
161
162
163struct SPDY_Daemon *
164SPDYF_start_daemon_va (uint16_t port,
165 const char *certfile,
166 const char *keyfile,
167 SPDY_NewSessionCallback nscb,
168 SPDY_SessionClosedCallback sccb,
169 SPDY_NewRequestCallback nrcb,
170 SPDY_NewPOSTDataCallback npdcb,
171 SPDYF_NewStreamCallback fnscb,
172 void * cls,
173 void * fcls,
174 va_list valist)
175{
176 struct SPDY_Daemon *daemon = NULL;
177 int afamily;
178 int option_on = 1;
179 int ret;
180 struct sockaddr_in* servaddr4 = NULL;
181#if HAVE_INET6
182 struct sockaddr_in6* servaddr6 = NULL;
183#endif
184 socklen_t addrlen;
185
186 if (NULL == (daemon = malloc (sizeof (struct SPDY_Daemon))))
187 {
188 SPDYF_DEBUG("malloc");
189 return NULL;
190 }
191 memset (daemon, 0, sizeof (struct SPDY_Daemon));
192 daemon->socket_fd = -1;
193 daemon->port = port;
194 if (NULL == (daemon->certfile = strdup (certfile)))
195 {
196 SPDYF_DEBUG("str");
197 goto free_and_fail;
198 }
199 if (NULL == (daemon->keyfile = strdup (keyfile)))
200 {
201 SPDYF_DEBUG("str");
202 goto free_and_fail;
203 }
204 daemon->new_session_cb = nscb;
205 daemon->session_closed_cb = sccb;
206 daemon->new_request_cb = nrcb;
207 daemon->new_post_data_cb = npdcb;
208 daemon->cls = cls;
209 daemon->fcls = fcls;
210 daemon->fnew_stream_cb = fnscb;
211
212 if(SPDY_YES != spdyf_parse_options_va (daemon, valist))
213 {
214 SPDYF_DEBUG("parse");
215 goto free_and_fail;
216 }
217
218 if(!port && NULL == daemon->address)
219 {
220 SPDYF_DEBUG("Port is 0");
221 goto free_and_fail;
222 }
223
224#if HAVE_INET6
225 //handling IPv6
226 if((daemon->flags & SPDY_DAEMON_FLAG_ONLY_IPV6)
227 && NULL != daemon->address && AF_INET6 != daemon->address->sa_family)
228 {
229 SPDYF_DEBUG("SPDY_DAEMON_FLAG_ONLY_IPV6 set but IPv4 address provided");
230 goto free_and_fail;
231 }
232
233 if(NULL == daemon->address)
234 {
235 addrlen = sizeof (struct sockaddr_in6);
236
237 if (NULL == (servaddr6 = malloc (addrlen)))
238 {
239 SPDYF_DEBUG("malloc");
240 goto free_and_fail;
241 }
242 memset (servaddr6, 0, addrlen);
243 servaddr6->sin6_family = AF_INET6;
244 servaddr6->sin6_addr = in6addr_any;
245 servaddr6->sin6_port = htons (port);
246 daemon->address = (struct sockaddr *) servaddr6;
247 }
248
249 afamily = AF_INET6 == daemon->address->sa_family ? PF_INET6 : PF_INET;
250#else
251 //handling IPv4
252 if(daemon->flags & SPDY_DAEMON_FLAG_ONLY_IPV6)
253 {
254 SPDYF_DEBUG("SPDY_DAEMON_FLAG_ONLY_IPV6 set but no support");
255 goto free_and_fail;
256 }
257
258 if(NULL == daemon->address)
259 {
260 addrlen = sizeof (struct sockaddr_in);
261
262 if (NULL == (servaddr4 = malloc (addrlen)))
263 {
264 SPDYF_DEBUG("malloc");
265 goto free_and_fail;
266 }
267 memset (servaddr4, 0, addrlen);
268 servaddr4->sin_family = AF_INET;
269 servaddr4->sin_addr = INADDR_ANY;
270 servaddr4->sin_port = htons (port);
271 daemon->address = (struct sockaddr *) servaddr4;
272 }
273
274 afamily = PF_INET;
275#endif
276
277 daemon->socket_fd = socket (afamily, SOCK_STREAM, 0);
278 if (-1 == daemon->socket_fd)
279 {
280 SPDYF_DEBUG("sock");
281 goto free_and_fail;
282 }
283
284 //setting option for the socket to reuse address
285 ret = setsockopt(daemon->socket_fd, SOL_SOCKET, SO_REUSEADDR, &option_on, sizeof(option_on));
286 if(ret)
287 {
288 SPDYF_DEBUG("WARNING: SO_REUSEADDR was not set for the server");
289 }
290
291#if HAVE_INET6
292 if(daemon->flags & SPDY_DAEMON_FLAG_ONLY_IPV6)
293 {
294 ret = setsockopt(daemon->socket_fd, IPPROTO_IPV6, IPV6_V6ONLY, &option_on, sizeof(option_on));
295 if(ret)
296 {
297 SPDYF_DEBUG("setsockopt with IPPROTO_IPV6 failed");
298 goto free_and_fail;
299 }
300 }
301#endif
302
303 if (-1 == bind (daemon->socket_fd, daemon->address, addrlen))
304 {
305 SPDYF_DEBUG("bind %i",errno);
306 goto free_and_fail;
307 }
308
309 if (listen (daemon->socket_fd, 20) < 0)
310 {
311 SPDYF_DEBUG("listen %i",errno);
312 goto free_and_fail;
313 }
314
315 if(SPDY_YES != SPDYF_tls_init(daemon))
316 {
317 SPDYF_DEBUG("tls");
318 goto free_and_fail;
319 }
320
321 return daemon;
322
323 //for GOTO
324 free_and_fail:
325 if(daemon->socket_fd > 0)
326 close (daemon->socket_fd);
327
328 free(servaddr4);
329#if HAVE_INET6
330 free(servaddr6);
331#endif
332 if(NULL != daemon->certfile)
333 free(daemon->certfile);
334 if(NULL != daemon->keyfile)
335 free(daemon->keyfile);
336 free (daemon);
337
338 return NULL;
339}
340
341
342void
343SPDYF_stop_daemon (struct SPDY_Daemon *daemon)
344{
345 SPDYF_tls_deinit(daemon);
346
347 shutdown (daemon->socket_fd, SHUT_RDWR);
348 spdyf_close_all_sessions (daemon);
349 close (daemon->socket_fd);
350
351 if(!(SPDY_DAEMON_OPTION_SOCK_ADDR & daemon->options))
352 free(daemon->address);
353
354 free(daemon->certfile);
355 free(daemon->keyfile);
356
357 free(daemon);
358}
359
360
361int
362SPDYF_get_timeout (struct SPDY_Daemon *daemon,
363 unsigned long long *timeout)
364{
365 time_t earliest_deadline = 0;
366 time_t now;
367 struct SPDY_Session *pos;
368 bool have_timeout;
369
370 if(0 == daemon->session_timeout)
371 return SPDY_NO;
372
373 now = SPDYF_monotonic_time();
374 have_timeout = false;
375 for (pos = daemon->sessions_head; NULL != pos; pos = pos->next)
376 {
377 if ( (! have_timeout) ||
378 (earliest_deadline > pos->last_activity + daemon->session_timeout) )
379 earliest_deadline = pos->last_activity + daemon->session_timeout;
380
381 have_timeout = true;
382
383 if (SPDY_YES == SPDYF_tls_is_pending(pos))
384 {
385 earliest_deadline = 0;
386 break;
387 }
388 }
389
390 if (!have_timeout)
391 return SPDY_NO;
392 if (earliest_deadline < now)
393 *timeout = 0;
394 else
395 //*timeout = 1000 * (1 + earliest_deadline - now);
396 *timeout = earliest_deadline - now;
397
398 return SPDY_YES;
399}
400
401
402int
403SPDYF_get_fdset (struct SPDY_Daemon *daemon,
404 fd_set *read_fd_set,
405 fd_set *write_fd_set,
406 fd_set *except_fd_set,
407 bool all)
408{
409 (void)except_fd_set;
410 struct SPDY_Session *pos;
411 int fd;
412 int max_fd = -1;
413
414 fd = daemon->socket_fd;
415 if (-1 != fd)
416 {
417 FD_SET (fd, read_fd_set);
418 /* update max file descriptor */
419 max_fd = fd;
420 }
421
422 for (pos = daemon->sessions_head; NULL != pos; pos = pos->next)
423 {
424 fd = pos->socket_fd;
425 FD_SET(fd, read_fd_set);
426 if(all
427 || NULL != pos->response_queue_head //frames pending
428 || NULL != pos->write_buffer //part of last frame pending
429 || SPDY_SESSION_STATUS_CLOSING == pos->status //the session is about to be closed
430 || daemon->session_timeout //timeout passed for the session
431 && (pos->last_activity + daemon->session_timeout < SPDYF_monotonic_time())
432 || SPDY_YES == SPDYF_tls_is_pending(pos) //data in TLS' read buffer pending
433 || ((pos->read_buffer_offset - pos->read_buffer_beginning) > 0) // data in lib's read buffer pending
434 )
435 FD_SET(fd, write_fd_set);
436 if(fd > max_fd)
437 max_fd = fd;
438 }
439
440 return max_fd;
441}
442
443
444void
445SPDYF_run (struct SPDY_Daemon *daemon)
446{
447 struct SPDY_Session *pos;
448 struct SPDY_Session *next;
449 int num_ready;
450 fd_set rs;
451 fd_set ws;
452 fd_set es;
453 int max;
454 struct timeval timeout;
455 int ds;
456
457 timeout.tv_sec = 0;
458 timeout.tv_usec = 0;
459 FD_ZERO (&rs);
460 FD_ZERO (&ws);
461 FD_ZERO (&es);
462 //here we need really all descriptors to see later which are ready
463 max = SPDYF_get_fdset(daemon,&rs,&ws,&es, true);
464
465 num_ready = select (max + 1, &rs, &ws, &es, &timeout);
466
467 if(num_ready < 1)
468 return;
469
470 if ( (-1 != (ds = daemon->socket_fd)) &&
471 (FD_ISSET (ds, &rs)) ){
472 SPDYF_session_accept(daemon);
473 }
474
475 next = daemon->sessions_head;
476 while (NULL != (pos = next))
477 {
478 next = pos->next;
479 ds = pos->socket_fd;
480 if (ds != -1)
481 {
482 //fill the read buffer
483 if (FD_ISSET (ds, &rs) || SPDYF_tls_is_pending(pos)){
484 SPDYF_session_read(pos);
485 }
486
487 //do something with the data in read buffer
488 if(SPDY_NO == SPDYF_session_idle(pos))
489 {
490 //the session was closed, cannot write anymore
491 //continue;
492 }
493
494 //write whatever has been put to the response queue
495 //during read or idle operation, something might be put
496 //on the response queue, thus call write operation
497 if (FD_ISSET (ds, &ws)){
498 if(SPDY_NO == SPDYF_session_write(pos, false))
499 {
500 //SPDYF_session_close(pos);
501 //continue;
502 }
503 }
504
505 /* the response queue has been flushed for half closed
506 * connections, so let close them */
507 /*if(pos->read_closed)
508 {
509 SPDYF_session_close(pos);
510 }*/
511 }
512 }
513
514 spdyf_cleanup_sessions(daemon);
515}
diff --git a/src/microspdy/daemon.h b/src/microspdy/daemon.h
new file mode 100644
index 00000000..d3f7f2ff
--- /dev/null
+++ b/src/microspdy/daemon.h
@@ -0,0 +1,121 @@
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 daemon.h
21 * @brief daemon functionality
22 * @author Andrey Uzunov
23 */
24
25#ifndef DAEMON_H
26#define DAEMON_H
27
28#include "platform.h"
29
30
31/**
32 * Start a SPDDY webserver on the given port.
33 *
34 * @param port port to bind to
35 * @param certfile path to the certificate that will be used by server
36 * @param keyfile path to the keyfile for the certificate
37 * @param nscb callback called when a new SPDY session is
38 * established by a client
39 * @param sccb callback called when a client closes the session
40 * @param nrcb callback called when a client sends request
41 * @param npdcb callback called when HTTP POST params are received
42 * after request
43 * @param fnscb callback called when new stream is opened by a client
44 * @param cls extra argument to all of the callbacks without those
45 * specific only for the framing layer
46 * @param fcls extra argument to all of the callbacks, specific only for
47 * the framing layer (those vars starting with 'f').
48 * @param valist va_list of options (type-value pairs,
49 * terminated with SPDY_DAEMON_OPTION_END).
50 * @return NULL on error, handle to daemon on success
51 */
52struct SPDY_Daemon *
53SPDYF_start_daemon_va (uint16_t port,
54 const char *certfile,
55 const char *keyfile,
56 SPDY_NewSessionCallback nscb,
57 SPDY_SessionClosedCallback sccb,
58 SPDY_NewRequestCallback nrcb,
59 SPDY_NewPOSTDataCallback npdcb,
60 SPDYF_NewStreamCallback fnscb,
61 void * cls,
62 void * fcls,
63 va_list valist);
64
65
66/**
67 * Run webserver operations (without blocking unless
68 * in client callbacks). This method must be called in the client event
69 * loop.
70 *
71 * @param daemon daemon to run
72 */
73void
74SPDYF_run (struct SPDY_Daemon *daemon);
75
76
77/**
78 * Obtain timeout value for select for this daemon. The returned value
79 * is how long select
80 * should at most block, not the timeout value set for connections.
81 *
82 * @param daemon daemon to query for timeout
83 * @param timeout set to the timeout (in seconds)
84 * @return SPDY_YES on success, SPDY_NO if no connections exist that
85 * would necessiate the use of a timeout right now
86 */
87int
88SPDYF_get_timeout (struct SPDY_Daemon *daemon,
89 unsigned long long *timeout);
90
91
92/**
93 * Obtain the select sets for this daemon. The idea of SPDYF_get_fdset
94 * is to return such descriptors that the select in the application can
95 * return and SPDY_run can be called only when this is really needed.
96 * That means not all sockets will be added to write_fd_set.
97 *
98 * @param daemon daemon to get sets from
99 * @param read_fd_set read set
100 * @param write_fd_set write set
101 * @param except_fd_set except set
102 * @param all add all session's descriptors to write_fd_set or not
103 * @return largest FD added
104 */
105int
106SPDYF_get_fdset (struct SPDY_Daemon *daemon,
107 fd_set *read_fd_set,
108 fd_set *write_fd_set,
109 fd_set *except_fd_set,
110 bool all);
111
112
113/**
114 * Shutdown the daemon.
115 *
116 * @param daemon daemon to stop
117 */
118void
119SPDYF_stop_daemon (struct SPDY_Daemon *daemon);
120
121#endif
diff --git a/src/microspdy/internal.c b/src/microspdy/internal.c
new file mode 100644
index 00000000..458bcb2f
--- /dev/null
+++ b/src/microspdy/internal.c
@@ -0,0 +1,38 @@
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 internal.c
21 * @brief internal functions and macros for the framing layer
22 * @author Andrey Uzunov
23 */
24
25#include "platform.h"
26#include "structures.h"
27
28
29time_t
30SPDYF_monotonic_time(void)
31{
32#ifdef HAVE_CLOCK_GETTIME
33 struct timespec ts;
34 if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0)
35 return ts.tv_sec;
36#endif
37 return time(NULL);
38}
diff --git a/src/microspdy/internal.h b/src/microspdy/internal.h
new file mode 100644
index 00000000..becde9d5
--- /dev/null
+++ b/src/microspdy/internal.h
@@ -0,0 +1,189 @@
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 structures.h
21 * @brief internal functions and macros for the framing layer
22 * @author Andrey Uzunov
23 */
24
25#ifndef INTERNAL_H_H
26#define INTERNAL_H_H
27
28#include "platform.h"
29#include "microspdy.h"
30#include "tls.h"
31
32/* size of read buffers for each connection
33 * must be at least the size of SPDY_MAX_SUPPORTED_FRAME_SIZE */
34#define SPDYF_BUFFER_SIZE 8192
35
36/* number of frames written to the socket at once. After X frames
37 * everything should be run again. In this way the application can
38 * response to more important requests while a big file is still
39 * being transmitted to the client */
40#define SPDYF_NUM_SENT_FRAMES_AT_ONCE 10
41
42
43/**
44 * Handler for fatal errors.
45 */
46extern SPDY_PanicCallback spdyf_panic;
47
48
49/**
50 * Closure argument for "mhd_panic".
51 */
52extern void *spdyf_panic_cls;
53
54
55/**
56 * Trigger 'panic' action based on fatal errors.
57 *
58 * @param msg error message (const char *)
59 */
60#define SPDYF_PANIC(msg) \
61 spdyf_panic (spdyf_panic_cls, __FILE__, __LINE__, msg)
62
63
64/**
65 * Asserts the validity of an expression.
66 *
67 * @param expression (bool)
68 * @param msg message to print on error (const char *)
69 */
70#define SPDYF_ASSERT(expr,msg) \
71 if(!(expr)){\
72 SPDYF_PANIC(msg);\
73 abort();\
74 }
75
76
77/**
78 * Convert 24 bit integer from host byte order to network byte order.
79 *
80 * @param n input value (int32_t)
81 * @return converted value (uint32_t)
82 */
83#if HAVE_BIG_ENDIAN
84#define HTON24(n) n
85#else
86#define HTON24(n) (((((uint32_t)(n) & 0xFF)) << 16)\
87 | (((uint32_t)(n) & 0xFF00))\
88 | ((((uint32_t)(n) & 0xFF0000)) >> 16))
89#endif
90
91
92/**
93 * Convert 24 bit integer from network byte order to host byte order.
94 *
95 * @param n input value (int32_t)
96 * @return converted value (uint32_t)
97 */
98#if HAVE_BIG_ENDIAN
99#define NTOH24(n) n
100#else
101#define NTOH24(n) (((((uint32_t)(n) & 0xFF)) << 16)\
102 | (((uint32_t)(n) & 0xFF00))\
103 | ((((uint32_t)(n) & 0xFF0000)) >> 16))
104#endif
105
106
107/**
108 * Convert 31 bit integer from network byte order to host byte order.
109 *
110 * @param n input value (int32_t)
111 * @return converted value (uint32_t)
112 */
113#if HAVE_BIG_ENDIAN
114#define NTOH31(n) n
115#else
116#define NTOH31(n) (((((uint32_t)(n) & 0x7F)) << 24) | \
117 ((((uint32_t)(n) & 0xFF00)) << 8) | \
118 ((((uint32_t)(n) & 0xFF0000)) >> 8) | \
119 ((((uint32_t)(n) & 0xFF000000)) >> 24))
120#endif
121
122
123/**
124 * Convert 31 bit integer from host byte order to network byte order.
125 *
126 * @param n input value (int32_t)
127 * @return converted value (uint32_t)
128 */
129#if HAVE_BIG_ENDIAN
130#define HTON31(n) n
131#else
132#define HTON31(n) (((((uint32_t)(n) & 0xFF)) << 24) | \
133 ((((uint32_t)(n) & 0xFF00)) << 8) | \
134 ((((uint32_t)(n) & 0xFF0000)) >> 8) | \
135 ((((uint32_t)(n) & 0x7F000000)) >> 24))
136#endif
137
138
139/**
140 * Print formatted debug value.
141 *
142 * @param fmt format (const char *)
143 * @param ... args for format
144 */
145#define SPDYF_DEBUG(fmt, ...) do { \
146 fprintf (stdout, "%s\n%u: ",__FILE__, __LINE__);\
147 fprintf(stdout,fmt,##__VA_ARGS__);\
148 fprintf(stdout,"\n");\
149 fflush(stdout); } while (0)
150
151
152/**
153 * Print stream for debuging.
154 *
155 * @param strm (void *)
156 * @param size (int)
157 */
158#define SPDYF_PRINT_STREAM(strm, size) do { \
159 int ___i;\
160 for(___i=0;___i<size;___i++){\
161 fprintf(stdout,"%x ",*((uint8_t *) strm + ___i));\
162 fflush(stdout);\
163 }\
164 fprintf(stdout,"\n");\
165 } while (0)
166
167
168/**
169 * Print message and raise SIGINT for debug purposes.
170 *
171 * @param msg message (const char *)
172 */
173#define SPDYF_SIGINT(msg) do { \
174 fprintf(stdout,"%i : %s\n", __LINE__,__FILE__);\
175 fprintf(stdout,msg);\
176 fprintf(stdout,"\n");\
177 fflush(stdout);\
178 raise(SIGINT); } while (0)
179
180
181/**
182 * Returns monotonic time, to be used for session timeouts.
183 *
184 * @return time in seconds
185 */
186time_t
187SPDYF_monotonic_time(void);
188
189#endif
diff --git a/src/microspdy/session.c b/src/microspdy/session.c
new file mode 100644
index 00000000..002aeb15
--- /dev/null
+++ b/src/microspdy/session.c
@@ -0,0 +1,1554 @@
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 session.c
21 * @brief TCP connection/SPDY session handling. So far most of the
22 * functions for handling SPDY framing layer are here.
23 * @author Andrey Uzunov
24 */
25
26#include "platform.h"
27#include "structures.h"
28#include "internal.h"
29#include "session.h"
30#include "compression.h"
31#include "tls.h"
32#include "stream.h"
33
34
35/**
36 * Handler for reading the full SYN_STREAM frame after we know that
37 * the frame is such.
38 * The function waits for the full frame and then changes status
39 * of the session. New stream is created.
40 *
41 * @param session SPDY_Session whose read buffer is used.
42 */
43static void
44spdyf_handler_read_syn_stream (struct SPDY_Session *session)
45{
46 size_t name_value_strm_size = 0;
47 uint compressed_data_size;
48 int ret;
49 void *name_value_strm = NULL;
50 struct SPDYF_Control_Frame *frame;
51 struct SPDY_NameValue *headers;
52
53 SPDYF_ASSERT(SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER == session->status
54 || SPDY_SESSION_STATUS_WAIT_FOR_BODY == session->status,
55 "the function is called wrong");
56
57 frame = (struct SPDYF_Control_Frame *)session->frame_handler_cls;
58
59 //handle subheaders
60 if(SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER == session->status)
61 {
62 if(0 == frame->length)
63 {
64 //protocol error: incomplete frame
65 //we just ignore it since there is no stream id for which to
66 //send RST_STREAM
67 //TODO maybe GOAWAY and closing session is appropriate
68 SPDYF_DEBUG("zero long SYN_STREAM received");
69 session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER;
70 free(frame);
71 return;
72 }
73
74 if(SPDY_YES != SPDYF_stream_new(session))
75 {
76 /* waiting for some more fields to create new stream
77 or something went wrong, SPDYF_stream_new has handled the
78 situation */
79 return;
80 }
81
82 session->current_stream_id = session->streams_head->stream_id;
83 if(frame->length > SPDY_MAX_SUPPORTED_FRAME_SIZE)
84 {
85 //TODO no need to create stream if this happens
86 session->status = SPDY_SESSION_STATUS_IGNORE_BYTES;
87 return;
88 }
89 else
90 session->status = SPDY_SESSION_STATUS_WAIT_FOR_BODY;
91 }
92
93 //handle body
94
95 //start reading the compressed name/value pairs (http headers)
96 compressed_data_size = frame->length //everything after length field
97 - 10;//4B stream id, 4B assoc strem id, 2B priority, unused and slot
98
99 if(session->read_buffer_offset - session->read_buffer_beginning < compressed_data_size)
100 {
101 // the full frame is not yet here, try later
102 return;
103 }
104
105 if(compressed_data_size > 0
106 && SPDY_YES != SPDYF_zlib_inflate(&session->zlib_recv_stream,
107 session->read_buffer + session->read_buffer_beginning,
108 compressed_data_size,
109 &name_value_strm,
110 &name_value_strm_size))
111 {
112 /* something went wrong on inflating,
113 * the state of the stream for decompression is unknown
114 * and we may not be able to read anything more received on
115 * this session,
116 * so it is better to close the session */
117 free(name_value_strm);
118 free(frame);
119
120 /* mark the session for closing and close it, when
121 * everything on the output queue is already written */
122 session->status = SPDY_SESSION_STATUS_FLUSHING;
123
124 SPDYF_prepare_goaway(session, SPDY_GOAWAY_STATUS_INTERNAL_ERROR, false);
125
126 return;
127 }
128
129 if(0 == name_value_strm_size || 0 == compressed_data_size)
130 {
131 //Protocol error: send RST_STREAM
132 if(SPDY_YES != SPDYF_prepare_rst_stream(session, session->current_stream_id,
133 SPDY_RST_STREAM_STATUS_PROTOCOL_ERROR))
134 {
135 //no memory, try later to send RST
136 return;
137 }
138 }
139
140 ret = SPDYF_name_value_from_stream(name_value_strm, name_value_strm_size, &headers);
141 if(SPDY_NO == ret)
142 {
143 //memory error, try later
144 free(name_value_strm);
145 return;
146 }
147
148 session->streams_head->headers = headers;
149 //inform the application layer for the new stream received
150 if(SPDY_YES != session->daemon->fnew_stream_cb(session->daemon->fcls, session->streams_head))
151 {
152 //memory error, try later
153 free(name_value_strm);
154 return;
155 }
156
157 session->read_buffer_beginning += compressed_data_size;
158 //change state to wait for new frame
159 session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER;
160 free(frame);
161 free(name_value_strm);
162}
163
164
165/**
166 * Handler for reading the GOAWAY frame after we know that
167 * the frame is such.
168 * The function waits for the full frame and then changes status
169 * of the session.
170 *
171 * @param session SPDY_Session whose read buffer is used.
172 */
173static void
174spdyf_handler_read_goaway (struct SPDY_Session *session)
175{
176 struct SPDYF_Control_Frame *frame;
177 uint32_t last_good_stream_id;
178 uint32_t status_int;
179 enum SPDY_GOAWAY_STATUS status;
180
181 SPDYF_ASSERT(SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER == session->status,
182 "the function is called wrong");
183
184 frame = (struct SPDYF_Control_Frame *)session->frame_handler_cls;
185
186 if(frame->length > SPDY_MAX_SUPPORTED_FRAME_SIZE)
187 {
188 //this is a protocol error/attack
189 session->status = SPDY_SESSION_STATUS_IGNORE_BYTES;
190 return;
191 }
192
193 if(0 != frame->flags || 8 != frame->length)
194 {
195 //this is a protocol error
196 SPDYF_DEBUG("wrong GOAWAY received");
197 //anyway, it will be handled
198 }
199
200 if((session->read_buffer_offset - session->read_buffer_beginning) < frame->length)
201 {
202 //not all fields are received
203 //try later
204 return;
205 }
206
207 //mark that the session is almost closed
208 session->is_goaway_received = true;
209
210 if(8 == frame->length)
211 {
212 memcpy(&last_good_stream_id, session->read_buffer + session->read_buffer_beginning, 4);
213 last_good_stream_id = NTOH31(last_good_stream_id);
214 session->read_buffer_beginning += 4;
215
216 memcpy(&status_int, session->read_buffer + session->read_buffer_beginning, 4);
217 status = ntohl(status_int);
218 session->read_buffer_beginning += 4;
219
220 //TODO do something with last_good
221
222 //SPDYF_DEBUG("Received GOAWAY; status=%i; lastgood=%i",status,last_good_stream_id);
223
224 //do something according to the status
225 //TODO
226 switch(status)
227 {
228 case SPDY_GOAWAY_STATUS_OK:
229 break;
230 case SPDY_GOAWAY_STATUS_PROTOCOL_ERROR:
231 break;
232 case SPDY_GOAWAY_STATUS_INTERNAL_ERROR:
233 break;
234 }
235 }
236
237 session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER;
238 free(frame);
239}
240
241
242/**
243 * Handler for reading RST_STREAM frames. After receiving the frame
244 * the stream moves into closed state and status
245 * of the session is changed. Frames, belonging to this stream, which
246 * are still at the output queue, will be ignored later.
247 *
248 * @param session SPDY_Session whose read buffer is used.
249 */
250static void
251spdyf_handler_read_rst_stream (struct SPDY_Session *session)
252{
253 struct SPDYF_Control_Frame *frame;
254 uint32_t stream_id;
255 int32_t status_int;
256 enum SPDY_RST_STREAM_STATUS status;
257 struct SPDYF_Stream *stream;
258
259 SPDYF_ASSERT(SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER == session->status,
260 "the function is called wrong");
261
262 frame = (struct SPDYF_Control_Frame *)session->frame_handler_cls;
263
264 if(0 != frame->flags || 8 != frame->length)
265 {
266 //this is a protocol error
267 SPDYF_DEBUG("wrong RST_STREAM received");
268 //ignore as a large frame
269 session->status = SPDY_SESSION_STATUS_IGNORE_BYTES;
270 return;
271 }
272
273 if((session->read_buffer_offset - session->read_buffer_beginning) < frame->length)
274 {
275 //not all fields are received
276 //try later
277 return;
278 }
279
280 memcpy(&stream_id, session->read_buffer + session->read_buffer_beginning, 4);
281 stream_id = NTOH31(stream_id);
282 session->read_buffer_beginning += 4;
283
284 memcpy(&status_int, session->read_buffer + session->read_buffer_beginning, 4);
285 status = ntohl(status_int);
286 session->read_buffer_beginning += 4;
287
288 session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER;
289 free(frame);
290
291 //mark the stream as closed
292 stream = session->streams_head;
293 while(NULL != stream)
294 {
295 if(stream_id == stream->stream_id)
296 {
297 stream->is_in_closed = true;
298 stream->is_out_closed = true;
299 break;
300 }
301 stream = stream->next;
302 }
303
304 SPDYF_DEBUG("Received RST_STREAM; status=%i; id=%i",status,stream_id);
305
306 //do something according to the status
307 //TODO
308 /*switch(status)
309 {
310 case SPDY_RST_STREAM_STATUS_PROTOCOL_ERROR:
311 break;
312 }*/
313}
314
315
316/**
317 * Handler for reading DATA frames. In requests they are used for POST
318 * arguments.
319 *
320 * @param session SPDY_Session whose read buffer is used.
321 */
322static void
323spdyf_handler_read_data (struct SPDY_Session *session)
324{
325 (void)session;
326 //TODO ignore data frames for now
327 SPDYF_PANIC("POST requests are Not yet implemented!");
328}
329
330
331int
332SPDYF_handler_write_syn_reply (struct SPDY_Session *session)
333{
334 struct SPDYF_Response_Queue *response_queue = session->response_queue_head;
335 struct SPDYF_Stream *stream = response_queue->stream;
336 struct SPDYF_Control_Frame control_frame;
337 void *compressed_headers = NULL;
338 size_t compressed_headers_size=0;
339 size_t used_data=0;
340 size_t total_size;
341 uint32_t stream_id_nbo;
342
343 SPDYF_ASSERT(NULL == session->write_buffer, "the function is called not in the correct moment");
344
345 memcpy(&control_frame, response_queue->control_frame, sizeof(control_frame));
346
347 if(SPDY_YES != SPDYF_zlib_deflate(&session->zlib_send_stream,
348 response_queue->data,
349 response_queue->data_size,
350 &used_data,
351 &compressed_headers,
352 &compressed_headers_size))
353 {
354 /* something went wrong on compressing,
355 * the state of the stream for compression is unknown
356 * and we may not be able to send anything more on
357 * this session,
358 * so it is better to close the session right now */
359 session->status = SPDY_SESSION_STATUS_CLOSING;
360
361 free(compressed_headers);
362
363 return SPDY_NO;
364 }
365
366 //TODO do we need this used_Data
367 SPDYF_ASSERT(used_data == response_queue->data_size, "not everything was used by zlib");
368
369 total_size = sizeof(struct SPDYF_Control_Frame) //SPDY header
370 + 4 // stream id as "subheader"
371 + compressed_headers_size;
372
373 if(NULL == (session->write_buffer = malloc(total_size)))
374 {
375 /* no memory
376 * since we do not save the compressed data anywhere and
377 * the sending zlib stream is already in new state, we must
378 * close the session */
379 session->status = SPDY_SESSION_STATUS_CLOSING;
380
381 free(compressed_headers);
382
383 return SPDY_NO;
384 }
385 session->write_buffer_beginning = 0;
386 session->write_buffer_offset = 0;
387 session->write_buffer_size = total_size;
388
389 control_frame.length = compressed_headers_size + 4; // compressed data + stream_id
390 SPDYF_CONTROL_FRAME_HTON(&control_frame);
391
392 //put frame headers to write buffer
393 memcpy(session->write_buffer + session->write_buffer_offset,&control_frame,sizeof(struct SPDYF_Control_Frame));
394 session->write_buffer_offset += sizeof(struct SPDYF_Control_Frame);
395
396 //put stream id to write buffer
397 stream_id_nbo = HTON31(stream->stream_id);
398 memcpy(session->write_buffer + session->write_buffer_offset, &stream_id_nbo, 4);
399 session->write_buffer_offset += 4;
400
401 //put compressed name/value pairs to write buffer
402 memcpy(session->write_buffer + session->write_buffer_offset, compressed_headers, compressed_headers_size);
403 session->write_buffer_offset += compressed_headers_size;
404
405 SPDYF_ASSERT(0 == session->write_buffer_beginning, "bug1");
406 SPDYF_ASSERT(session->write_buffer_offset == session->write_buffer_size, "bug2");
407
408 //DEBUG CODE, break compression state to see what happens
409/* SPDYF_zlib_deflate(&session->zlib_send_stream,
410 "1234567890",
411 10,
412 &used_data,
413 &compressed_headers,
414 &compressed_headers_size);
415*/
416 free(compressed_headers);
417
418 session->last_replied_to_stream_id = stream->stream_id;
419
420 return SPDY_YES;
421}
422
423
424int
425SPDYF_handler_write_goaway (struct SPDY_Session *session)
426{
427 struct SPDYF_Response_Queue *response_queue = session->response_queue_head;
428 struct SPDYF_Control_Frame control_frame;
429 size_t total_size;
430 int last_good_stream_id;
431
432 SPDYF_ASSERT(NULL == session->write_buffer, "the function is called not in the correct moment");
433
434 memcpy(&control_frame, response_queue->control_frame, sizeof(control_frame));
435
436 session->is_goaway_sent = true;
437
438 total_size = sizeof(struct SPDYF_Control_Frame) //SPDY header
439 + 4 // last good stream id as "subheader"
440 + 4; // status code as "subheader"
441
442 if(NULL == (session->write_buffer = malloc(total_size)))
443 {
444 return SPDY_NO;
445 }
446 session->write_buffer_beginning = 0;
447 session->write_buffer_offset = 0;
448 session->write_buffer_size = total_size;
449
450 control_frame.length = 8; // always for GOAWAY
451 SPDYF_CONTROL_FRAME_HTON(&control_frame);
452
453 //put frame headers to write buffer
454 memcpy(session->write_buffer + session->write_buffer_offset,&control_frame,sizeof(struct SPDYF_Control_Frame));
455 session->write_buffer_offset += sizeof(struct SPDYF_Control_Frame);
456
457 //put last good stream id to write buffer
458 last_good_stream_id = HTON31(session->last_replied_to_stream_id);
459 memcpy(session->write_buffer + session->write_buffer_offset, &last_good_stream_id, 4);
460 session->write_buffer_offset += 4;
461
462 //put "data" to write buffer. This is the status
463 memcpy(session->write_buffer + session->write_buffer_offset, response_queue->data, 4);
464 session->write_buffer_offset += 4;
465 //data is not freed by the destroy function so:
466 //free(response_queue->data);
467
468 SPDYF_ASSERT(0 == session->write_buffer_beginning, "bug1");
469 SPDYF_ASSERT(session->write_buffer_offset == session->write_buffer_size, "bug2");
470
471 return SPDY_YES;
472}
473
474
475int
476SPDYF_handler_write_data (struct SPDY_Session *session)
477{
478 struct SPDYF_Response_Queue *response_queue = session->response_queue_head;
479 struct SPDYF_Response_Queue *new_response_queue;
480 size_t total_size;
481 struct SPDYF_Data_Frame data_frame;
482 ssize_t ret;
483 bool more;
484
485 SPDYF_ASSERT(NULL == session->write_buffer, "the function is called not in the correct moment");
486
487 memcpy(&data_frame, response_queue->data_frame, sizeof(data_frame));
488
489 if(NULL == response_queue->response->rcb)
490 {
491 //standard response with data into the struct
492 SPDYF_ASSERT(NULL != response_queue->data, "no data for the response");
493
494 total_size = sizeof(struct SPDYF_Data_Frame) //SPDY header
495 + response_queue->data_size;
496
497 if(NULL == (session->write_buffer = malloc(total_size)))
498 {
499 return SPDY_NO;
500 }
501 session->write_buffer_beginning = 0;
502 session->write_buffer_offset = 0;
503 session->write_buffer_size = total_size;
504
505 data_frame.length = response_queue->data_size;
506 SPDYF_DATA_FRAME_HTON(&data_frame);
507
508 //put SPDY headers to the writing buffer
509 memcpy(session->write_buffer + session->write_buffer_offset,&data_frame,sizeof(struct SPDYF_Data_Frame));
510 session->write_buffer_offset += sizeof(struct SPDYF_Data_Frame);
511
512 //put data to the writing buffer
513 memcpy(session->write_buffer + session->write_buffer_offset, response_queue->data, response_queue->data_size);
514 session->write_buffer_offset += response_queue->data_size;
515 }
516 else
517 {
518 /* response with callbacks. The lib will produce more than 1
519 * data frames
520 */
521
522 total_size = sizeof(struct SPDYF_Data_Frame) //SPDY header
523 + SPDY_MAX_SUPPORTED_FRAME_SIZE; //max possible size
524
525 if(NULL == (session->write_buffer = malloc(total_size)))
526 {
527 return SPDY_NO;
528 }
529 session->write_buffer_beginning = 0;
530 session->write_buffer_offset = 0;
531 session->write_buffer_size = total_size;
532
533 ret = response_queue->response->rcb(response_queue->response->rcb_cls,
534 session->write_buffer + sizeof(struct SPDYF_Data_Frame),
535 response_queue->response->rcb_block_size,
536 &more);
537
538 if(ret < 0 || ret > response_queue->response->rcb_block_size)
539 {
540 //TODO send RST_STREAM (app error)
541 //for now close session
542 session->status = SPDY_SESSION_STATUS_CLOSING;
543
544 free(session->write_buffer);
545 return SPDY_NO;
546 }
547 if(0 == ret && more)
548 {
549 //the app couldn't write anything to buf but later will
550 free(session->write_buffer);
551 session->write_buffer = NULL;
552 session->write_buffer_size = 0;
553
554 if(NULL != response_queue->next)
555 {
556 //put the frame at the end of the queue
557 //otherwise - head of line blocking
558 session->response_queue_head = response_queue->next;
559 session->response_queue_head->prev = NULL;
560 session->response_queue_tail->next = response_queue;
561 response_queue->prev = session->response_queue_tail;
562 response_queue->next = NULL;
563 session->response_queue_tail = response_queue;
564 }
565
566 return SPDY_YES;
567 }
568
569 if(more)
570 {
571 //create another response queue object to call the user cb again
572 if(NULL == (new_response_queue = SPDYF_response_queue_create(true,
573 NULL,
574 0,
575 response_queue->response,
576 response_queue->stream,
577 false,
578 response_queue->frqcb,
579 response_queue->frqcb_cls,
580 response_queue->rrcb,
581 response_queue->rrcb_cls)))
582 {
583 //TODO send RST_STREAM
584 //for now close session
585 session->status = SPDY_SESSION_STATUS_CLOSING;
586
587 free(session->write_buffer);
588 return SPDY_NO;
589 }
590
591 //put it at second position on the queue
592 new_response_queue->prev = response_queue;
593 new_response_queue->next = response_queue->next;
594 if(NULL == response_queue->next)
595 {
596 session->response_queue_tail = new_response_queue;
597 }
598 else
599 {
600 response_queue->next->prev = new_response_queue;
601 }
602 response_queue->next = new_response_queue;
603
604 response_queue->frqcb = NULL;
605 response_queue->frqcb_cls = NULL;
606 response_queue->rrcb = NULL;
607 response_queue->rrcb_cls = NULL;
608 }
609 else
610 {
611 data_frame.flags |= SPDY_DATA_FLAG_FIN;
612 }
613
614 data_frame.length = ret;
615 SPDYF_DATA_FRAME_HTON(&data_frame);
616
617 //put SPDY headers to the writing buffer
618 memcpy(session->write_buffer + session->write_buffer_offset,
619 &data_frame,
620 sizeof(struct SPDYF_Data_Frame));
621 session->write_buffer_offset += sizeof(struct SPDYF_Data_Frame);
622 session->write_buffer_offset += ret;
623 session->write_buffer_size = session->write_buffer_offset;
624 }
625
626 SPDYF_ASSERT(0 == session->write_buffer_beginning, "bug1");
627 SPDYF_ASSERT(session->write_buffer_offset == session->write_buffer_size, "bug2");
628
629 return SPDY_YES;
630}
631
632
633int
634SPDYF_handler_write_rst_stream (struct SPDY_Session *session)
635{
636 struct SPDYF_Response_Queue *response_queue = session->response_queue_head;
637 struct SPDYF_Control_Frame control_frame;
638 size_t total_size;
639
640 SPDYF_ASSERT(NULL == session->write_buffer, "the function is called not in the correct moment");
641
642 memcpy(&control_frame, response_queue->control_frame, sizeof(control_frame));
643
644 total_size = sizeof(struct SPDYF_Control_Frame) //SPDY header
645 + 4 // stream id as "subheader"
646 + 4; // status code as "subheader"
647
648 if(NULL == (session->write_buffer = malloc(total_size)))
649 {
650 return SPDY_NO;
651 }
652 session->write_buffer_beginning = 0;
653 session->write_buffer_offset = 0;
654 session->write_buffer_size = total_size;
655
656 control_frame.length = 8; // always for RST_STREAM
657 SPDYF_CONTROL_FRAME_HTON(&control_frame);
658
659 //put frame headers to write buffer
660 memcpy(session->write_buffer + session->write_buffer_offset,&control_frame,sizeof(struct SPDYF_Control_Frame));
661 session->write_buffer_offset += sizeof(struct SPDYF_Control_Frame);
662
663 //put stream id to write buffer. This is the status
664 memcpy(session->write_buffer + session->write_buffer_offset, response_queue->data, 8);
665 session->write_buffer_offset += 8;
666 //data is not freed by the destroy function so:
667 //free(response_queue->data);
668
669 SPDYF_ASSERT(0 == session->write_buffer_beginning, "bug1");
670 SPDYF_ASSERT(session->write_buffer_offset == session->write_buffer_size, "bug2");
671
672 return SPDY_YES;
673}
674
675
676void
677SPDYF_handler_ignore_frame (struct SPDY_Session *session)
678{
679 struct SPDYF_Control_Frame *frame;
680
681 SPDYF_ASSERT(SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER == session->status
682 || SPDY_SESSION_STATUS_WAIT_FOR_BODY == session->status,
683 "the function is called wrong");
684
685
686 frame = (struct SPDYF_Control_Frame *)session->frame_handler_cls;
687
688 //handle subheaders
689 if(SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER == session->status)
690 {
691 if(frame->length > SPDY_MAX_SUPPORTED_FRAME_SIZE)
692 {
693 session->status = SPDY_SESSION_STATUS_IGNORE_BYTES;
694 return;
695 }
696 else
697 session->status = SPDY_SESSION_STATUS_WAIT_FOR_BODY;
698 }
699
700 //handle body
701
702 if(session->read_buffer_offset - session->read_buffer_beginning
703 >= frame->length)
704 {
705 session->read_buffer_beginning += frame->length;
706 session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER;
707 free(frame);
708 }
709}
710
711
712int
713SPDYF_session_read (struct SPDY_Session *session)
714{
715 int bytes_read;
716 bool reallocate;
717 size_t actual_buf_size;
718
719 if(SPDY_SESSION_STATUS_CLOSING == session->status
720 || SPDY_SESSION_STATUS_FLUSHING == session->status)
721 return SPDY_NO;
722
723 //if the read buffer is full to the end, we need to reallocate space
724 if (session->read_buffer_size == session->read_buffer_offset)
725 {
726 //but only if the state of the session requires it
727 //i.e. no further proceeding is possible without reallocation
728 reallocate = false;
729 actual_buf_size = session->read_buffer_offset
730 - session->read_buffer_beginning;
731 switch(session->status)
732 {
733 case SPDY_SESSION_STATUS_WAIT_FOR_HEADER:
734
735 case SPDY_SESSION_STATUS_IGNORE_BYTES:
736 //we need space for a whole control frame header
737 if(actual_buf_size < sizeof(struct SPDYF_Control_Frame))
738 reallocate = true;
739 break;
740
741 case SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER:
742
743 case SPDY_SESSION_STATUS_WAIT_FOR_BODY:
744 //we need as many bytes as set in length field of the
745 //header
746 SPDYF_ASSERT(NULL != session->frame_handler_cls,
747 "no frame for session");
748 if(session->frame_handler != &spdyf_handler_read_data)
749 {
750 if(actual_buf_size
751 < ((struct SPDYF_Control_Frame *)session->frame_handler_cls)->length)
752 reallocate = true;
753 }
754 else
755 {
756 if(actual_buf_size
757 < ((struct SPDYF_Data_Frame *)session->frame_handler_cls)->length)
758 reallocate = true;
759 }
760 break;
761
762 case SPDY_SESSION_STATUS_CLOSING:
763 case SPDY_SESSION_STATUS_FLUSHING:
764 //nothing needed
765 break;
766 }
767
768 if(reallocate)
769 {
770 //reuse the space in the buffer that was already read by the lib
771 memmove(session->read_buffer,
772 session->read_buffer + session->read_buffer_beginning,
773 session->read_buffer_offset - session->read_buffer_beginning);
774
775 session->read_buffer_offset -= session->read_buffer_beginning;
776 session->read_buffer_beginning = 0;
777 }
778 else
779 {
780 //will read next time
781 //TODO optimize it, memmove more often?
782 return SPDY_NO;
783 }
784 }
785
786 session->last_activity = SPDYF_monotonic_time();
787
788 //actual read from the TLS socket
789 bytes_read = SPDYF_tls_recv(session,
790 session->read_buffer + session->read_buffer_offset,
791 session->read_buffer_size - session->read_buffer_offset);
792
793 switch(bytes_read)
794 {
795 case SPDY_TLS_ERROR_CLOSED:
796 //The TLS connection was closed by the other party, clean
797 //or not
798 shutdown (session->socket_fd, SHUT_RD);
799 session->read_closed = true;
800 session->status = SPDY_SESSION_STATUS_CLOSING;
801 return SPDY_YES;
802
803 case SPDY_TLS_ERROR_ERROR:
804 //any kind of error in the TLS subsystem
805 //try to prepare GOAWAY frame
806 SPDYF_prepare_goaway(session, SPDY_GOAWAY_STATUS_INTERNAL_ERROR, false);
807 //try to flush the queue when write is called
808 session->status = SPDY_SESSION_STATUS_FLUSHING;
809 return SPDY_YES;
810
811 case SPDY_TLS_ERROR_AGAIN:
812 //read or write should be called again; leave it for the
813 //next time
814 return SPDY_NO;
815
816 //default:
817 //something was really read from the TLS subsystem
818 //just continue
819 }
820
821 session->read_buffer_offset += bytes_read;
822
823 return SPDY_YES;
824}
825
826
827int
828SPDYF_session_write (struct SPDY_Session *session, bool only_one_frame)
829{
830 int i;
831 int bytes_written;
832 struct SPDYF_Response_Queue *queue_head;
833 struct SPDYF_Response_Queue *response_queue;
834
835 if(SPDY_SESSION_STATUS_CLOSING == session->status)
836 return SPDY_NO;
837
838 for(i=0;
839 only_one_frame
840 ? i < 1
841 : i < SPDYF_NUM_SENT_FRAMES_AT_ONCE;
842 ++i)
843 {
844 //if the buffer is not null, part of the last frame is still
845 //pending to be sent
846 if(NULL == session->write_buffer)
847 {
848 //discard frames on closed streams
849 response_queue = session->response_queue_head;
850
851 while(NULL != response_queue)
852 {
853 //if stream is closed, remove not yet sent frames
854 //associated with it
855 //GOAWAY frames are not associated to streams
856 //and still need to be sent
857 if(NULL == response_queue->stream
858 || !response_queue->stream->is_out_closed)
859 break;
860
861 DLL_remove(session->response_queue_head,session->response_queue_tail,response_queue);
862
863 if(NULL != response_queue->frqcb)
864 {
865 response_queue->frqcb(response_queue->frqcb_cls, response_queue, SPDY_RESPONSE_RESULT_STREAM_CLOSED);
866 }
867
868 SPDYF_response_queue_destroy(response_queue);
869 response_queue = session->response_queue_head;
870 }
871
872 if(NULL == session->response_queue_head)
873 break;//nothing on the queue
874
875 //get next data from queue and put it to the write buffer
876 // to send it
877 if(SPDY_NO == session->response_queue_head->process_response_handler(session))
878 {
879 //error occured and the handler changed or not the
880 //session's status appropriately
881 if(SPDY_SESSION_STATUS_CLOSING == session->status)
882 {
883 //try to send GOAWAY first if the current frame is different
884 if(session->response_queue_head->is_data
885 || SPDY_CONTROL_FRAME_TYPES_GOAWAY
886 != session->response_queue_head->control_frame->type)
887 {
888 session->status = SPDY_SESSION_STATUS_FLUSHING;
889 SPDYF_prepare_goaway(session, SPDY_GOAWAY_STATUS_INTERNAL_ERROR, true);
890 SPDYF_session_write(session,true);
891 session->status = SPDY_SESSION_STATUS_CLOSING;
892 }
893 return SPDY_YES;
894 }
895
896 //just return from the loop to return from this function
897 break;
898 }
899
900 //check if something was prepared for writing
901 //on respones with callbacks it is possible that their is no
902 //data available
903 if(0 == session->write_buffer_size)//nothing to write
904 if(response_queue != session->response_queue_head)
905 {
906 //the handler modified the queue
907 continue;
908 }
909 else
910 {
911 //no need to try the same frame again
912 break;
913 }
914 }
915
916 session->last_activity = SPDYF_monotonic_time();
917
918 //actual write to the TLS socket
919 bytes_written = SPDYF_tls_send(session,
920 session->write_buffer + session->write_buffer_beginning,
921 session->write_buffer_offset - session->write_buffer_beginning);
922
923 switch(bytes_written)
924 {
925 case SPDY_TLS_ERROR_CLOSED:
926 //The TLS connection was closed by the other party, clean
927 //or not
928 shutdown (session->socket_fd, SHUT_RD);
929 session->read_closed = true;
930 session->status = SPDY_SESSION_STATUS_CLOSING;
931 return SPDY_YES;
932
933 case SPDY_TLS_ERROR_ERROR:
934 //any kind of error in the TLS subsystem
935 //forbid more writing
936 session->status = SPDY_SESSION_STATUS_CLOSING;
937 return SPDY_YES;
938
939 case SPDY_TLS_ERROR_AGAIN:
940 //read or write should be called again; leave it for the
941 //next time; return from the function as we do not now
942 //whether reading or writing is needed
943 return i>0 ? SPDY_YES : SPDY_NO;
944
945 //default:
946 //something was really read from the TLS subsystem
947 //just continue
948 }
949
950 session->write_buffer_beginning += bytes_written;
951
952 //check if the full buffer was written
953 if(session->write_buffer_beginning == session->write_buffer_size)
954 {
955 //that response is handled, remove it from queue
956 free(session->write_buffer);
957 session->write_buffer = NULL;
958 session->write_buffer_size = 0;
959 queue_head = session->response_queue_head;
960 if(NULL == queue_head->next)
961 {
962 session->response_queue_head = NULL;
963 session->response_queue_tail = NULL;
964 }
965 else
966 {
967 session->response_queue_head = queue_head->next;
968 session->response_queue_head->prev = NULL;
969 }
970
971 //set stream to closed if the frame's fin flag is set
972 SPDYF_stream_set_flags(queue_head);
973
974 if(NULL != queue_head->frqcb)
975 {
976 //application layer callback to notify sending of the response
977 queue_head->frqcb(queue_head->frqcb_cls, queue_head, SPDY_RESPONSE_RESULT_SUCCESS);
978 }
979
980 SPDYF_response_queue_destroy(queue_head);
981 }
982 }
983
984 if(SPDY_SESSION_STATUS_FLUSHING == session->status
985 && NULL == session->response_queue_head)
986 session->status = SPDY_SESSION_STATUS_CLOSING;
987
988 return i>0 ? SPDY_YES : SPDY_NO;
989}
990
991
992int
993SPDYF_session_idle (struct SPDY_Session *session)
994{
995 size_t read_buffer_beginning;
996 size_t frame_length;
997 struct SPDYF_Control_Frame* control_frame;
998 struct SPDYF_Data_Frame *data_frame;
999
1000 //prepare session for closing if timeout is used and already passed
1001 if(SPDY_SESSION_STATUS_CLOSING != session->status
1002 && session->daemon->session_timeout
1003 && (session->last_activity + session->daemon->session_timeout < SPDYF_monotonic_time()))
1004 {
1005 session->status = SPDY_SESSION_STATUS_CLOSING;
1006 //best effort for sending GOAWAY
1007 SPDYF_prepare_goaway(session, SPDY_GOAWAY_STATUS_OK, true);
1008 SPDYF_session_write(session,true);
1009 }
1010
1011 switch(session->status)
1012 {
1013 //expect new frame to arrive
1014 case SPDY_SESSION_STATUS_WAIT_FOR_HEADER:
1015 session->current_stream_id = 0;
1016 //check if the whole frame header is already here
1017 //both frame types have the same length
1018 if(session->read_buffer_offset - session->read_buffer_beginning
1019 < sizeof(struct SPDYF_Control_Frame))
1020 return SPDY_NO;
1021
1022 /* check the first bit to see if it is data or control frame
1023 * and also if the version is supported */
1024 if(0x80 == *(uint8_t *)(session->read_buffer + session->read_buffer_beginning)
1025 && SPDY_VERSION == *((uint8_t *)session->read_buffer + session->read_buffer_beginning + 1))
1026 {
1027 //control frame
1028 if(NULL == (control_frame = malloc(sizeof(struct SPDYF_Control_Frame))))
1029 {
1030 SPDYF_DEBUG("No memory");
1031 return SPDY_NO;
1032 }
1033
1034 //get frame headers
1035 memcpy(control_frame,
1036 session->read_buffer + session->read_buffer_beginning,
1037 sizeof(struct SPDYF_Control_Frame));
1038 session->read_buffer_beginning += sizeof(struct SPDYF_Control_Frame);
1039 SPDYF_CONTROL_FRAME_NTOH(control_frame);
1040
1041 session->status = SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER;
1042 //assign different frame handler according to frame type
1043 switch(control_frame->type){
1044 case SPDY_CONTROL_FRAME_TYPES_SYN_STREAM:
1045 session->frame_handler = &spdyf_handler_read_syn_stream;
1046 break;
1047 case SPDY_CONTROL_FRAME_TYPES_GOAWAY:
1048 session->frame_handler = &spdyf_handler_read_goaway;
1049 break;
1050 case SPDY_CONTROL_FRAME_TYPES_RST_STREAM:
1051 session->frame_handler = &spdyf_handler_read_rst_stream;
1052 break;
1053 default:
1054 session->frame_handler = &SPDYF_handler_ignore_frame;
1055 }
1056 session->frame_handler_cls = control_frame;
1057 //DO NOT break the outer case
1058 }
1059 else if(0 == *(uint8_t *)(session->read_buffer + session->read_buffer_beginning))
1060 {
1061 //needed for POST
1062 //data frame
1063 if(NULL == (data_frame = malloc(sizeof(struct SPDYF_Data_Frame))))
1064 {
1065 SPDYF_DEBUG("No memory");
1066 return SPDY_NO;
1067 }
1068
1069 //get frame headers
1070 memcpy(data_frame,
1071 session->read_buffer + session->read_buffer_beginning,
1072 sizeof(struct SPDYF_Data_Frame));
1073 session->read_buffer_beginning += sizeof(struct SPDYF_Data_Frame);
1074
1075 session->status = SPDY_SESSION_STATUS_WAIT_FOR_BODY;
1076 session->frame_handler = &spdyf_handler_read_data;
1077 session->frame_handler_cls = data_frame;
1078 //DO NOT brake the outer case
1079 }
1080 else
1081 {
1082 SPDYF_DEBUG("another protocol or version received!");
1083
1084 /* According to the draft the lib should send here
1085 * RST_STREAM with status UNSUPPORTED_VERSION. I don't
1086 * see any sense of keeping the session open since
1087 * we don't know how many bytes is the bogus "frame".
1088 * And the latter normally will be HTTP request.
1089 *
1090 */
1091
1092 //shutdown(session->socket_fd, SHUT_RD);
1093 session->status = SPDY_SESSION_STATUS_FLUSHING;
1094 SPDYF_prepare_goaway(session, SPDY_GOAWAY_STATUS_PROTOCOL_ERROR,false);
1095 //SPDYF_session_write(session,false);
1096 /* close connection since the client expects another
1097 protocol from us */
1098 //SPDYF_session_close(session);
1099 return SPDY_YES;
1100 }
1101
1102 //expect specific header fields after the standard header
1103 case SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER:
1104 if(NULL!=session->frame_handler)
1105 {
1106 read_buffer_beginning = session->read_buffer_beginning;
1107 //if everything is ok, the "body" will also be processed
1108 //by the handler
1109 session->frame_handler(session);
1110
1111 if(SPDY_SESSION_STATUS_IGNORE_BYTES == session->status)
1112 {
1113 //check for larger than max supported frame
1114 if(session->frame_handler != &spdyf_handler_read_data)
1115 {
1116 frame_length = ((struct SPDYF_Control_Frame *)session->frame_handler_cls)->length;
1117 }
1118 else
1119 {
1120 frame_length = ((struct SPDYF_Data_Frame *)session->frame_handler_cls)->length;
1121 }
1122
1123 //if(SPDY_MAX_SUPPORTED_FRAME_SIZE < frame_length)
1124 {
1125 SPDYF_DEBUG("received frame with unsupported size: %zu", frame_length);
1126 //the data being received must be ignored and
1127 //RST_STREAM sent
1128
1129 //ignore bytes that will arive later
1130 session->read_ignore_bytes = frame_length
1131 + read_buffer_beginning
1132 - session->read_buffer_offset;
1133 //ignore what is already in read buffer
1134 session->read_buffer_beginning = session->read_buffer_offset;
1135
1136 SPDYF_prepare_rst_stream(session,
1137 session->current_stream_id, //may be 0 here which is not good
1138 SPDY_RST_STREAM_STATUS_FRAME_TOO_LARGE);
1139
1140 //actually the read buffer can be bigger than the
1141 //max supported size
1142 session->status = session->read_ignore_bytes
1143 ? SPDY_SESSION_STATUS_IGNORE_BYTES
1144 : SPDY_SESSION_STATUS_WAIT_FOR_HEADER;
1145
1146 free(session->frame_handler_cls);
1147 }
1148 }
1149 }
1150
1151 if(SPDY_SESSION_STATUS_IGNORE_BYTES != session->status)
1152 {
1153 break;
1154 }
1155
1156 //ignoring data in read buffer
1157 case SPDY_SESSION_STATUS_IGNORE_BYTES:
1158 SPDYF_ASSERT(session->read_ignore_bytes > 0,
1159 "Session is in wrong state");
1160 if(session->read_ignore_bytes
1161 > session->read_buffer_offset - session->read_buffer_beginning)
1162 {
1163 session->read_ignore_bytes -=
1164 session->read_buffer_offset - session->read_buffer_beginning;
1165 session->read_buffer_beginning = session->read_buffer_offset;
1166 }
1167 else
1168 {
1169 session->read_buffer_beginning += session->read_ignore_bytes;
1170 session->read_ignore_bytes = 0;
1171 session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER;
1172 }
1173 break;
1174
1175 //expect frame body (name/value pairs)
1176 case SPDY_SESSION_STATUS_WAIT_FOR_BODY:
1177 if(NULL!=session->frame_handler)
1178 session->frame_handler(session);
1179 break;
1180
1181 case SPDY_SESSION_STATUS_FLUSHING:
1182
1183 return SPDY_NO;
1184
1185 //because of error the session needs to be closed
1186 case SPDY_SESSION_STATUS_CLOSING:
1187 //error should be already sent to the client
1188 SPDYF_session_close(session);
1189 return SPDY_YES;
1190 }
1191
1192 return SPDY_YES;
1193}
1194
1195
1196void
1197SPDYF_session_close (struct SPDY_Session *session)
1198{
1199 struct SPDY_Daemon *daemon = session->daemon;
1200 int by_client = session->read_closed ? SPDY_YES : SPDY_NO;
1201
1202 //shutdown the tls and deinit the tls context
1203 SPDYF_tls_close_session(session);
1204 shutdown (session->socket_fd,
1205 session->read_closed ? SHUT_WR : SHUT_RDWR);
1206 session->read_closed = true;
1207
1208 //remove session from the list
1209 DLL_remove (daemon->sessions_head,
1210 daemon->sessions_tail,
1211 session);
1212 //add the session for the list for cleaning up
1213 DLL_insert (daemon->cleanup_head,
1214 daemon->cleanup_tail,
1215 session);
1216
1217 //call callback for closed session
1218 if(NULL != daemon->session_closed_cb)
1219 {
1220 daemon->session_closed_cb(daemon->cls, session, by_client);
1221 }
1222}
1223
1224
1225int
1226SPDYF_session_accept(struct SPDY_Daemon *daemon)
1227{
1228 int new_socket_fd;
1229 //int fd_flags;
1230 struct SPDY_Session *session = NULL;
1231 socklen_t addr_len;
1232 struct sockaddr *addr;
1233#if HAVE_INET6
1234 struct sockaddr_in6 addr6;
1235
1236 addr = (struct sockaddr *)&addr6;
1237 addr_len = sizeof(addr6);
1238#else
1239 struct sockaddr_in addr4;
1240
1241 addr = (struct sockaddr *)&addr4;
1242 addr_len = sizeof(addr6);
1243#endif
1244
1245 new_socket_fd = accept (daemon->socket_fd, addr, &addr_len);
1246
1247 if(new_socket_fd < 1)
1248 return SPDY_NO;
1249
1250 //setting the socket to be non-blocking
1251 /*
1252 * different handling is needed by libssl if non-blocking is used
1253 *
1254 fd_flags = fcntl (new_socket_fd, F_GETFL);
1255 if ( -1 == fd_flags
1256 || 0 != fcntl (new_socket_fd, F_SETFL, fd_flags | O_NONBLOCK))
1257 {
1258 SPDYF_DEBUG("WARNING: Couldn't set the new connection to be non-blocking");
1259 }
1260 */
1261
1262 if (NULL == (session = malloc (sizeof (struct SPDY_Session))))
1263 {
1264 goto free_and_fail;
1265 }
1266 memset (session, 0, sizeof (struct SPDY_Session));
1267
1268 session->daemon = daemon;
1269 session->socket_fd = new_socket_fd;
1270
1271 //init TLS context, handshake will be done
1272 if(SPDY_YES != SPDYF_tls_new_session(session))
1273 {
1274 goto free_and_fail;
1275 }
1276
1277 //read buffer
1278 session->read_buffer_size = SPDYF_BUFFER_SIZE;
1279 if (NULL == (session->read_buffer = malloc (session->read_buffer_size)))
1280 {
1281 SPDYF_tls_close_session(session);
1282 goto free_and_fail;
1283 }
1284
1285 //address of the client
1286 if (NULL == (session->addr = malloc (addr_len)))
1287 {
1288 SPDYF_tls_close_session(session);
1289 goto free_and_fail;
1290 }
1291 memcpy (session->addr, addr, addr_len);
1292
1293 session->addr_len = addr_len;
1294 session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER;
1295
1296 //init zlib context for the whole session
1297 if(SPDY_YES != SPDYF_zlib_deflate_init(&session->zlib_send_stream))
1298 {
1299 SPDYF_tls_close_session(session);
1300 goto free_and_fail;
1301 }
1302 if(SPDY_YES != SPDYF_zlib_inflate_init(&session->zlib_recv_stream))
1303 {
1304 SPDYF_tls_close_session(session);
1305 SPDYF_zlib_deflate_end(&session->zlib_send_stream);
1306 goto free_and_fail;
1307 }
1308
1309 //add it to daemon's list
1310 DLL_insert(daemon->sessions_head,daemon->sessions_tail,session);
1311
1312 session->last_activity = SPDYF_monotonic_time();
1313
1314 if(NULL != daemon->new_session_cb)
1315 daemon->new_session_cb(daemon->cls, session);
1316
1317 return SPDY_YES;
1318
1319 //for GOTO
1320 free_and_fail:
1321 /* something failed, so shutdown, close and free memory */
1322 shutdown (new_socket_fd, SHUT_RDWR);
1323 close (new_socket_fd);
1324
1325 if(NULL != session)
1326 {
1327 if(NULL != session->addr)
1328 free (session->addr);
1329 if(NULL != session->read_buffer)
1330 free (session->read_buffer);
1331 free (session);
1332 }
1333 return SPDY_NO;
1334}
1335
1336
1337void
1338SPDYF_queue_response (struct SPDYF_Response_Queue *response_to_queue,
1339 struct SPDY_Session *session,
1340 int consider_priority)
1341{
1342 struct SPDYF_Response_Queue *pos;
1343 struct SPDYF_Response_Queue *last;
1344 uint8_t priority;
1345
1346 SPDYF_ASSERT(SPDY_YES != consider_priority || NULL != response_to_queue->stream,
1347 "called with consider_priority but no stream provided");
1348
1349 last = response_to_queue;
1350 while(NULL != last->next)
1351 {
1352 last = last->next;
1353 }
1354
1355 if(SPDY_NO == consider_priority)
1356 {
1357 //put it at the end of the queue
1358 response_to_queue->prev = session->response_queue_tail;
1359 if (NULL == session->response_queue_head)
1360 session->response_queue_head = response_to_queue;
1361 else
1362 session->response_queue_tail->next = response_to_queue;
1363 session->response_queue_tail = last;
1364 return;
1365 }
1366 else if(-1 == consider_priority)
1367 {
1368 //put it at the head of the queue
1369 last->next = session->response_queue_head;
1370 if (NULL == session->response_queue_tail)
1371 session->response_queue_tail = last;
1372 else
1373 session->response_queue_head->prev = response_to_queue;
1374 session->response_queue_head = response_to_queue;
1375 return;
1376 }
1377
1378 if(NULL == session->response_queue_tail)
1379 {
1380 session->response_queue_head = response_to_queue;
1381 session->response_queue_tail = last;
1382 return;
1383 }
1384
1385 //search for the right position to put it
1386 pos = session->response_queue_tail;
1387 priority = response_to_queue->stream->priority;
1388 while(NULL != pos
1389 && pos->stream->priority > priority)
1390 {
1391 pos = pos->prev;
1392 }
1393
1394 if(NULL == pos)
1395 {
1396 //put it on the head
1397 session->response_queue_head->prev = last;
1398 last->next = session->response_queue_head;
1399 session->response_queue_head = response_to_queue;
1400 }
1401 else if(NULL == pos->next)
1402 {
1403 //put it at the end
1404 response_to_queue->prev = pos;
1405 pos->next = response_to_queue;
1406 session->response_queue_tail = last;
1407 }
1408 else
1409 {
1410 response_to_queue->prev = pos;
1411 last->next = pos->next;
1412 pos->next = response_to_queue;
1413 last->next->prev = last;
1414 }
1415}
1416
1417
1418void
1419SPDYF_session_destroy(struct SPDY_Session *session)
1420{
1421 struct SPDYF_Stream *stream;
1422 struct SPDYF_Response_Queue *response_queue;
1423
1424 close (session->socket_fd);
1425 SPDYF_zlib_deflate_end(&session->zlib_send_stream);
1426 SPDYF_zlib_inflate_end(&session->zlib_recv_stream);
1427
1428 //clean up unsent data in the output queue
1429 while (NULL != (response_queue = session->response_queue_head))
1430 {
1431 DLL_remove (session->response_queue_head,
1432 session->response_queue_tail,
1433 response_queue);
1434
1435 if(NULL != response_queue->frqcb)
1436 {
1437 response_queue->frqcb(response_queue->frqcb_cls, response_queue, SPDY_RESPONSE_RESULT_SESSION_CLOSED);
1438 }
1439
1440 SPDYF_response_queue_destroy(response_queue);
1441 }
1442
1443 //clean up the streams belonging to this session
1444 while (NULL != (stream = session->streams_head))
1445 {
1446 DLL_remove (session->streams_head,
1447 session->streams_tail,
1448 stream);
1449
1450 SPDYF_stream_destroy(stream);
1451 }
1452
1453 free(session->addr);
1454 free(session->read_buffer);
1455 free(session->write_buffer);
1456 free(session);
1457}
1458
1459
1460int
1461SPDYF_prepare_goaway (struct SPDY_Session *session,
1462 enum SPDY_GOAWAY_STATUS status,
1463 bool in_front)
1464{
1465 struct SPDYF_Response_Queue *response_to_queue;
1466 struct SPDYF_Control_Frame *control_frame;
1467 uint32_t *data;
1468
1469 if(NULL == (response_to_queue = malloc(sizeof(struct SPDYF_Response_Queue))))
1470 {
1471 return SPDY_NO;
1472 }
1473 memset(response_to_queue, 0, sizeof(struct SPDYF_Response_Queue));
1474
1475 if(NULL == (control_frame = malloc(sizeof(struct SPDYF_Control_Frame))))
1476 {
1477 free(response_to_queue);
1478 return SPDY_NO;
1479 }
1480 memset(control_frame, 0, sizeof(struct SPDYF_Control_Frame));
1481
1482 if(NULL == (data = malloc(4)))
1483 {
1484 free(control_frame);
1485 free(response_to_queue);
1486 return SPDY_NO;
1487 }
1488 *(data) = htonl(status);
1489
1490 control_frame->control_bit = 1;
1491 control_frame->version = SPDY_VERSION;
1492 control_frame->type = SPDY_CONTROL_FRAME_TYPES_GOAWAY;
1493 control_frame->flags = 0;
1494
1495 response_to_queue->control_frame = control_frame;
1496 response_to_queue->process_response_handler = &SPDYF_handler_write_goaway;
1497 response_to_queue->data = data;
1498 response_to_queue->data_size = 4;
1499
1500 SPDYF_queue_response (response_to_queue,
1501 session,
1502 in_front ? -1 : SPDY_NO);
1503
1504 return SPDY_YES;
1505}
1506
1507
1508int
1509SPDYF_prepare_rst_stream (struct SPDY_Session *session,
1510 uint32_t stream_id,
1511 enum SPDY_RST_STREAM_STATUS status)
1512{
1513 struct SPDYF_Response_Queue *response_to_queue;
1514 struct SPDYF_Control_Frame *control_frame;
1515 uint32_t *data;
1516
1517 if(NULL == (response_to_queue = malloc(sizeof(struct SPDYF_Response_Queue))))
1518 {
1519 return SPDY_NO;
1520 }
1521 memset(response_to_queue, 0, sizeof(struct SPDYF_Response_Queue));
1522
1523 if(NULL == (control_frame = malloc(sizeof(struct SPDYF_Control_Frame))))
1524 {
1525 free(response_to_queue);
1526 return SPDY_NO;
1527 }
1528 memset(control_frame, 0, sizeof(struct SPDYF_Control_Frame));
1529
1530 if(NULL == (data = malloc(8)))
1531 {
1532 free(control_frame);
1533 free(response_to_queue);
1534 return SPDY_NO;
1535 }
1536 *(data) = HTON31(stream_id);
1537 *(data + 1) = htonl(status);
1538
1539 control_frame->control_bit = 1;
1540 control_frame->version = SPDY_VERSION;
1541 control_frame->type = SPDY_CONTROL_FRAME_TYPES_RST_STREAM;
1542 control_frame->flags = 0;
1543
1544 response_to_queue->control_frame = control_frame;
1545 response_to_queue->process_response_handler = &SPDYF_handler_write_rst_stream;
1546 response_to_queue->data = data;
1547 response_to_queue->data_size = 8;
1548
1549 SPDYF_queue_response (response_to_queue,
1550 session,
1551 -1);
1552
1553 return SPDY_YES;
1554}
diff --git a/src/microspdy/session.h b/src/microspdy/session.h
new file mode 100644
index 00000000..cdfa8d15
--- /dev/null
+++ b/src/microspdy/session.h
@@ -0,0 +1,248 @@
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 session.h
21 * @brief TCP connection/SPDY session handling
22 * @author Andrey Uzunov
23 */
24
25#ifndef SESSION_H
26#define SESSION_H
27
28#include "platform.h"
29#include "structures.h"
30
31/**
32 * Called by the daemon when the socket for the session has available
33 * data to be read. Reads data from the TLS socket and puts it to the
34 * session's read buffer. The latte
35 *
36 * @param session SPDY_Session for which data will be read.
37 * @return SPDY_YES if something was read or session's status was
38 * changed. It is possible that error occurred but was handled
39 * and the status was therefore changed.
40 * SPDY_NO if nothing happened, e.g. the subsystem wants read/
41 * write to be called again.
42 */
43int
44SPDYF_session_read (struct SPDY_Session *session);
45
46
47/**
48 * Called by the daemon when the socket for the session is ready for some
49 * data to be written to it. For one or more objects on the response
50 * queue tries to fill in the write buffer, based on the frame on the
51 * queue, and to write data to the TLS socket.
52 *
53 * @param session SPDY_Session for which data will be written.
54 * @return TODO document after changes
55 * SPDY_YES if something was written, the status was changed or
56 * response callback was called but did not provide data
57 * @return SPDY_YES if something was written, session's status was
58 * changed or response callback was called but did not provide
59 * data. It is possible that error occurred but was handled
60 * and the status was therefore changed.
61 * SPDY_NO if nothing happened, e.g. the subsystem wants read/
62 * write to be called again. However, it is possible that some
63 * frames were discarded within the call, e.g. frames belonging
64 * to a closed stream.
65 */
66int
67SPDYF_session_write (struct SPDY_Session *session, bool only_one_frame);
68
69
70/**
71 * Called by the daemon on SPDY_run to handle the data in the read and write
72 * buffer of a session. Based on the state and the content of the read
73 * buffer new frames are received and interpreted, appropriate user
74 * callbacks are called and maybe something is put on the response queue
75 * ready to be handled by session_write.
76 *
77 * @param session SPDY_Session which will be handled.
78 * @return SPDY_YES if something from the read buffers was processed,
79 * session's status was changed and/or the session was closed.
80 * SPDY_NO if nothing happened, e.g. the session is in a state,
81 * not allowing processing read buffers.
82 */
83int
84SPDYF_session_idle (struct SPDY_Session *session);
85
86
87/**
88 * This function shutdowns the socket, moves the session structure to
89 * daemon's queue for sessions to be cleaned up.
90 *
91 * @param session SPDY_Session which will be handled.
92 */
93void
94SPDYF_session_close (struct SPDY_Session *session);
95
96
97/**
98 * Called to accept new TCP connection and create SPDY session.
99 *
100 * @param daemon SPDY_Daemon whose listening socket is used.
101 * @return SPDY_NO on any kind of error while accepting new TCP connection
102 * and initializing new SPDY_Session.
103 * SPDY_YES otherwise.
104 */
105int
106SPDYF_session_accept(struct SPDY_Daemon *daemon);
107
108
109/**
110 * Puts SPDYF_Response_Queue object on the queue to be sent to the
111 * client later.
112 *
113 * @param response_to_queue linked list of objects containing SPDY
114 * frame and data to be added to the queue
115 * @param session SPDY session for which the response is sent
116 * @param consider_priority if SPDY_NO, the list will be added to the
117 * end of the queue.
118 * If SPDY_YES, the response will be added after
119 * the last previously added response with priority of the
120 * request grater or equal to that of the current one.
121 * If -1, the object will be put at the head of the queue.
122 */
123void
124SPDYF_queue_response (struct SPDYF_Response_Queue *response_to_queue,
125 struct SPDY_Session *session,
126 int consider_priority);
127
128
129/**
130 * Cleans up the TSL context for the session, closes the TCP connection,
131 * cleans up any data pointed by members of the session structure
132 * (buffers, queue of responses, etc.) and frees the memory allocated by
133 * the session itself.
134 */
135void
136SPDYF_session_destroy(struct SPDY_Session *session);
137
138
139/**
140 * Prepares GOAWAY frame to tell the client to stop creating new streams.
141 * The session should be closed soon after this call.
142 *
143 * @param session SPDY session
144 * @param status code for the GOAWAY frame
145 * @param in_front whether or not to put the frame in front of everything
146 * on the response queue
147 * @return SPDY_NO on error (not enough memory) or
148 * SPDY_YES on success
149 */
150int
151SPDYF_prepare_goaway (struct SPDY_Session *session,
152 enum SPDY_GOAWAY_STATUS status,
153 bool in_front);
154
155
156/**
157 * Prepares RST_STREAM frame to terminate a stream. This frame may or
158 * not indicate an error. The frame will be put at the head of the queue.
159 * This means that frames for this stream which are still in the queue
160 * will be discarded soon.
161 *
162 * @param session SPDY session
163 * @param stream_id stream to terminate
164 * @param status code for the RST_STREAM frame
165 * @return SPDY_NO on memory error or
166 * SPDY_YES on success
167 */
168int
169SPDYF_prepare_rst_stream (struct SPDY_Session *session,
170 uint32_t stream_id,
171 enum SPDY_RST_STREAM_STATUS status);
172
173
174/**
175 * Handler called by session_write to fill the write buffer according to
176 * the data frame waiting in the response queue.
177 * When response data is given by user callback, the lib does not know
178 * how many frames are needed. In such case this call produces
179 * another ResponseQueue object and puts it on the queue while the the
180 * user callback says that there will be more data.
181 *
182 * @return SPDY_NO on error (not enough memory or the user calback for
183 * providing response data did something wrong). If
184 * the error is unrecoverable the handler changes session's
185 * status.
186 * SPDY_YES on success
187 */
188int
189SPDYF_handler_write_data (struct SPDY_Session *session);
190
191
192/**
193 * Handler called by session_write to fill the write buffer based on the
194 * control frame (SYN_REPLY) waiting in the response queue.
195 *
196 * @param session SPDY session
197 * @return SPDY_NO on error (zlib state is broken; the session MUST be
198 * closed). If
199 * the error is unrecoverable the handler changes session's
200 * status.
201 * SPDY_YES on success
202 */
203int
204SPDYF_handler_write_syn_reply (struct SPDY_Session *session);
205
206
207/**
208 * Handler called by session_write to fill the write buffer based on the
209 * control frame (GOAWAY) waiting in the response queue.
210 *
211 * @param session SPDY session
212 * @return SPDY_NO on error (not enough memory; by specification the
213 * session must be closed
214 * soon, thus there is no need to handle the error) or
215 * SPDY_YES on success
216 */
217int
218SPDYF_handler_write_goaway (struct SPDY_Session *session);
219
220
221/**
222 * Handler called by session_write to fill the write buffer based on the
223 * control frame (RST_STREAM) waiting in the response queue.
224 *
225 * @param session SPDY session
226 * @return SPDY_NO on error (not enough memory). If
227 * the error is unrecoverable the handler changes session's
228 * status.
229 * SPDY_YES on success
230 */
231int
232SPDYF_handler_write_rst_stream (struct SPDY_Session *session);
233
234
235/**
236 * Carefully ignore the full size of frames which are not yet supported
237 * by the lib.
238 * TODO Ignoring frames containing compressed bodies means that the
239 * compress state will be corrupted on next received frame. According to
240 * the draft the lib SHOULD try to decompress data also in corrupted
241 * frames just to keep right compression state.
242 *
243 * @param session SPDY_Session whose read buffer is used.
244 */
245void
246SPDYF_handler_ignore_frame (struct SPDY_Session *session);
247
248#endif
diff --git a/src/microspdy/stream.c b/src/microspdy/stream.c
new file mode 100644
index 00000000..97fdd6c8
--- /dev/null
+++ b/src/microspdy/stream.c
@@ -0,0 +1,151 @@
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 stream.c
21 * @brief SPDY streams handling
22 * @author Andrey Uzunov
23 */
24
25#include "platform.h"
26#include "structures.h"
27#include "internal.h"
28#include "session.h"
29
30
31int
32SPDYF_stream_new (struct SPDY_Session *session)
33{
34 uint32_t stream_id;
35 uint32_t assoc_stream_id;
36 uint8_t priority;
37 uint8_t slot;
38 size_t buffer_pos = session->read_buffer_beginning;
39 struct SPDYF_Stream *stream;
40 struct SPDYF_Control_Frame *frame;
41
42 if((session->read_buffer_offset - session->read_buffer_beginning) < 10)
43 {
44 //not all fields are received to create new stream
45 return SPDY_NO;
46 }
47
48 frame = (struct SPDYF_Control_Frame *)session->frame_handler_cls;
49
50 //get stream id of the new stream
51 memcpy(&stream_id, session->read_buffer + session->read_buffer_beginning, 4);
52 stream_id = NTOH31(stream_id);
53 session->read_buffer_beginning += 4;
54 if(stream_id <= session->last_in_stream_id || 0==(stream_id % 2))
55 {
56 //wrong stream id sent by client
57 //GOAWAY with PROTOCOL_ERROR MUST be sent
58 //TODO
59
60 //ignore frame
61 session->frame_handler = &SPDYF_handler_ignore_frame;
62 return SPDY_NO;
63 }
64 else if(session->is_goaway_sent)
65 {
66 //the client is not allowed to create new streams anymore
67 //we MUST ignore the frame
68 session->frame_handler = &SPDYF_handler_ignore_frame;
69 return SPDY_NO;
70 }
71
72 //set highest stream id for session
73 session->last_in_stream_id = stream_id;
74
75 //get assoc stream id of the new stream
76 //this value is used with SPDY PUSH, thus nothing to do with it here
77 memcpy(&assoc_stream_id, session->read_buffer + session->read_buffer_beginning, 4);
78 assoc_stream_id = NTOH31(assoc_stream_id);
79 session->read_buffer_beginning += 4;
80
81 //get stream priority (3 bits)
82 //after it there are 5 bits that are not used
83 priority = *(uint8_t *)(session->read_buffer + session->read_buffer_beginning) >> 5;
84 session->read_buffer_beginning++;
85
86 //get slot (see SPDY draft)
87 slot = *(uint8_t *)(session->read_buffer + session->read_buffer_beginning);
88 session->read_buffer_beginning++;
89
90 if(NULL == (stream = malloc(sizeof(struct SPDYF_Stream))))
91 {
92 SPDYF_DEBUG("No memory");
93 //revert buffer state
94 session->read_buffer_beginning = buffer_pos;
95 return SPDY_NO;
96 }
97 memset(stream,0, sizeof(struct SPDYF_Stream));
98 stream->session = session;
99 stream->stream_id = stream_id;
100 stream->assoc_stream_id = assoc_stream_id;
101 stream->priority = priority;
102 stream->slot = slot;
103 stream->is_in_closed = (frame->flags & SPDY_SYN_STREAM_FLAG_FIN) != 0;
104 stream->flag_unidirectional = (frame->flags & SPDY_SYN_STREAM_FLAG_UNIDIRECTIONAL) != 0;
105 stream->is_out_closed = stream->flag_unidirectional;
106 stream->is_server_initiator = false;
107
108 //put the stream to the list of streams for the session
109 DLL_insert(session->streams_head, session->streams_tail, stream);
110
111 return SPDY_YES;
112}
113
114
115void
116SPDYF_stream_destroy(struct SPDYF_Stream *stream)
117{
118 SPDY_name_value_destroy(stream->headers);
119 free(stream);
120 stream = NULL;
121}
122
123
124void
125SPDYF_stream_set_flags(struct SPDYF_Response_Queue *response_queue)
126{
127 struct SPDYF_Stream * stream = response_queue->stream;
128
129 if(NULL != response_queue->data_frame)
130 {
131 stream->is_out_closed = (bool)(response_queue->data_frame->flags & SPDY_DATA_FLAG_FIN);
132 }
133 else if(NULL != response_queue->control_frame)
134 {
135 switch(response_queue->control_frame->type)
136 {
137 case SPDY_CONTROL_FRAME_TYPES_SYN_REPLY:
138 stream->is_out_closed = (bool)(response_queue->control_frame->flags & SPDY_SYN_REPLY_FLAG_FIN);
139 break;
140
141 case SPDY_CONTROL_FRAME_TYPES_RST_STREAM:
142 if(NULL != stream)
143 {
144 stream->is_out_closed = true;
145 stream->is_in_closed = true;
146 }
147 break;
148
149 }
150 }
151}
diff --git a/src/microspdy/stream.h b/src/microspdy/stream.h
new file mode 100644
index 00000000..a795ad28
--- /dev/null
+++ b/src/microspdy/stream.h
@@ -0,0 +1,65 @@
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 stream.h
21 * @brief SPDY streams handling
22 * @author Andrey Uzunov
23 */
24
25#ifndef STREAM_H
26#define STREAM_H
27
28#include "platform.h"
29
30
31/**
32 * Reads data from session's read buffer and tries to create a new SPDY
33 * stream. This function is called after control frame's header has been
34 * read from the buffer (after the length field). If bogus frame is
35 * received the function changes the read handler of the session and
36 * fails, i.e. there is no need of further error handling by the caller.
37 *
38 * @param session SPDY_Session whose read buffer is being read
39 * @return SPDY_YES if a new SPDY stream request was correctly received
40 * and handled. SPDY_NO if the whole SPDY frame was not yet
41 * received or memory error occurred.
42 */
43int
44SPDYF_stream_new (struct SPDY_Session *session);
45
46
47/**
48 * Destroys stream structure and whatever is in it.
49 *
50 * @param stream SPDY_Stream to destroy
51 */
52void
53SPDYF_stream_destroy(struct SPDYF_Stream *stream);
54
55
56/**
57 * Set stream flags if needed based on the type of the frame that was
58 * just sent (e.g., close stream if it was RST_STREAM).
59 *
60 * @param response_queue sent for this stream
61 */
62void
63SPDYF_stream_set_flags(struct SPDYF_Response_Queue *response_queue);
64
65#endif
diff --git a/src/microspdy/structures.c b/src/microspdy/structures.c
new file mode 100644
index 00000000..b3760be3
--- /dev/null
+++ b/src/microspdy/structures.c
@@ -0,0 +1,612 @@
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 structures.c
21 * @brief Functions for handling most of the structures in defined
22 * in structures.h
23 * @author Andrey Uzunov
24 */
25
26#include "platform.h"
27#include "structures.h"
28#include "internal.h"
29#include "session.h"
30
31
32struct SPDY_NameValue *
33SPDY_name_value_create ()
34{
35 struct SPDY_NameValue *pair;
36
37 if(NULL == (pair = malloc(sizeof(struct SPDY_NameValue))))
38 return NULL;
39
40 memset (pair, 0, sizeof (struct SPDY_NameValue));
41
42 return pair;
43}
44
45
46int
47SPDY_name_value_add (struct SPDY_NameValue *container,
48 const char *name,
49 const char *value)
50{
51 uint i;
52 uint len;
53 struct SPDY_NameValue *pair;
54 struct SPDY_NameValue *temp;
55 char **temp_value;
56 char *temp_string;
57
58 if(NULL == container || NULL == name || 0 == (len = strlen(name)))
59 return SPDY_INPUT_ERROR;
60
61 for(i=0; i<len; ++i)
62 {
63 if(isupper(name[i]))
64 return SPDY_INPUT_ERROR;
65 }
66
67 if(NULL == container->name && NULL == container->value)
68 {
69 //container is empty/just created
70 if (NULL == (container->name = strdup (name)))
71 {
72 return SPDY_NO;
73 }
74 if (NULL == (container->value = malloc(sizeof(char *))))
75 {
76 free(container->name);
77 return SPDY_NO;
78 }
79 if (NULL == (container->value[0] = strdup (value)))
80 {
81 free(container->value);
82 free(container->name);
83 return SPDY_NO;
84 }
85 container->num_values = 1;
86 return SPDY_YES;
87 }
88
89 pair = container;
90 while(NULL != pair)
91 {
92 if(0 == strcmp(pair->name, name))
93 {
94 //the value will be added to this pair
95 break;
96 }
97 pair = pair->next;
98 }
99
100 if(NULL == pair)
101 {
102 //the name doesn't exist in container, add new pair
103 if(NULL == (pair = malloc(sizeof(struct SPDY_NameValue))))
104 return SPDY_NO;
105
106 memset(pair, 0, sizeof(struct SPDY_NameValue));
107
108 if (NULL == (pair->name = strdup (name)))
109 {
110 free(pair);
111 return SPDY_NO;
112 }
113 if (NULL == (pair->value = malloc(sizeof(char *))))
114 {
115 free(pair->name);
116 free(pair);
117 return SPDY_NO;
118 }
119 if (NULL == (pair->value[0] = strdup (value)))
120 {
121 free(pair->value);
122 free(pair->name);
123 free(pair);
124 return SPDY_NO;
125 }
126 pair->num_values = 1;
127
128 temp = container;
129 while(NULL != temp->next)
130 temp = temp->next;
131 temp->next = pair;
132 pair->prev = temp;
133
134 return SPDY_YES;
135 }
136
137 //check for duplication (case sensitive)
138 for(i=0; i<pair->num_values; ++i)
139 if(0 == strcmp(pair->value[i], value))
140 return SPDY_NO;
141
142 if(strlen(pair->value[0]) > 0)
143 {
144 //the value will be appended to the others for this name
145 if (NULL == (temp_value = malloc((pair->num_values + 1) * sizeof(char *))))
146 {
147 return SPDY_NO;
148 }
149 memcpy(temp_value, pair->value, pair->num_values * sizeof(char *));
150 if (NULL == (temp_value[pair->num_values] = strdup (value)))
151 {
152 free(temp_value);
153 return SPDY_NO;
154 }
155 free(pair->value);
156 pair->value = temp_value;
157 ++pair->num_values;
158 return SPDY_YES;
159 }
160
161 //just replace the empty value
162
163 if (NULL == (temp_string = strdup (value)))
164 {
165 return SPDY_NO;
166 }
167 free(pair->value[0]);
168 pair->value[0] = temp_string;
169
170 return SPDY_YES;
171}
172
173
174const char * const *
175SPDY_name_value_lookup (struct SPDY_NameValue *container,
176 const char *name,
177 int *num_values)
178{
179 struct SPDY_NameValue *temp = container;
180
181 if(NULL == container || NULL == name || NULL == num_values)
182 return NULL;
183 if(NULL == container->name && NULL == container->value)
184 return NULL;
185
186 do
187 {
188 if(strcmp(name, temp->name) == 0)
189 {
190 *num_values = temp->num_values;
191 return (const char * const *)temp->value;
192 }
193
194 temp = temp->next;
195 }
196 while(NULL != temp);
197
198 return NULL;
199}
200
201
202void
203SPDY_name_value_destroy (struct SPDY_NameValue *container)
204{
205 uint i;
206 struct SPDY_NameValue *temp = container;
207
208 while(NULL != temp)
209 {
210 container = container->next;
211 free(temp->name);
212 for(i=0; i<temp->num_values; ++i)
213 free(temp->value[i]);
214 free(temp->value);
215 free(temp);
216 temp=container;
217 }
218}
219
220
221int
222SPDY_name_value_iterate (struct SPDY_NameValue *container,
223 SPDY_NameValueIterator iterator,
224 void *iterator_cls)
225{
226 int count;
227 int ret;
228 struct SPDY_NameValue *temp = container;
229
230 if(NULL == container)
231 return SPDY_INPUT_ERROR;
232
233 //check if container is an empty struct
234 if(NULL == container->name && NULL == container->value)
235 return 0;
236
237 count = 0;
238
239 if(NULL == iterator)
240 {
241 do
242 {
243 ++count;
244 temp=temp->next;
245 }
246 while(NULL != temp);
247
248 return count;
249 }
250
251 //code duplication for avoiding if here
252 do
253 {
254 ++count;
255 ret = iterator(iterator_cls, temp->name, (const char * const *)temp->value, temp->num_values);
256 temp=temp->next;
257 }
258 while(NULL != temp && SPDY_YES == ret);
259
260 return count;
261}
262
263void
264SPDY_destroy_response(struct SPDY_Response *response)
265{
266 free(response->data);
267 free(response->headers);
268 free(response);
269}
270
271
272struct SPDYF_Response_Queue *
273SPDYF_response_queue_create(bool is_data,
274 void *data,
275 size_t data_size,
276 struct SPDY_Response *response,
277 struct SPDYF_Stream *stream,
278 bool closestream,
279 SPDYF_ResponseQueueResultCallback frqcb,
280 void *frqcb_cls,
281 SPDY_ResponseResultCallback rrcb,
282 void *rrcb_cls)
283{
284 struct SPDYF_Response_Queue *head = NULL;
285 struct SPDYF_Response_Queue *prev;
286 struct SPDYF_Response_Queue *response_to_queue;
287 struct SPDYF_Control_Frame *control_frame;
288 struct SPDYF_Data_Frame *data_frame;
289 uint i;
290 bool is_last;
291
292 SPDYF_ASSERT(!is_data
293 || 0 == data_size && NULL != response->rcb
294 || 0 < data_size && NULL == response->rcb,
295 "either data or request->rcb must not be null");
296
297 if(is_data && data_size > SPDY_MAX_SUPPORTED_FRAME_SIZE)
298 {
299 //separate the data in more frames and add them to the queue
300
301 prev=NULL;
302 for(i = 0; i < data_size; i += SPDY_MAX_SUPPORTED_FRAME_SIZE)
303 {
304 is_last = (i + SPDY_MAX_SUPPORTED_FRAME_SIZE) >= data_size;
305
306 if(NULL == (response_to_queue = malloc(sizeof(struct SPDYF_Response_Queue))))
307 goto free_and_fail;
308
309 memset(response_to_queue, 0, sizeof(struct SPDYF_Response_Queue));
310 if(0 == i)
311 head = response_to_queue;
312
313 if(NULL == (data_frame = malloc(sizeof(struct SPDYF_Data_Frame))))
314 {
315 free(response_to_queue);
316 goto free_and_fail;
317 }
318 memset(data_frame, 0, sizeof(struct SPDYF_Data_Frame));
319 data_frame->control_bit = 0;
320 data_frame->stream_id = stream->stream_id;
321 if(is_last && closestream)
322 data_frame->flags |= SPDY_DATA_FLAG_FIN;
323
324 response_to_queue->data_frame = data_frame;
325 response_to_queue->process_response_handler = &SPDYF_handler_write_data;
326 response_to_queue->is_data = is_data;
327 response_to_queue->stream = stream;
328 if(is_last)
329 {
330 response_to_queue->frqcb = frqcb;
331 response_to_queue->frqcb_cls = frqcb_cls;
332 response_to_queue->rrcb = rrcb;
333 response_to_queue->rrcb_cls = rrcb_cls;
334 }
335 response_to_queue->data = data + i;
336 response_to_queue->data_size = is_last
337 ? (data_size - 1) % SPDY_MAX_SUPPORTED_FRAME_SIZE + 1
338 : SPDY_MAX_SUPPORTED_FRAME_SIZE;
339 response_to_queue->response = response;
340
341 response_to_queue->prev = prev;
342 if(NULL != prev)
343 prev->next = response_to_queue;
344 prev = response_to_queue;
345 }
346
347 return head;
348
349 //for GOTO
350 free_and_fail:
351 while(NULL != head)
352 {
353 response_to_queue = head;
354 head = head->next;
355 free(response_to_queue->data_frame);
356 free(response_to_queue);
357 }
358 return NULL;
359 }
360
361 //create only one frame for data, data with callback or control frame
362
363 if(NULL == (response_to_queue = malloc(sizeof(struct SPDYF_Response_Queue))))
364 {
365 return NULL;
366 }
367 memset(response_to_queue, 0, sizeof(struct SPDYF_Response_Queue));
368
369 if(is_data)
370 {
371 if(NULL == (data_frame = malloc(sizeof(struct SPDYF_Data_Frame))))
372 {
373 free(response_to_queue);
374 return NULL;
375 }
376 memset(data_frame, 0, sizeof(struct SPDYF_Data_Frame));
377 data_frame->control_bit = 0;
378 data_frame->stream_id = stream->stream_id;
379 if(closestream && NULL == response->rcb)
380 data_frame->flags |= SPDY_DATA_FLAG_FIN;
381
382 response_to_queue->data_frame = data_frame;
383 response_to_queue->process_response_handler = &SPDYF_handler_write_data;
384 }
385 else
386 {
387 if(NULL == (control_frame = malloc(sizeof(struct SPDYF_Control_Frame))))
388 {
389 free(response_to_queue);
390 return NULL;
391 }
392 memset(control_frame, 0, sizeof(struct SPDYF_Control_Frame));
393 control_frame->control_bit = 1;
394 control_frame->version = SPDY_VERSION;
395 control_frame->type = SPDY_CONTROL_FRAME_TYPES_SYN_REPLY;
396 if(closestream)
397 control_frame->flags |= SPDY_SYN_REPLY_FLAG_FIN;
398
399 response_to_queue->control_frame = control_frame;
400 response_to_queue->process_response_handler = &SPDYF_handler_write_syn_reply;
401 }
402
403 response_to_queue->is_data = is_data;
404 response_to_queue->stream = stream;
405 response_to_queue->frqcb = frqcb;
406 response_to_queue->frqcb_cls = frqcb_cls;
407 response_to_queue->rrcb = rrcb;
408 response_to_queue->rrcb_cls = rrcb_cls;
409 response_to_queue->data = data;
410 response_to_queue->data_size = data_size;
411 response_to_queue->response = response;
412
413 return response_to_queue;
414}
415
416
417void
418SPDYF_response_queue_destroy(struct SPDYF_Response_Queue *response_queue)
419{
420 //data is not copied to the struct but only linked
421 //but this is not valid for GOAWAY and RST_STREAM
422 if(!response_queue->is_data
423 && (SPDY_CONTROL_FRAME_TYPES_RST_STREAM == response_queue->control_frame->type
424 || SPDY_CONTROL_FRAME_TYPES_GOAWAY == response_queue->control_frame->type))
425 {
426 free(response_queue->data);
427 }
428 if(response_queue->is_data)
429 free(response_queue->data_frame);
430 else
431 free(response_queue->control_frame);
432
433 free(response_queue);
434}
435
436
437ssize_t
438SPDYF_name_value_to_stream(struct SPDY_NameValue * container[],
439 int num_containers,
440 void **stream)
441{
442 size_t size;
443 int32_t num_pairs = 0;
444 int32_t value_size;
445 int32_t name_size;
446 int32_t temp;
447 uint i;
448 uint offset;
449 uint value_offset;
450 struct SPDY_NameValue * iterator;
451 int j;
452
453 size = 4; //for num pairs
454
455 for(j=0; j<num_containers; ++j)
456 {
457 iterator = container[j];
458 while(iterator != NULL)
459 {
460 ++num_pairs;
461 size += 4 + strlen(iterator->name); //length + string
462
463 SPDYF_ASSERT(iterator->num_values>0, "num_values is 0");
464
465 size += 4; //value length
466
467 for(i=0; i<iterator->num_values; ++i)
468 {
469 size += strlen(iterator->value[i]); // string
470 if(i/* || !strlen(iterator->value[i])*/) ++size; //NULL separator
471 }
472
473 iterator = iterator->next;
474 }
475}
476
477 if(NULL == (*stream = malloc(size)))
478 {
479 return -1;
480 }
481
482 //put num_pairs to the stream
483 num_pairs = htonl(num_pairs);
484 memcpy(*stream, &num_pairs, 4);
485 offset = 4;
486
487 //put all other headers to the stream
488 for(j=0; j<num_containers; ++j)
489 {
490 iterator = container[j];
491 while(iterator != NULL)
492 {
493 name_size = strlen(iterator->name);
494 temp = htonl(name_size);
495 memcpy(*stream + offset, &temp, 4);
496 offset += 4;
497 strncpy(*stream + offset, iterator->name, name_size);
498 offset += name_size;
499
500 value_offset = offset;
501 offset += 4;
502 for(i=0; i<iterator->num_values; ++i)
503 {
504 if(i /*|| !strlen(iterator->value[0])*/)
505 {
506 memset(*stream + offset, 0, 1);
507 ++offset;
508 if(!i) continue;
509 }
510 strncpy(*stream + offset, iterator->value[i], strlen(iterator->value[i]));
511 offset += strlen(iterator->value[i]);
512 }
513 value_size = offset - value_offset - 4;
514 value_size = htonl(value_size);
515 memcpy(*stream + value_offset, &value_size, 4);
516
517 iterator = iterator->next;
518 }
519}
520
521 SPDYF_ASSERT(offset == size,"offset is wrong");
522
523 return size;
524}
525
526
527int
528SPDYF_name_value_from_stream(void *stream,
529 size_t size,
530 struct SPDY_NameValue ** container)
531{
532 int32_t num_pairs;
533 int32_t value_size;
534 int32_t name_size;
535 int i;
536 uint offset = 0;
537 uint value_end_offset;
538 char *name;
539 char *value;
540
541 if(NULL == (*container = SPDY_name_value_create ()))
542 {
543 return SPDY_NO;
544 }
545
546 //get number of pairs
547 memcpy(&num_pairs, stream, 4);
548 offset = 4;
549 num_pairs = ntohl(num_pairs);
550
551 if(num_pairs > 0)
552 {
553 for(i = 0; i < num_pairs; ++i)
554 {
555 //get name size
556 memcpy(&name_size, stream + offset, 4);
557 offset += 4;
558 name_size = ntohl(name_size);
559 //get name
560 if(NULL == (name = strndup(stream + offset, name_size)))
561 {
562 SPDY_name_value_destroy(*container);
563 return SPDY_NO;
564 }
565 offset+=name_size;
566
567 //get value size
568 memcpy(&value_size, stream + offset, 4);
569 offset += 4;
570 value_size = ntohl(value_size);
571 value_end_offset = offset + value_size;
572 //get value
573 do
574 {
575 if(NULL == (value = strndup(stream + offset, value_size)))
576 {
577 free(name);
578 SPDY_name_value_destroy(*container);
579 return SPDY_NO;
580 }
581 offset += strlen(value);
582 if(offset < value_end_offset)
583 ++offset; //NULL separator
584
585 //add name/value to the struct
586 if(SPDY_YES != SPDY_name_value_add(*container, name, value))
587 {
588 free(name);
589 free(value);
590 SPDY_name_value_destroy(*container);
591 return SPDY_NO;
592 }
593 free(value);
594 }
595 while(offset < value_end_offset);
596
597 free(name);
598
599 if(offset != value_end_offset)
600 {
601 SPDY_name_value_destroy(*container);
602 return SPDY_INPUT_ERROR;
603 }
604 }
605 }
606
607 if(offset == size)
608 return SPDY_YES;
609
610 SPDY_name_value_destroy(*container);
611 return SPDY_INPUT_ERROR;
612}
diff --git a/src/microspdy/structures.h b/src/microspdy/structures.h
new file mode 100644
index 00000000..5fb91889
--- /dev/null
+++ b/src/microspdy/structures.h
@@ -0,0 +1,1128 @@
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 structures.h
21 * @brief internal and public structures -- most of the structs used by
22 * the library are defined here
23 * @author Andrey Uzunov
24 */
25
26#ifndef STRUCTURES_H
27#define STRUCTURES_H
28
29#include "platform.h"
30#include "microspdy.h"
31#include "tls.h"
32
33
34/**
35 * All possible SPDY control frame types. The number is used in the header
36 * of the control frame.
37 */
38enum SPDY_CONTROL_FRAME_TYPES
39{
40 /**
41 * The SYN_STREAM control frame allows the sender to asynchronously
42 * create a stream between the endpoints.
43 */
44 SPDY_CONTROL_FRAME_TYPES_SYN_STREAM = 1,
45
46 /**
47 * SYN_REPLY indicates the acceptance of a stream creation by
48 * the recipient of a SYN_STREAM frame.
49 */
50 SPDY_CONTROL_FRAME_TYPES_SYN_REPLY = 2,
51
52 /**
53 * The RST_STREAM frame allows for abnormal termination of a stream.
54 * When sent by the creator of a stream, it indicates the creator
55 * wishes to cancel the stream. When sent by the recipient of a
56 * stream, it indicates an error or that the recipient did not want
57 * to accept the stream, so the stream should be closed.
58 */
59 SPDY_CONTROL_FRAME_TYPES_RST_STREAM = 3,
60
61 /**
62 * A SETTINGS frame contains a set of id/value pairs for
63 * communicating configuration data about how the two endpoints may
64 * communicate. SETTINGS frames can be sent at any time by either
65 * endpoint, are optionally sent, and are fully asynchronous. When
66 * the server is the sender, the sender can request that
67 * configuration data be persisted by the client across SPDY
68 * sessions and returned to the server in future communications.
69 */
70 SPDY_CONTROL_FRAME_TYPES_SETTINGS = 4,
71
72 /**
73 * The PING control frame is a mechanism for measuring a minimal
74 * round-trip time from the sender. It can be sent from the client
75 * or the server. Recipients of a PING frame should send an
76 * identical frame to the sender as soon as possible (if there is
77 * other pending data waiting to be sent, PING should take highest
78 * priority). Each ping sent by a sender should use a unique ID.
79 */
80 SPDY_CONTROL_FRAME_TYPES_PING = 6,
81
82 /**
83 * The GOAWAY control frame is a mechanism to tell the remote side
84 * of the connection to stop creating streams on this session. It
85 * can be sent from the client or the server.
86 */
87 SPDY_CONTROL_FRAME_TYPES_GOAWAY = 7,
88
89 /**
90 * The HEADERS frame augments a stream with additional headers. It
91 * may be optionally sent on an existing stream at any time.
92 * Specific application of the headers in this frame is
93 * application-dependent. The name/value header block within this
94 * frame is compressed.
95 */
96 SPDY_CONTROL_FRAME_TYPES_HEADERS = 8,
97
98 /**
99 * The WINDOW_UPDATE control frame is used to implement per stream
100 * flow control in SPDY. Flow control in SPDY is per hop, that is,
101 * only between the two endpoints of a SPDY connection. If there are
102 * one or more intermediaries between the client and the origin
103 * server, flow control signals are not explicitly forwarded by the
104 * intermediaries.
105 */
106 SPDY_CONTROL_FRAME_TYPES_WINDOW_UPDATE = 9,
107
108 /**
109 * The CREDENTIAL control frame is used by the client to send
110 * additional client certificates to the server. A SPDY client may
111 * decide to send requests for resources from different origins on
112 * the same SPDY session if it decides that that server handles both
113 * origins. For example if the IP address associated with both
114 * hostnames matches and the SSL server certificate presented in the
115 * initial handshake is valid for both hostnames. However, because
116 * the SSL connection can contain at most one client certificate,
117 * the client needs a mechanism to send additional client
118 * certificates to the server.
119 */
120 SPDY_CONTROL_FRAME_TYPES_CREDENTIAL = 11
121};
122
123
124/**
125 * SPDY_SESSION_STATUS is used to show the current receiving state
126 * of each session, i.e. what is expected to come now, and how it should
127 * be handled.
128 */
129enum SPDY_SESSION_STATUS
130{
131 /**
132 * The session is in closing state, do not read read anything from
133 * it. Do not write anything to it.
134 */
135 SPDY_SESSION_STATUS_CLOSING = 0,
136
137 /**
138 * Wait for new SPDY frame to come.
139 */
140 SPDY_SESSION_STATUS_WAIT_FOR_HEADER = 1,
141
142 /**
143 * The standard 8 byte header of the SPDY frame was received and
144 * handled. Wait for the specific (sub)headers according to the
145 * frame type.
146 */
147 SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER = 2,
148
149 /**
150 * The specific (sub)headers were received and handled. Wait for the
151 * "body", i.e. wait for the name/value pairs compressed by zlib.
152 */
153 SPDY_SESSION_STATUS_WAIT_FOR_BODY = 3,
154
155 /**
156 * Ignore all the bytes read from the socket, e.g. larger frames.
157 */
158 SPDY_SESSION_STATUS_IGNORE_BYTES= 4,
159
160 /**
161 * The session is in pre-closing state, do not read read anything
162 * from it. In this state the output queue will be written to the
163 * socket.
164 */
165 SPDY_SESSION_STATUS_FLUSHING = 5,
166};
167
168
169/**
170 * Specific flags for the SYN_STREAM control frame.
171 */
172enum SPDY_SYN_STREAM_FLAG
173{
174 /**
175 * The sender won't send any more frames on this stream.
176 */
177 SPDY_SYN_STREAM_FLAG_FIN = 1,
178
179 /**
180 * The sender creates this stream as unidirectional.
181 */
182 SPDY_SYN_STREAM_FLAG_UNIDIRECTIONAL = 2
183};
184
185
186/**
187 * Specific flags for the SYN_REPLY control frame.
188 */
189enum SPDY_SYN_REPLY_FLAG
190{
191 /**
192 * The sender won't send any more frames on this stream.
193 */
194 SPDY_SYN_REPLY_FLAG_FIN = 1
195};
196
197
198/**
199 * Specific flags for the data frame.
200 */
201enum SPDY_DATA_FLAG
202{
203 /**
204 * The sender won't send any more frames on this stream.
205 */
206 SPDY_DATA_FLAG_FIN = 1,
207
208 /**
209 * The data in the frame is compressed.
210 * This flag appears only in the draft on ietf.org but not on
211 * chromium.org.
212 */
213 SPDY_DATA_FLAG_COMPRESS = 2
214};
215
216/**
217 * Status code within RST_STREAM control frame.
218 */
219enum SPDY_RST_STREAM_STATUS
220{
221 /**
222 * This is a generic error, and should only be used if a more
223 * specific error is not available.
224 */
225 SPDY_RST_STREAM_STATUS_PROTOCOL_ERROR = 1,
226
227 /**
228 * This is returned when a frame is received for a stream which is
229 * not active.
230 */
231 SPDY_RST_STREAM_STATUS_INVALID_STREAM = 2,
232
233 /**
234 * Indicates that the stream was refused before any processing has
235 * been done on the stream.
236 */
237 SPDY_RST_STREAM_STATUS_REFUSED_STREAM = 3,
238
239 /**
240 * Indicates that the recipient of a stream does not support the
241 * SPDY version requested.
242 */
243 SPDY_RST_STREAM_STATUS_UNSUPPORTED_VERSION = 4,
244
245 /**
246 * Used by the creator of a stream to indicate that the stream is
247 * no longer needed.
248 */
249 SPDY_RST_STREAM_STATUS_CANCEL = 5,
250
251 /**
252 * This is a generic error which can be used when the implementation
253 * has internally failed, not due to anything in the protocol.
254 */
255 SPDY_RST_STREAM_STATUS_INTERNAL_ERROR = 6,
256
257 /**
258 * The endpoint detected that its peer violated the flow control
259 * protocol.
260 */
261 SPDY_RST_STREAM_STATUS_FLOW_CONTROL_ERROR = 7,
262
263 /**
264 * The endpoint received a SYN_REPLY for a stream already open.
265 */
266 SPDY_RST_STREAM_STATUS_STREAM_IN_USE = 8,
267
268 /**
269 * The endpoint received a data or SYN_REPLY frame for a stream
270 * which is half closed.
271 */
272 SPDY_RST_STREAM_STATUS_STREAM_ALREADY_CLOSED = 9,
273
274 /**
275 * The server received a request for a resource whose origin does
276 * not have valid credentials in the client certificate vector.
277 */
278 SPDY_RST_STREAM_STATUS_INVALID_CREDENTIALS = 10,
279
280 /**
281 * The endpoint received a frame which this implementation could not
282 * support. If FRAME_TOO_LARGE is sent for a SYN_STREAM, HEADERS,
283 * or SYN_REPLY frame without fully processing the compressed
284 * portion of those frames, then the compression state will be
285 * out-of-sync with the other endpoint. In this case, senders of
286 * FRAME_TOO_LARGE MUST close the session.
287 */
288 SPDY_RST_STREAM_STATUS_FRAME_TOO_LARGE = 11
289};
290
291
292/**
293 * Status code within GOAWAY control frame.
294 */
295enum SPDY_GOAWAY_STATUS
296{
297 /**
298 * This is a normal session teardown.
299 */
300 SPDY_GOAWAY_STATUS_OK = 0,
301
302 /**
303 * This is a generic error, and should only be used if a more
304 * specific error is not available.
305 */
306 SPDY_GOAWAY_STATUS_PROTOCOL_ERROR = 1,
307
308 /**
309 * This is a generic error which can be used when the implementation
310 * has internally failed, not due to anything in the protocol.
311 */
312 SPDY_GOAWAY_STATUS_INTERNAL_ERROR = 11
313};
314
315
316struct SPDYF_Stream;
317
318struct SPDYF_Response_Queue;
319
320/**
321 * Callback for new stream. To be used in the application layer of the
322 * lib.
323 *
324 * @param cls
325 * @param stream the new stream
326 * @return SPDY_YES on success,
327 * SPDY_NO if error occurs
328 */
329typedef int
330(*SPDYF_NewStreamCallback) (void *cls,
331 struct SPDYF_Stream * stream);
332
333
334/**
335 * Callback to be called when the response queue object was handled and
336 * the data was already sent.
337 *
338 * @param cls
339 * @param response_queue the SPDYF_Response_Queue structure which will
340 * be cleaned very soon
341 * @param status shows if actually the response was sent or it was
342 * discarded by the lib for any reason (e.g., closing session,
343 * closing stream, stopping daemon, etc.). It is possible that
344 * status indicates an error but part of the response (in one
345 * or several frames) was sent to the client.
346 */
347typedef void
348(*SPDYF_ResponseQueueResultCallback) (void * cls,
349 struct SPDYF_Response_Queue *response_queue,
350 enum SPDY_RESPONSE_RESULT status);
351
352
353/**
354 * Representation of the control frame's headers, which are common for
355 * all types.
356 */
357struct __attribute__((__packed__)) SPDYF_Control_Frame
358{
359 uint16_t version : 15;
360 uint16_t control_bit : 1; /* always 1 for control frames */
361 uint16_t type;
362 uint32_t flags : 8;
363 uint32_t length : 24;
364};
365
366
367/**
368 * Representation of the data frame's headers.
369 */
370struct __attribute__((__packed__)) SPDYF_Data_Frame
371{
372 uint32_t stream_id : 31;
373 uint32_t control_bit : 1; /* always 0 for data frames */
374 uint32_t flags : 8;
375 uint32_t length : 24;
376};
377
378
379/**
380 * Queue of the responses, to be handled (e.g. compressed) and sent later.
381 */
382struct SPDYF_Response_Queue
383{
384 /**
385 * This is a doubly-linked list.
386 */
387 struct SPDYF_Response_Queue *next;
388
389 /**
390 * This is a doubly-linked list.
391 */
392 struct SPDYF_Response_Queue *prev;
393
394 /**
395 * Stream (Request) for which is the response.
396 */
397 struct SPDYF_Stream *stream;
398
399 /**
400 * Response structure with all the data (uncompressed headers) to be sent.
401 */
402 struct SPDY_Response *response;
403
404 /**
405 * Control frame. The length field should be set after compressing
406 * the headers!
407 */
408 struct SPDYF_Control_Frame *control_frame;
409
410 /**
411 * Data frame. The length field should be set after compressing
412 * the body!
413 */
414 struct SPDYF_Data_Frame *data_frame;
415
416 /**
417 * Data to be sent: name/value pairs in control frames or body in data frames.
418 */
419 void *data;
420
421 /**
422 * Specific handler for different frame types.
423 */
424 int (* process_response_handler)(struct SPDY_Session *session);
425
426 /**
427 * Callback to be called when the last bytes from the response was sent
428 * to the client.
429 */
430 SPDYF_ResponseQueueResultCallback frqcb;
431
432 /**
433 * Closure for frqcb.
434 */
435 void *frqcb_cls;
436
437 /**
438 * Callback to be used by the application layer.
439 */
440 SPDY_ResponseResultCallback rrcb;
441
442 /**
443 * Closure for rcb.
444 */
445 void *rrcb_cls;
446
447 /**
448 * Data size.
449 */
450 size_t data_size;
451
452 /**
453 * True if data frame should be sent. False if control frame should
454 * be sent.
455 */
456 bool is_data;
457};
458
459
460
461/**
462 * Collection of HTTP headers used in requests and responses.
463 */
464struct SPDY_NameValue
465{
466 /**
467 * This is a doubly-linked list.
468 */
469 struct SPDY_NameValue *next;
470
471 /**
472 * This is a doubly-linked list.
473 */
474 struct SPDY_NameValue *prev;
475
476 /**
477 * Null terminated string for name.
478 */
479 char *name;
480
481 /**
482 * Array of Null terminated strings for value. num_values is the
483 * length of the array.
484 */
485 char **value;
486
487 /**
488 * Number of values, this is >= 0.
489 */
490 uint num_values;
491};
492
493
494/**
495 * Represents a SPDY stream
496 */
497struct SPDYF_Stream
498{
499 /**
500 * This is a doubly-linked list.
501 */
502 struct SPDYF_Stream *next;
503
504 /**
505 * This is a doubly-linked list.
506 */
507 struct SPDYF_Stream *prev;
508
509 /**
510 * Reference to the SPDY_Session struct.
511 */
512 struct SPDY_Session *session;
513
514 /**
515 * Name value pairs, sent within the frame which created the stream.
516 */
517 struct SPDY_NameValue *headers;
518
519 /**
520 * This stream's ID.
521 */
522 uint32_t stream_id;
523
524 /**
525 * Stream to which this one is associated.
526 */
527 uint32_t assoc_stream_id;
528
529 /**
530 * Stream priority. 0 is the highest, 7 is the lowest.
531 */
532 uint8_t priority;
533
534 /**
535 * Integer specifying the index in the server's CREDENTIAL vector of
536 * the client certificate to be used for this request The value 0
537 * means no client certificate should be associated with this stream.
538 */
539 uint8_t slot;
540
541 /**
542 * If initially the stream was created as unidirectional.
543 */
544 bool flag_unidirectional;
545
546 /**
547 * If the stream won't be used for receiving frames anymore. The
548 * client has sent FLAG_FIN or the stream was terminated with
549 * RST_STREAM.
550 */
551 bool is_in_closed;
552
553 /**
554 * If the stream won't be used for sending out frames anymore. The
555 * server has sent FLAG_FIN or the stream was terminated with
556 * RST_STREAM.
557 */
558 bool is_out_closed;
559
560 /**
561 * Which entity (server/client) has created the stream.
562 */
563 bool is_server_initiator;
564};
565
566
567/**
568 * Represents a SPDY session which is just a TCP connection
569 */
570struct SPDY_Session
571{
572 /**
573 * zlib stream for decompressing all the name/pair values from the
574 * received frames. All the received compressed data must be
575 * decompressed within one context: this stream. Thus, it should be
576 * unique for the session and initialized at its creation.
577 */
578 z_stream zlib_recv_stream;
579
580 /**
581 * zlib stream for compressing all the name/pair values from the
582 * frames to be sent. All the sent compressed data must be
583 * compressed within one context: this stream. Thus, it should be
584 * unique for the session and initialized at its creation.
585 */
586 z_stream zlib_send_stream;
587
588 /**
589 * This is a doubly-linked list.
590 */
591 struct SPDY_Session *next;
592
593 /**
594 * This is a doubly-linked list.
595 */
596 struct SPDY_Session *prev;
597
598 /**
599 * Reference to the SPDY_Daemon struct.
600 */
601 struct SPDY_Daemon *daemon;
602
603 /**
604 * Foreign address (of length addr_len).
605 */
606 struct sockaddr *addr;
607
608 /**
609 * Head of doubly-linked list of the SPDY streams belonging to the
610 * session.
611 */
612 struct SPDYF_Stream *streams_head;
613
614 /**
615 * Tail of doubly-linked list of the streams.
616 */
617 struct SPDYF_Stream *streams_tail;
618
619 /**
620 * Unique TLS context for the session. Initialized on each creation
621 * (actually when the TCP connection is established).
622 */
623 SPDYF_TLS_SESSION_CONTEXT *tls_context;
624
625 /**
626 * Head of doubly-linked list of the responses.
627 */
628 struct SPDYF_Response_Queue *response_queue_head;
629
630 /**
631 * Tail of doubly-linked list of the responses.
632 */
633 struct SPDYF_Response_Queue *response_queue_tail;
634
635 /**
636 * Buffer for reading requests.
637 */
638 void *read_buffer;
639
640 /**
641 * Buffer for writing responses.
642 */
643 void *write_buffer;
644
645 /**
646 * Specific handler for the frame that is currently being received.
647 */
648 void (*frame_handler) (struct SPDY_Session * session);
649
650 /**
651 * Closure for frame_handler.
652 */
653 void *frame_handler_cls;
654
655 /**
656 * Extra field to be used by the user with set/get func for whatever
657 * purpose he wants.
658 */
659 void *user_cls;
660
661 /**
662 * Number of bytes that the lib must ignore immediately after they
663 * are read from the TLS socket without adding them to the read buf.
664 * This is needed, for instance, when receiving frame bigger than
665 * the buffer to avoid deadlock situations.
666 */
667 size_t read_ignore_bytes;
668
669 /**
670 * Size of read_buffer (in bytes). This value indicates
671 * how many bytes we're willing to read into the buffer;
672 * the real buffer is one byte longer to allow for
673 * adding zero-termination (when needed).
674 */
675 size_t read_buffer_size;
676
677 /**
678 * Position where we currently append data in
679 * read_buffer (last valid position).
680 */
681 size_t read_buffer_offset;
682
683 /**
684 * Position until where everything was already read
685 */
686 size_t read_buffer_beginning;
687
688 /**
689 * Size of write_buffer (in bytes). This value indicates
690 * how many bytes we're willing to prepare for writing.
691 */
692 size_t write_buffer_size;
693
694 /**
695 * Position where we currently append data in
696 * write_buffer (last valid position).
697 */
698 size_t write_buffer_offset;
699
700 /**
701 * Position until where everything was already written to the socket
702 */
703 size_t write_buffer_beginning;
704
705 /**
706 * Last time this connection had any activity
707 * (reading or writing).
708 */
709 time_t last_activity;
710
711 /**
712 * Socket for this connection. Set to -1 if
713 * this connection has died (daemon should clean
714 * up in that case).
715 */
716 int socket_fd;
717
718 /**
719 * Length of the foreign address.
720 */
721 socklen_t addr_len;
722
723 /**
724 * The biggest stream ID for this session for streams initiated
725 * by the client.
726 */
727 uint32_t last_in_stream_id;
728
729 /**
730 * The biggest stream ID for this session for streams initiated
731 * by the server.
732 */
733 uint32_t last_out_stream_id;
734
735 /**
736 * This value is updated whenever SYN_REPLY or RST_STREAM are sent
737 * and is used later in GOAWAY frame.
738 * TODO it is not clear in the draft what happens when streams are
739 * not answered in the order of their IDs. Moreover, why should we
740 * send GOAWAY with the ID of received bogus SYN_STREAM with huge ID?
741 */
742 uint32_t last_replied_to_stream_id;
743
744 /**
745 * Shows the stream id of the currently handled frame. This value is
746 * to be used when sending RST_STREAM in answer to a problematic
747 * frame, e.g. larger than supported.
748 */
749 uint32_t current_stream_id;
750
751 /**
752 * Shows the current receiving state the session, i.e. what is
753 * expected to come now, and how it shold be handled.
754 */
755 enum SPDY_SESSION_STATUS status;
756
757 /**
758 * Has this socket been closed for reading (i.e.
759 * other side closed the connection)? If so,
760 * we must completely close the connection once
761 * we are done sending our response (and stop
762 * trying to read from this socket).
763 */
764 bool read_closed;
765
766 /**
767 * If the server sends GOAWAY, it must ignore all SYN_STREAMS for
768 * this session. Normally the server will soon close the TCP session.
769 */
770 bool is_goaway_sent;
771
772 /**
773 * If the server receives GOAWAY, it must not send new SYN_STREAMS
774 * on this session. Normally the client will soon close the TCP
775 * session.
776 */
777 bool is_goaway_received;
778};
779
780
781/**
782 * State and settings kept for each SPDY daemon.
783 */
784struct SPDY_Daemon
785{
786
787 /**
788 * Tail of doubly-linked list of our current, active sessions.
789 */
790 struct SPDY_Session *sessions_head;
791
792 /**
793 * Tail of doubly-linked list of our current, active sessions.
794 */
795 struct SPDY_Session *sessions_tail;
796
797 /**
798 * Tail of doubly-linked list of connections to clean up.
799 */
800 struct SPDY_Session *cleanup_head;
801
802 /**
803 * Tail of doubly-linked list of connections to clean up.
804 */
805 struct SPDY_Session *cleanup_tail;
806
807 /**
808 * Unique TLS context for the daemon. Initialized on daemon start.
809 */
810 SPDYF_TLS_DAEMON_CONTEXT *tls_context;
811
812 /**
813 * Certificate file of the server. File path is kept here.
814 */
815 char *certfile;
816
817 /**
818 * Key file for the certificate of the server. File path is
819 * kept here.
820 */
821 char *keyfile;
822
823
824 /**
825 * The address to which the listening socket is bound.
826 */
827 struct sockaddr *address;
828
829 /**
830 * Callback called when a new SPDY session is
831 * established by a client
832 */
833 SPDY_NewSessionCallback new_session_cb;
834
835 /**
836 * Callback called when a client closes the session
837 */
838 SPDY_SessionClosedCallback session_closed_cb;
839
840 /**
841 * Callback called when a client sends request
842 */
843 SPDY_NewRequestCallback new_request_cb;
844
845 /**
846 * Callback called when HTTP POST params are received
847 * after request
848 */
849 SPDY_NewPOSTDataCallback new_post_data_cb;
850
851 /**
852 * Closure argument for all the callbacks that can be used by the client.
853 */
854 void *cls;
855
856 /**
857 * Callback called when new stream is created.
858 */
859 SPDYF_NewStreamCallback fnew_stream_cb;
860
861 /**
862 * Closure argument for all the callbacks defined in the framing layer.
863 */
864 void *fcls;
865
866 /**
867 * After how many seconds of inactivity should
868 * connections time out? Zero for no timeout.
869 */
870 time_t session_timeout;
871
872 /**
873 * Listen socket.
874 */
875 int socket_fd;
876
877 /**
878 * Daemon's options.
879 */
880 enum SPDY_DAEMON_OPTION options;
881
882 /**
883 * Daemon's flags.
884 */
885 enum SPDY_DAEMON_FLAG flags;
886
887 /**
888 * Listen port.
889 */
890 uint16_t port;
891};
892
893
894/**
895 * Represents a SPDY response.
896 */
897struct SPDY_Response
898{
899 /**
900 * Raw uncompressed stream of the name/value pairs in SPDY frame
901 * used for the HTTP headers.
902 */
903 void *headers;
904
905 /**
906 * Raw stream of the data to be sent. Equivalent to the body in HTTP
907 * response.
908 */
909 void *data;
910
911 /**
912 * Callback function to be used when the response data is provided
913 * with callbacks. In this case data must be NULL and data_size must
914 * be 0.
915 */
916 SPDY_ResponseCallback rcb;
917
918 /**
919 * Extra argument to rcb.
920 */
921 void *rcb_cls;
922
923 /**
924 * Length of headers.
925 */
926 size_t headers_size;
927
928 /**
929 * Length of data.
930 */
931 size_t data_size;
932
933 /**
934 * The callback func will be called to get that amount of bytes to
935 * put them into a DATA frame. It is either user preffered or
936 * the maximum supported by the lib value.
937 */
938 uint32_t rcb_block_size;
939};
940
941
942/* Macros for handling data and structures */
943
944
945/**
946 * Insert an element at the head of a DLL. Assumes that head, tail and
947 * element are structs with prev and next fields.
948 *
949 * @param head pointer to the head of the DLL (struct ? *)
950 * @param tail pointer to the tail of the DLL (struct ? *)
951 * @param element element to insert (struct ? *)
952 */
953#define DLL_insert(head,tail,element) do { \
954 (element)->next = (head); \
955 (element)->prev = NULL; \
956 if ((tail) == NULL) \
957 (tail) = element; \
958 else \
959 (head)->prev = element; \
960 (head) = (element); } while (0)
961
962
963/**
964 * Remove an element from a DLL. Assumes
965 * that head, tail and element are structs
966 * with prev and next fields.
967 *
968 * @param head pointer to the head of the DLL (struct ? *)
969 * @param tail pointer to the tail of the DLL (struct ? *)
970 * @param element element to remove (struct ? *)
971 */
972#define DLL_remove(head,tail,element) do { \
973 if ((element)->prev == NULL) \
974 (head) = (element)->next; \
975 else \
976 (element)->prev->next = (element)->next; \
977 if ((element)->next == NULL) \
978 (tail) = (element)->prev; \
979 else \
980 (element)->next->prev = (element)->prev; \
981 (element)->next = NULL; \
982 (element)->prev = NULL; } while (0)
983
984
985/**
986 * Convert all integers in a SPDY control frame headers structure from
987 * host byte order to network byte order.
988 *
989 * @param frame input and output structure (struct SPDY_Control_Frame *)
990 */
991#if HAVE_BIG_ENDIAN
992#define SPDYF_CONTROL_FRAME_HTON(frame)
993#else
994#define SPDYF_CONTROL_FRAME_HTON(frame) do { \
995 (*((uint16_t *) frame )) = (*((uint8_t *) (frame) +1 )) | ((*((uint8_t *) frame ))<<8);\
996 (frame)->type = htons((frame)->type); \
997 (frame)->length = HTON24((frame)->length); \
998 } while (0)
999#endif
1000
1001
1002/**
1003 * Convert all integers in a SPDY control frame headers structure from
1004 * network byte order to host byte order.
1005 *
1006 * @param frame input and output structure (struct SPDY_Control_Frame *)
1007 */
1008#if HAVE_BIG_ENDIAN
1009#define SPDYF_CONTROL_FRAME_NTOH(frame)
1010#else
1011#define SPDYF_CONTROL_FRAME_NTOH(frame) do { \
1012 (*((uint16_t *) frame )) = (*((uint8_t *) (frame) +1 )) | ((*((uint8_t *) frame ))<<8);\
1013 (frame)->type = ntohs((frame)->type); \
1014 (frame)->length = NTOH24((frame)->length); \
1015 } while (0)
1016#endif
1017
1018
1019/**
1020 * Convert all integers in a SPDY data frame headers structure from
1021 * host byte order to network byte order.
1022 *
1023 * @param frame input and output structure (struct SPDY_Data_Frame *)
1024 */
1025#if HAVE_BIG_ENDIAN
1026#define SPDYF_DATA_FRAME_HTON(frame)
1027#else
1028#define SPDYF_DATA_FRAME_HTON(frame) do { \
1029 *((uint32_t *) frame ) = htonl(*((uint32_t *) frame ));\
1030 (frame)->length = HTON24((frame)->length); \
1031 } while (0)
1032#endif
1033
1034
1035/**
1036 * Convert all integers in a SPDY data frame headers structure from
1037 * network byte order to host byte order.
1038 *
1039 * @param frame input and output structure (struct SPDY_Data_Frame *)
1040 */
1041#if HAVE_BIG_ENDIAN
1042#define SPDYF_DATA_FRAME_NTOH(frame)
1043#else
1044#define SPDYF_DATA_FRAME_NTOH(frame) do { \
1045 *((uint32_t *) frame ) = ntohl(*((uint32_t *) frame ));\
1046 (frame)->length = NTOH24((frame)->length); \
1047 } while (0)
1048#endif
1049
1050
1051/**
1052 * Creates one or more new SPDYF_Response_Queue object to be put on the
1053 * response queue.
1054 *
1055 * @param is_data whether new data frame or new control frame will be
1056 * crerated
1057 * @param data the row stream which will be used as the body of the frame
1058 * @param data_size length of data
1059 * @param response object, part of which is the frame
1060 * @param stream on which data is to be sent
1061 * @param closestream TRUE if the frame must close the stream (with flag)
1062 * @param frqcb callback to notify application layer when the frame
1063 * has been sent or discarded
1064 * @param frqcb_cls closure for frqcb
1065 * @param rrcb callback used by the application layer to notify the
1066 * application when the frame has been sent or discarded.
1067 * frqcb will call it
1068 * @param rrcb_cls closure for rrcb
1069 * @return double linked list of SPDYF_Response_Queue structures: one or
1070 * more frames are returned based on the size of the data
1071 */
1072struct SPDYF_Response_Queue *
1073SPDYF_response_queue_create(bool is_data,
1074 void *data,
1075 size_t data_size,
1076 struct SPDY_Response *response,
1077 struct SPDYF_Stream *stream,
1078 bool closestream,
1079 SPDYF_ResponseQueueResultCallback frqcb,
1080 void *frqcb_cls,
1081 SPDY_ResponseResultCallback rrcb,
1082 void *rrcb_cls);
1083
1084
1085/**
1086 * Destroys SPDYF_Response_Queue structure and whatever is in it.
1087 *
1088 * @param response_queue to destroy
1089 */
1090void
1091SPDYF_response_queue_destroy(struct SPDYF_Response_Queue *response_queue);
1092
1093
1094/**
1095 * Transforms raw binary decomressed stream of headers
1096 * into SPDY_NameValue, containing all of the headers and values.
1097 *
1098 * @param stream that is to be transformed
1099 * @param size length of the stream
1100 * @param container will contain the newly created SPDY_NameValue
1101 * container. Should point to NULL.
1102 * @return SPDY_YES on success
1103 * SPDY_NO on memory error
1104 * SPDY_INPUT_ERROR if the provided stream is not valid
1105 */
1106int
1107SPDYF_name_value_from_stream(void *stream,
1108 size_t size,
1109 struct SPDY_NameValue ** container);
1110
1111
1112/**
1113 * Transforms array of objects of name/values tuples, containing HTTP
1114 * headers, into raw binary stream. The resulting stream is ready to
1115 * be compressed and sent.
1116 *
1117 * @param container one or more SPDY_NameValue objects. Each object
1118 * contains multiple number of name/value tuples.
1119 * @param num_containers length of the array
1120 * @param stream will contain the resulting stream. Should point to NULL.
1121 * @return length of stream or value less than 0 indicating error
1122 */
1123ssize_t
1124SPDYF_name_value_to_stream(struct SPDY_NameValue * container[],
1125 int num_containers,
1126 void **stream);
1127
1128#endif
diff --git a/src/microspdy/tls.c b/src/microspdy/tls.c
new file mode 100644
index 00000000..521f8f24
--- /dev/null
+++ b/src/microspdy/tls.c
@@ -0,0 +1,255 @@
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 tls.c
21 * @brief TLS handling using libssl. The current code assumes that
22 * blocking I/O is in use.
23 * @author Andrey Uzunov
24 */
25
26#include "platform.h"
27#include "internal.h"
28#include "session.h"
29#include "tls.h"
30
31
32/**
33 * Callback to advertise spdy ver. 3 in Next Protocol Negotiation
34 *
35 * @param ssl openssl context for a connection
36 * @param out must be set to the raw data that is advertised in NPN
37 * @param outlen must be set to size of out
38 * @param arg
39 * @return SSL_TLSEXT_ERR_OK to do advertising
40 */
41static int
42spdyf_next_protos_advertised_cb (SSL *ssl, const unsigned char **out, unsigned int *outlen, void *arg)
43{
44 (void)ssl;
45 (void)arg;
46 static unsigned char npn_spdy3[] = {0x06, // length of "spdy/3"
47 0x73,0x70,0x64,0x79,0x2f,0x33};// spdy/3
48
49 *out = npn_spdy3;
50 *outlen = 7; // total length of npn_spdy3
51 return SSL_TLSEXT_ERR_OK;
52}
53
54
55void
56SPDYF_tls_global_init()
57{
58 //error strings are now not used by the lib
59 //SSL_load_error_strings();
60 //init libssl
61 SSL_library_init(); //always returns 1
62 //the table for looking up algos is not used now by the lib
63 //OpenSSL_add_all_algorithms();
64}
65
66
67void
68SPDYF_tls_global_deinit()
69{
70 //if SSL_load_error_strings was called
71 //ERR_free_strings();
72 //if OpenSSL_add_all_algorithms was called
73 //EVP_cleanup();
74}
75
76
77int
78SPDYF_tls_init(struct SPDY_Daemon *daemon)
79{
80 //create ssl context. TLSv1 used
81 if(NULL == (daemon->tls_context = SSL_CTX_new(TLSv1_server_method())))
82 {
83 SPDYF_DEBUG("Couldn't create ssl context");
84 return SPDY_NO;
85 }
86 //set options for tls
87 //TODO DH is not enabled for easier debugging
88 //SSL_CTX_set_options(daemon->tls_context, SSL_OP_SINGLE_DH_USE);
89
90 //TODO here session tickets are disabled for easier debuging with
91 //wireshark when using Chrome
92 //SSL_OP_NO_COMPRESSION disables TLS compression to avoid CRIME attack
93 SSL_CTX_set_options(daemon->tls_context, SSL_OP_NO_TICKET | SSL_OP_NO_COMPRESSION);
94 if(1 != SSL_CTX_use_certificate_file(daemon->tls_context, daemon->certfile , SSL_FILETYPE_PEM))
95 {
96 SPDYF_DEBUG("Couldn't load the cert file");
97 SSL_CTX_free(daemon->tls_context);
98 return SPDY_NO;
99 }
100 if(1 != SSL_CTX_use_PrivateKey_file(daemon->tls_context, daemon->keyfile, SSL_FILETYPE_PEM))
101 {
102 SPDYF_DEBUG("Couldn't load the name file");
103 SSL_CTX_free(daemon->tls_context);
104 return SPDY_NO;
105 }
106 SSL_CTX_set_next_protos_advertised_cb(daemon->tls_context, &spdyf_next_protos_advertised_cb, NULL);
107 //TODO only RC4-SHA is used to make it easy to debug with wireshark
108 if (1 != SSL_CTX_set_cipher_list(daemon->tls_context, "RC4-SHA"))
109 {
110 SPDYF_DEBUG("Couldn't set the desired cipher list");
111 SSL_CTX_free(daemon->tls_context);
112 return SPDY_NO;
113 }
114
115 return SPDY_YES;
116}
117
118
119void
120SPDYF_tls_deinit(struct SPDY_Daemon *daemon)
121{
122 SSL_CTX_free(daemon->tls_context);
123}
124
125
126int
127SPDYF_tls_new_session(struct SPDY_Session *session)
128{
129 int ret;
130
131 if(NULL == (session->tls_context = SSL_new(session->daemon->tls_context)))
132 {
133 SPDYF_DEBUG("Couldn't create ssl structure");
134 return SPDY_NO;
135 }
136 if(1 != (ret = SSL_set_fd(session->tls_context, session->socket_fd)))
137 {
138 SPDYF_DEBUG("SSL_set_fd %i",ret);
139 SSL_free(session->tls_context);
140 session->tls_context = NULL;
141 return SPDY_NO;
142 }
143
144 //for non-blocking I/O SSL_accept may return -1
145 //and this function won't work
146 if(1 != (ret = SSL_accept(session->tls_context)))
147 {
148 SPDYF_DEBUG("SSL_accept %i",ret);
149 SSL_free(session->tls_context);
150 session->tls_context = NULL;
151 return SPDY_NO;
152 }
153 /* alternatively
154 SSL_set_accept_state(session->tls_context);
155 * may be called and then the negotiation will be done on reading
156 */
157
158 return SPDY_YES;
159}
160
161
162void
163SPDYF_tls_close_session(struct SPDY_Session *session)
164{
165 //SSL_shutdown sends TLS "close notify" as in TLS standard.
166 //The function may fail as it waits for the other party to also close
167 //the TLS session. The lib just sends it and will close the socket
168 //after that because the browsers don't seem to care much about
169 //"close notify"
170 SSL_shutdown(session->tls_context);
171
172 SSL_free(session->tls_context);
173}
174
175
176int
177SPDYF_tls_recv(struct SPDY_Session *session,
178 void * buffer,
179 size_t size)
180{
181 int ret;
182 int n = SSL_read(session->tls_context,
183 buffer,
184 size);
185 //if(n > 0) SPDYF_DEBUG("recvd: %i",n);
186 if (n <= 0)
187 {
188 ret = SSL_get_error(session->tls_context, n);
189 switch(ret)
190 {
191 case SSL_ERROR_ZERO_RETURN:
192 return 0;
193
194 case SSL_ERROR_WANT_READ:
195 case SSL_ERROR_WANT_WRITE:
196 return SPDY_TLS_ERROR_AGAIN;
197
198 case SSL_ERROR_SYSCALL:
199 if(EINTR == errno)
200 return SPDY_TLS_ERROR_AGAIN;
201
202 default:
203 return SPDY_TLS_ERROR_ERROR;
204 }
205 }
206
207 return n;
208}
209
210
211int
212SPDYF_tls_send(struct SPDY_Session *session,
213 const void * buffer,
214 size_t size)
215{
216 int ret;
217
218 int n = SSL_write(session->tls_context,
219 buffer,
220 size);
221 //if(n > 0) SPDYF_DEBUG("sent: %i",n);
222 if (n <= 0)
223 {
224 ret = SSL_get_error(session->tls_context, n);
225 switch(ret)
226 {
227 case SSL_ERROR_ZERO_RETURN:
228 return 0;
229
230 case SSL_ERROR_WANT_READ:
231 case SSL_ERROR_WANT_WRITE:
232 return SPDY_TLS_ERROR_AGAIN;
233
234 case SSL_ERROR_SYSCALL:
235 if(EINTR == errno)
236 return SPDY_TLS_ERROR_AGAIN;
237
238 default:
239 return SPDY_TLS_ERROR_ERROR;
240 }
241 }
242
243 return n;
244}
245
246
247int
248SPDYF_tls_is_pending(struct SPDY_Session *session)
249{
250 /* From openssl docs:
251 * BUGS
252SSL_pending() takes into account only bytes from the TLS/SSL record that is currently being processed (if any). If the SSL object's read_ahead flag is set, additional protocol bytes may have been read containing more TLS/SSL records; these are ignored by SSL_pending().
253 */
254 return SSL_pending(session->tls_context) > 0 ? SPDY_YES : SPDY_NO;
255}
diff --git a/src/microspdy/tls.h b/src/microspdy/tls.h
new file mode 100644
index 00000000..5fb4371a
--- /dev/null
+++ b/src/microspdy/tls.h
@@ -0,0 +1,171 @@
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 tls.h
21 * @brief TLS handling. openssl with NPN is used, but as long as the
22 * functions conform to this interface file, other libraries
23 * can be used.
24 * @author Andrey Uzunov
25 */
26
27#ifndef TLS_H
28#define TLS_H
29
30#include "platform.h"
31#include <openssl/err.h>
32#include <openssl/ssl.h>
33#include <openssl/rand.h>
34
35/* macros used in other files instead of types.
36 * useful in case of changing openssl to something else */
37#define SPDYF_TLS_SESSION_CONTEXT SSL
38#define SPDYF_TLS_DAEMON_CONTEXT SSL_CTX
39
40
41/**
42 * Used for return code when reading and writing to the TLS socket.
43 */
44enum SPDY_TLS_ERROR
45{
46 /**
47 * The connection was closed by the other party.
48 */
49 SPDY_TLS_ERROR_CLOSED = 0,
50
51 /**
52 * Any kind of error ocurred. The session has to be closed.
53 */
54 SPDY_TLS_ERROR_ERROR = -2,
55
56 /**
57 * The function had to return without processing any data. The whole
58 * cycle of events has to be called again (SPDY_run) as something
59 * either has to be written or read or the the syscall was
60 * interrupted by a signal.
61 */
62 SPDY_TLS_ERROR_AGAIN = -3,
63};
64
65
66/**
67 * Global initializing of openssl. Must be called only once in the program.
68 *
69 */
70void
71SPDYF_tls_global_init();
72
73
74/**
75 * Global deinitializing of openssl for the whole program. Should be called
76 * at the end of the program.
77 *
78 */
79void
80SPDYF_tls_global_deinit();
81
82
83/**
84 * Initializing of openssl for a specific daemon.
85 * Must be called when the daemon starts.
86 *
87 * @param daemon SPDY_Daemon for which openssl will be used. Daemon's
88 * certificate and key file are used.
89 * @return SPDY_YES on success or SPDY_NO on error
90 */
91int
92SPDYF_tls_init(struct SPDY_Daemon *daemon);
93
94
95/**
96 * Deinitializing openssl for a daemon. Should be called
97 * when the deamon is stopped.
98 *
99 * @param daemon SPDY_Daemon which is being stopped
100 */
101void
102SPDYF_tls_deinit(struct SPDY_Daemon *daemon);
103
104
105/**
106 * Initializing openssl for a specific connection. Must be called
107 * after the connection has been accepted.
108 *
109 * @param session SPDY_Session whose socket will be used by openssl
110 * @return SPDY_NO if some openssl funcs fail. SPDY_YES otherwise
111 */
112int
113SPDYF_tls_new_session(struct SPDY_Session *session);
114
115
116/**
117 * Deinitializing openssl for a specific connection. Should be called
118 * closing session's socket.
119 *
120 * @param session SPDY_Session whose socket is used by openssl
121 */
122void
123SPDYF_tls_close_session(struct SPDY_Session *session);
124
125
126/**
127 * Reading from a TLS socket. Reads available data and put it to the
128 * buffer.
129 *
130 * @param session for which data is received
131 * @param buffer where data from the socket will be written to
132 * @param size of the buffer
133 * @return number of bytes (at most size) read from the TLS connection
134 * 0 if the other party has closed the connection
135 * SPDY_TLS_ERROR code on error
136 */
137int
138SPDYF_tls_recv(struct SPDY_Session *session,
139 void * buffer,
140 size_t size);
141
142
143/**
144 * Writing to a TLS socket. Writes the data given into the buffer to the
145 * TLS socket.
146 *
147 * @param session whose context is used
148 * @param buffer from where data will be written to the socket
149 * @param size number of bytes to be taken from the buffer
150 * @return number of bytes (at most size) from the buffer that has been
151 * written to the TLS connection
152 * 0 if the other party has closed the connection
153 * SPDY_TLS_ERROR code on error
154 */
155int
156SPDYF_tls_send(struct SPDY_Session *session,
157 const void * buffer,
158 size_t size);
159
160
161/**
162 * Checks if there is data staying in the buffers of the underlying
163 * system that waits to be read.
164 *
165 * @param session which is checked
166 * @return SPDY_YES if data is pending or SPDY_NO otherwise
167 */
168int
169SPDYF_tls_is_pending(struct SPDY_Session *session);
170
171#endif