aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am16
-rw-r--r--src/datadir/spdy-draft.txt2856
-rw-r--r--src/examples/Makefile.am40
-rw-r--r--src/examples/mhd2spdy.c322
-rw-r--r--src/examples/mhd2spdy_http.c422
-rw-r--r--src/examples/mhd2spdy_http.h54
-rw-r--r--src/examples/mhd2spdy_spdy.c1150
-rw-r--r--src/examples/mhd2spdy_spdy.h102
-rw-r--r--src/examples/mhd2spdy_structures.c162
-rw-r--r--src/examples/mhd2spdy_structures.h296
-rw-r--r--src/examples/spdy_event_loop.c487
-rw-r--r--src/examples/spdy_fileserver.c353
-rw-r--r--src/examples/spdy_response_with_callback.c236
-rw-r--r--src/include/Makefile.am6
-rw-r--r--src/include/microspdy.h1380
-rw-r--r--src/microspdy/Makefile.am40
-rw-r--r--src/microspdy/alstructures.c41
-rw-r--r--src/microspdy/alstructures.h79
-rw-r--r--src/microspdy/applicationlayer.c748
-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.c544
-rw-r--r--src/microspdy/daemon.h130
-rw-r--r--src/microspdy/internal.c40
-rw-r--r--src/microspdy/internal.h199
-rw-r--r--src/microspdy/io.c90
-rw-r--r--src/microspdy/io.h216
-rw-r--r--src/microspdy/io_openssl.c280
-rw-r--r--src/microspdy/io_openssl.h166
-rw-r--r--src/microspdy/io_raw.c194
-rw-r--r--src/microspdy/io_raw.h158
-rw-r--r--src/microspdy/session.c1769
-rw-r--r--src/microspdy/session.h281
-rw-r--r--src/microspdy/stream.c169
-rw-r--r--src/microspdy/stream.h76
-rw-r--r--src/microspdy/structures.c638
-rw-r--r--src/microspdy/structures.h1246
-rw-r--r--src/spdy2http/Makefile.am29
-rw-r--r--src/spdy2http/proxy.c1411
-rw-r--r--src/testspdy/Makefile.am115
-rw-r--r--src/testspdy/common.c59
-rw-r--r--src/testspdy/common.h38
-rw-r--r--src/testspdy/test_daemon_start_stop.c49
-rw-r--r--src/testspdy/test_daemon_start_stop_many.c66
-rw-r--r--src/testspdy/test_misc.c289
-rw-r--r--src/testspdy/test_new_connection.c1012
-rw-r--r--src/testspdy/test_notls.c973
-rw-r--r--src/testspdy/test_proxies.c255
-rw-r--r--src/testspdy/test_request_response.c1029
-rw-r--r--src/testspdy/test_request_response_with_callback.c320
-rw-r--r--src/testspdy/test_requests_with_assets.c302
-rw-r--r--src/testspdy/test_session_timeout.c338
-rw-r--r--src/testspdy/test_struct_namevalue.c346
54 files changed, 4 insertions, 22202 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index d496c95d..51263449 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -7,19 +7,8 @@ zzuftests = testzzuf
7endif 7endif
8endif 8endif
9endif 9endif
10if ENABLE_SPDY
11if HAVE_OPENSSL
12microspdy = microspdy
13if HAVE_CURL
14microspdy += spdy2http
15endif
16#if HAVE_SPDYLAY
17microspdy += testspdy
18#endif
19endif
20endif
21 10
22SUBDIRS = include platform microhttpd $(microspdy) $(curltests) $(zzuftests) . 11SUBDIRS = include platform microhttpd $(curltests) $(zzuftests) .
23 12
24if BUILD_EXAMPLES 13if BUILD_EXAMPLES
25SUBDIRS += examples 14SUBDIRS += examples
@@ -27,5 +16,4 @@ endif
27 16
28EXTRA_DIST = \ 17EXTRA_DIST = \
29 datadir/cert-and-key.pem \ 18 datadir/cert-and-key.pem \
30 datadir/cert-and-key-for-wireshark.pem \ 19 datadir/cert-and-key-for-wireshark.pem
31 datadir/spdy-draft.txt
diff --git a/src/datadir/spdy-draft.txt b/src/datadir/spdy-draft.txt
deleted file mode 100644
index c31648cc..00000000
--- a/src/datadir/spdy-draft.txt
+++ /dev/null
@@ -1,2856 +0,0 @@
1
2
3
4Network Working Group M. Belshe
5Internet-Draft Twist
6Expires: August 4, 2012 R. Peon
7 Google, Inc
8 Feb 2012
9
10
11 SPDY Protocol
12 draft-mbelshe-httpbis-spdy-00
13
14Abstract
15
16 This document describes SPDY, a protocol designed for low-latency
17 transport of content over the World Wide Web. SPDY introduces two
18 layers of protocol. The lower layer is a general purpose framing
19 layer which can be used atop a reliable transport (likely TCP) for
20 multiplexed, prioritized, and compressed data communication of many
21 concurrent streams. The upper layer of the protocol provides HTTP-
22 like RFC2616 [RFC2616] semantics for compatibility with existing HTTP
23 application servers.
24
25Status of this Memo
26
27 This Internet-Draft is submitted in full conformance with the
28 provisions of BCP 78 and BCP 79.
29
30 Internet-Drafts are working documents of the Internet Engineering
31 Task Force (IETF). Note that other groups may also distribute
32 working documents as Internet-Drafts. The list of current Internet-
33 Drafts is at http://datatracker.ietf.org/drafts/current/.
34
35 Internet-Drafts are draft documents valid for a maximum of six months
36 and may be updated, replaced, or obsoleted by other documents at any
37 time. It is inappropriate to use Internet-Drafts as reference
38 material or to cite them other than as "work in progress."
39
40 This Internet-Draft will expire on August 4, 2012.
41
42Copyright Notice
43
44 Copyright (c) 2012 IETF Trust and the persons identified as the
45 document authors. All rights reserved.
46
47 This document is subject to BCP 78 and the IETF Trust's Legal
48 Provisions Relating to IETF Documents
49 (http://trustee.ietf.org/license-info) in effect on the date of
50 publication of this document. Please review these documents
51 carefully, as they describe your rights and restrictions with respect
52
53
54
55Belshe & Peon Expires August 4, 2012 [Page 1]
56
57Internet-Draft SPDY Feb 2012
58
59
60 to this document. Code Components extracted from this document must
61 include Simplified BSD License text as described in Section 4.e of
62 the Trust Legal Provisions and are provided without warranty as
63 described in the Simplified BSD License.
64
65
66Table of Contents
67
68 1. Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
69 1.1. Document Organization . . . . . . . . . . . . . . . . . . 4
70 1.2. Definitions . . . . . . . . . . . . . . . . . . . . . . . 5
71 2. SPDY Framing Layer . . . . . . . . . . . . . . . . . . . . . . 6
72 2.1. Session (Connections) . . . . . . . . . . . . . . . . . . 6
73 2.2. Framing . . . . . . . . . . . . . . . . . . . . . . . . . 6
74 2.2.1. Control frames . . . . . . . . . . . . . . . . . . . . 6
75 2.2.2. Data frames . . . . . . . . . . . . . . . . . . . . . 7
76 2.3. Streams . . . . . . . . . . . . . . . . . . . . . . . . . 8
77 2.3.1. Stream frames . . . . . . . . . . . . . . . . . . . . 9
78 2.3.2. Stream creation . . . . . . . . . . . . . . . . . . . 9
79 2.3.3. Stream priority . . . . . . . . . . . . . . . . . . . 10
80 2.3.4. Stream headers . . . . . . . . . . . . . . . . . . . . 10
81 2.3.5. Stream data exchange . . . . . . . . . . . . . . . . . 10
82 2.3.6. Stream half-close . . . . . . . . . . . . . . . . . . 10
83 2.3.7. Stream close . . . . . . . . . . . . . . . . . . . . . 11
84 2.4. Error Handling . . . . . . . . . . . . . . . . . . . . . . 11
85 2.4.1. Session Error Handling . . . . . . . . . . . . . . . . 11
86 2.4.2. Stream Error Handling . . . . . . . . . . . . . . . . 12
87 2.5. Data flow . . . . . . . . . . . . . . . . . . . . . . . . 12
88 2.6. Control frame types . . . . . . . . . . . . . . . . . . . 12
89 2.6.1. SYN_STREAM . . . . . . . . . . . . . . . . . . . . . . 12
90 2.6.2. SYN_REPLY . . . . . . . . . . . . . . . . . . . . . . 14
91 2.6.3. RST_STREAM . . . . . . . . . . . . . . . . . . . . . . 15
92 2.6.4. SETTINGS . . . . . . . . . . . . . . . . . . . . . . . 16
93 2.6.5. PING . . . . . . . . . . . . . . . . . . . . . . . . . 19
94 2.6.6. GOAWAY . . . . . . . . . . . . . . . . . . . . . . . . 20
95 2.6.7. HEADERS . . . . . . . . . . . . . . . . . . . . . . . 21
96 2.6.8. WINDOW_UPDATE . . . . . . . . . . . . . . . . . . . . 22
97 2.6.9. CREDENTIAL . . . . . . . . . . . . . . . . . . . . . . 24
98 2.6.10. Name/Value Header Block . . . . . . . . . . . . . . . 26
99 3. HTTP Layering over SPDY . . . . . . . . . . . . . . . . . . . 33
100 3.1. Connection Management . . . . . . . . . . . . . . . . . . 33
101 3.1.1. Use of GOAWAY . . . . . . . . . . . . . . . . . . . . 33
102 3.2. HTTP Request/Response . . . . . . . . . . . . . . . . . . 34
103 3.2.1. Request . . . . . . . . . . . . . . . . . . . . . . . 34
104 3.2.2. Response . . . . . . . . . . . . . . . . . . . . . . . 35
105 3.2.3. Authentication . . . . . . . . . . . . . . . . . . . . 36
106 3.3. Server Push Transactions . . . . . . . . . . . . . . . . . 37
107 3.3.1. Server implementation . . . . . . . . . . . . . . . . 38
108
109
110
111Belshe & Peon Expires August 4, 2012 [Page 2]
112
113Internet-Draft SPDY Feb 2012
114
115
116 3.3.2. Client implementation . . . . . . . . . . . . . . . . 39
117 4. Design Rationale and Notes . . . . . . . . . . . . . . . . . . 40
118 4.1. Separation of Framing Layer and Application Layer . . . . 40
119 4.2. Error handling - Framing Layer . . . . . . . . . . . . . . 40
120 4.3. One Connection Per Domain . . . . . . . . . . . . . . . . 40
121 4.4. Fixed vs Variable Length Fields . . . . . . . . . . . . . 41
122 4.5. Compression Context(s) . . . . . . . . . . . . . . . . . . 41
123 4.6. Unidirectional streams . . . . . . . . . . . . . . . . . . 42
124 4.7. Data Compression . . . . . . . . . . . . . . . . . . . . . 42
125 4.8. Server Push . . . . . . . . . . . . . . . . . . . . . . . 42
126 5. Security Considerations . . . . . . . . . . . . . . . . . . . 43
127 5.1. Use of Same-origin constraints . . . . . . . . . . . . . . 43
128 5.2. HTTP Headers and SPDY Headers . . . . . . . . . . . . . . 43
129 5.3. Cross-Protocol Attacks . . . . . . . . . . . . . . . . . . 43
130 5.4. Server Push Implicit Headers . . . . . . . . . . . . . . . 43
131 6. Privacy Considerations . . . . . . . . . . . . . . . . . . . . 44
132 6.1. Long Lived Connections . . . . . . . . . . . . . . . . . . 44
133 6.2. SETTINGS frame . . . . . . . . . . . . . . . . . . . . . . 44
134 7. Incompatibilities with SPDY draft #2 . . . . . . . . . . . . . 45
135 8. Requirements Notation . . . . . . . . . . . . . . . . . . . . 46
136 9. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . 47
137 10. Normative References . . . . . . . . . . . . . . . . . . . . . 48
138 Appendix A. Changes . . . . . . . . . . . . . . . . . . . . . . . 50
139 Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . . 51
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167Belshe & Peon Expires August 4, 2012 [Page 3]
168
169Internet-Draft SPDY Feb 2012
170
171
1721. Overview
173
174 One of the bottlenecks of HTTP implementations is that HTTP relies on
175 multiple connections for concurrency. This causes several problems,
176 including additional round trips for connection setup, slow-start
177 delays, and connection rationing by the client, where it tries to
178 avoid opening too many connections to any single server. HTTP
179 pipelining helps some, but only achieves partial multiplexing. In
180 addition, pipelining has proven non-deployable in existing browsers
181 due to intermediary interference.
182
183 SPDY adds a framing layer for multiplexing multiple, concurrent
184 streams across a single TCP connection (or any reliable transport
185 stream). The framing layer is optimized for HTTP-like request-
186 response streams, such that applications which run over HTTP today
187 can work over SPDY with little or no change on behalf of the web
188 application writer.
189
190 The SPDY session offers four improvements over HTTP:
191
192 Multiplexed requests: There is no limit to the number of requests
193 that can be issued concurrently over a single SPDY connection.
194
195 Prioritized requests: Clients can request certain resources to be
196 delivered first. This avoids the problem of congesting the
197 network channel with non-critical resources when a high-priority
198 request is pending.
199
200 Compressed headers: Clients today send a significant amount of
201 redundant data in the form of HTTP headers. Because a single web
202 page may require 50 or 100 subrequests, this data is significant.
203
204 Server pushed streams: Server Push enables content to be pushed
205 from servers to clients without a request.
206
207 SPDY attempts to preserve the existing semantics of HTTP. All
208 features such as cookies, ETags, Vary headers, Content-Encoding
209 negotiations, etc work as they do with HTTP; SPDY only replaces the
210 way the data is written to the network.
211
2121.1. Document Organization
213
214 The SPDY Specification is split into two parts: a framing layer
215 (Section 2), which multiplexes a TCP connection into independent,
216 length-prefixed frames, and an HTTP layer (Section 3), which
217 specifies the mechanism for overlaying HTTP request/response pairs on
218 top of the framing layer. While some of the framing layer concepts
219 are isolated from the HTTP layer, building a generic framing layer
220
221
222
223Belshe & Peon Expires August 4, 2012 [Page 4]
224
225Internet-Draft SPDY Feb 2012
226
227
228 has not been a goal. The framing layer is tailored to the needs of
229 the HTTP protocol and server push.
230
2311.2. Definitions
232
233 client: The endpoint initiating the SPDY session.
234
235 connection: A transport-level connection between two endpoints.
236
237 endpoint: Either the client or server of a connection.
238
239 frame: A header-prefixed sequence of bytes sent over a SPDY
240 session.
241
242 server: The endpoint which did not initiate the SPDY session.
243
244 session: A synonym for a connection.
245
246 session error: An error on the SPDY session.
247
248 stream: A bi-directional flow of bytes across a virtual channel
249 within a SPDY session.
250
251 stream error: An error on an individual SPDY stream.
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279Belshe & Peon Expires August 4, 2012 [Page 5]
280
281Internet-Draft SPDY Feb 2012
282
283
2842. SPDY Framing Layer
285
2862.1. Session (Connections)
287
288 The SPDY framing layer (or "session") runs atop a reliable transport
289 layer such as TCP [RFC0793]. The client is the TCP connection
290 initiator. SPDY connections are persistent connections.
291
292 For best performance, it is expected that clients will not close open
293 connections until the user navigates away from all web pages
294 referencing a connection, or until the server closes the connection.
295 Servers are encouraged to leave connections open for as long as
296 possible, but can terminate idle connections if necessary. When
297 either endpoint closes the transport-level connection, it MUST first
298 send a GOAWAY (Section 2.6.6) frame so that the endpoints can
299 reliably determine if requests finished before the close.
300
3012.2. Framing
302
303 Once the connection is established, clients and servers exchange
304 framed messages. There are two types of frames: control frames
305 (Section 2.2.1) and data frames (Section 2.2.2). Frames always have
306 a common header which is 8 bytes in length.
307
308 The first bit is a control bit indicating whether a frame is a
309 control frame or data frame. Control frames carry a version number,
310 a frame type, flags, and a length. Data frames contain the stream
311 ID, flags, and the length for the payload carried after the common
312 header. The simple header is designed to make reading and writing of
313 frames easy.
314
315 All integer values, including length, version, and type, are in
316 network byte order. SPDY does not enforce alignment of types in
317 dynamically sized frames.
318
3192.2.1. Control frames
320
321 +----------------------------------+
322 |C| Version(15bits) | Type(16bits) |
323 +----------------------------------+
324 | Flags (8) | Length (24 bits) |
325 +----------------------------------+
326 | Data |
327 +----------------------------------+
328
329 Control bit: The 'C' bit is a single bit indicating if this is a
330 control message. For control frames this value is always 1.
331
332
333
334
335Belshe & Peon Expires August 4, 2012 [Page 6]
336
337Internet-Draft SPDY Feb 2012
338
339
340 Version: The version number of the SPDY protocol. This document
341 describes SPDY version 3.
342
343 Type: The type of control frame. See Control Frames for the complete
344 list of control frames.
345
346 Flags: Flags related to this frame. Flags for control frames and
347 data frames are different.
348
349 Length: An unsigned 24-bit value representing the number of bytes
350 after the length field.
351
352 Data: data associated with this control frame. The format and length
353 of this data is controlled by the control frame type.
354
355 Control frame processing requirements:
356
357 Note that full length control frames (16MB) can be large for
358 implementations running on resource-limited hardware. In such
359 cases, implementations MAY limit the maximum length frame
360 supported. However, all implementations MUST be able to receive
361 control frames of at least 8192 octets in length.
362
3632.2.2. Data frames
364
365 +----------------------------------+
366 |C| Stream-ID (31bits) |
367 +----------------------------------+
368 | Flags (8) | Length (24 bits) |
369 +----------------------------------+
370 | Data |
371 +----------------------------------+
372
373 Control bit: For data frames this value is always 0.
374
375 Stream-ID: A 31-bit value identifying the stream.
376
377 Flags: Flags related to this frame. Valid flags are:
378
379 0x01 = FLAG_FIN - signifies that this frame represents the last
380 frame to be transmitted on this stream. See Stream Close
381 (Section 2.3.7) below.
382
383 0x02 = FLAG_COMPRESS - indicates that the data in this frame has
384 been compressed.
385
386 Length: An unsigned 24-bit value representing the number of bytes
387 after the length field. The total size of a data frame is 8 bytes +
388
389
390
391Belshe & Peon Expires August 4, 2012 [Page 7]
392
393Internet-Draft SPDY Feb 2012
394
395
396 length. It is valid to have a zero-length data frame.
397
398 Data: The variable-length data payload; the length was defined in the
399 length field.
400
401 Data frame processing requirements:
402
403 If an endpoint receives a data frame for a stream-id which is not
404 open and the endpoint has not sent a GOAWAY (Section 2.6.6) frame,
405 it MUST send issue a stream error (Section 2.4.2) with the error
406 code INVALID_STREAM for the stream-id.
407
408 If the endpoint which created the stream receives a data frame
409 before receiving a SYN_REPLY on that stream, it is a protocol
410 error, and the recipient MUST issue a stream error (Section 2.4.2)
411 with the status code PROTOCOL_ERROR for the stream-id.
412
413 Implementors note: If an endpoint receives multiple data frames
414 for invalid stream-ids, it MAY close the session.
415
416 All SPDY endpoints MUST accept compressed data frames.
417 Compression of data frames is always done using zlib compression.
418 Each stream initializes and uses its own compression context
419 dedicated to use within that stream. Endpoints are encouraged to
420 use application level compression rather than SPDY stream level
421 compression.
422
423 Each SPDY stream sending compressed frames creates its own zlib
424 context for that stream, and these compression contexts MUST be
425 distinct from the compression contexts used with SYN_STREAM/
426 SYN_REPLY/HEADER compression. (Thus, if both endpoints of a
427 stream are compressing data on the stream, there will be two zlib
428 contexts, one for sending and one for receiving).
429
4302.3. Streams
431
432 Streams are independent sequences of bi-directional data divided into
433 frames with several properties:
434
435 Streams may be created by either the client or server.
436
437 Streams optionally carry a set of name/value header pairs.
438
439 Streams can concurrently send data interleaved with other streams.
440
441 Streams may be cancelled.
442
443
444
445
446
447Belshe & Peon Expires August 4, 2012 [Page 8]
448
449Internet-Draft SPDY Feb 2012
450
451
4522.3.1. Stream frames
453
454 SPDY defines 3 control frames to manage the lifecycle of a stream:
455
456 SYN_STREAM - Open a new stream
457
458 SYN_REPLY - Remote acknowledgement of a new, open stream
459
460 RST_STREAM - Close a stream
461
4622.3.2. Stream creation
463
464 A stream is created by sending a control frame with the type set to
465 SYN_STREAM (Section 2.6.1). If the server is initiating the stream,
466 the Stream-ID must be even. If the client is initiating the stream,
467 the Stream-ID must be odd. 0 is not a valid Stream-ID. Stream-IDs
468 from each side of the connection must increase monotonically as new
469 streams are created. E.g. Stream 2 may be created after stream 3,
470 but stream 7 must not be created after stream 9. Stream IDs do not
471 wrap: when a client or server cannot create a new stream id without
472 exceeding a 31 bit value, it MUST NOT create a new stream.
473
474 The stream-id MUST increase with each new stream. If an endpoint
475 receives a SYN_STREAM with a stream id which is less than any
476 previously received SYN_STREAM, it MUST issue a session error
477 (Section 2.4.1) with the status PROTOCOL_ERROR.
478
479 It is a protocol error to send two SYN_STREAMs with the same
480 stream-id. If a recipient receives a second SYN_STREAM for the same
481 stream, it MUST issue a stream error (Section 2.4.2) with the status
482 code PROTOCOL_ERROR.
483
484 Upon receipt of a SYN_STREAM, the recipient can reject the stream by
485 sending a stream error (Section 2.4.2) with the error code
486 REFUSED_STREAM. Note, however, that the creating endpoint may have
487 already sent additional frames for that stream which cannot be
488 immediately stopped.
489
490 Once the stream is created, the creator may immediately send HEADERS
491 or DATA frames for that stream, without needing to wait for the
492 recipient to acknowledge.
493
4942.3.2.1. Unidirectional streams
495
496 When an endpoint creates a stream with the FLAG_UNIDIRECTIONAL flag
497 set, it creates a unidirectional stream which the creating endpoint
498 can use to send frames, but the receiving endpoint cannot. The
499 receiving endpoint is implicitly already in the half-closed
500
501
502
503Belshe & Peon Expires August 4, 2012 [Page 9]
504
505Internet-Draft SPDY Feb 2012
506
507
508 (Section 2.3.6) state.
509
5102.3.2.2. Bidirectional streams
511
512 SYN_STREAM frames which do not use the FLAG_UNIDIRECTIONAL flag are
513 bidirectional streams. Both endpoints can send data on a bi-
514 directional stream.
515
5162.3.3. Stream priority
517
518 The creator of a stream assigns a priority for that stream. Priority
519 is represented as an integer from 0 to 7. 0 represents the highest
520 priority and 7 represents the lowest priority.
521
522 The sender and recipient SHOULD use best-effort to process streams in
523 the order of highest priority to lowest priority.
524
5252.3.4. Stream headers
526
527 Streams carry optional sets of name/value pair headers which carry
528 metadata about the stream. After the stream has been created, and as
529 long as the sender is not closed (Section 2.3.7) or half-closed
530 (Section 2.3.6), each side may send HEADERS frame(s) containing the
531 header data. Header data can be sent in multiple HEADERS frames, and
532 HEADERS frames may be interleaved with data frames.
533
5342.3.5. Stream data exchange
535
536 Once a stream is created, it can be used to send arbitrary amounts of
537 data. Generally this means that a series of data frames will be sent
538 on the stream until a frame containing the FLAG_FIN flag is set. The
539 FLAG_FIN can be set on a SYN_STREAM (Section 2.6.1), SYN_REPLY
540 (Section 2.6.2), HEADERS (Section 2.6.7) or a DATA (Section 2.2.2)
541 frame. Once the FLAG_FIN has been sent, the stream is considered to
542 be half-closed.
543
5442.3.6. Stream half-close
545
546 When one side of the stream sends a frame with the FLAG_FIN flag set,
547 the stream is half-closed from that endpoint. The sender of the
548 FLAG_FIN MUST NOT send further frames on that stream. When both
549 sides have half-closed, the stream is closed.
550
551 If an endpoint receives a data frame after the stream is half-closed
552 from the sender (e.g. the endpoint has already received a prior frame
553 for the stream with the FIN flag set), it MUST send a RST_STREAM to
554 the sender with the status STREAM_ALREADY_CLOSED.
555
556
557
558
559Belshe & Peon Expires August 4, 2012 [Page 10]
560
561Internet-Draft SPDY Feb 2012
562
563
5642.3.7. Stream close
565
566 There are 3 ways that streams can be terminated:
567
568 Normal termination: Normal stream termination occurs when both
569 sender and recipient have half-closed the stream by sending a
570 FLAG_FIN.
571
572 Abrupt termination: Either the client or server can send a
573 RST_STREAM control frame at any time. A RST_STREAM contains an
574 error code to indicate the reason for failure. When a RST_STREAM
575 is sent from the stream originator, it indicates a failure to
576 complete the stream and that no further data will be sent on the
577 stream. When a RST_STREAM is sent from the stream recipient, the
578 sender, upon receipt, should stop sending any data on the stream.
579 The stream recipient should be aware that there is a race between
580 data already in transit from the sender and the time the
581 RST_STREAM is received. See Stream Error Handling (Section 2.4.2)
582
583 TCP connection teardown: If the TCP connection is torn down while
584 un-closed streams exist, then the endpoint must assume that the
585 stream was abnormally interrupted and may be incomplete.
586
587 If an endpoint receives a data frame after the stream is closed, it
588 must send a RST_STREAM to the sender with the status PROTOCOL_ERROR.
589
5902.4. Error Handling
591
592 The SPDY framing layer has only two types of errors, and they are
593 always handled consistently. Any reference in this specification to
594 "issue a session error" refers to Section 2.4.1. Any reference to
595 "issue a stream error" refers to Section 2.4.2.
596
5972.4.1. Session Error Handling
598
599 A session error is any error which prevents further processing of the
600 framing layer or which corrupts the session compression state. When
601 a session error occurs, the endpoint encountering the error MUST
602 first send a GOAWAY (Section 2.6.6) frame with the stream id of most
603 recently received stream from the remote endpoint, and the error code
604 for why the session is terminating. After sending the GOAWAY frame,
605 the endpoint MUST close the TCP connection.
606
607 Note that the session compression state is dependent upon both
608 endpoints always processing all compressed data. If an endpoint
609 partially processes a frame containing compressed data without
610 updating compression state properly, future control frames which use
611 compression will be always be errored. Implementations SHOULD always
612
613
614
615Belshe & Peon Expires August 4, 2012 [Page 11]
616
617Internet-Draft SPDY Feb 2012
618
619
620 try to process compressed data so that errors which could be handled
621 as stream errors do not become session errors.
622
623 Note that because this GOAWAY is sent during a session error case, it
624 is possible that the GOAWAY will not be reliably received by the
625 receiving endpoint. It is a best-effort attempt to communicate with
626 the remote about why the session is going down.
627
6282.4.2. Stream Error Handling
629
630 A stream error is an error related to a specific stream-id which does
631 not affect processing of other streams at the framing layer. Upon a
632 stream error, the endpoint MUST send a RST_STREAM (Section 2.6.3)
633 frame which contains the stream id of the stream where the error
634 occurred and the error status which caused the error. After sending
635 the RST_STREAM, the stream is closed to the sending endpoint. After
636 sending the RST_STREAM, if the sender receives any frames other than
637 a RST_STREAM for that stream id, it will result in sending additional
638 RST_STREAM frames. An endpoint MUST NOT send a RST_STREAM in
639 response to an RST_STREAM, as doing so would lead to RST_STREAM
640 loops. Sending a RST_STREAM does not cause the SPDY session to be
641 closed.
642
643 If an endpoint has multiple RST_STREAM frames to send in succession
644 for the same stream-id and the same error code, it MAY coalesce them
645 into a single RST_STREAM frame. (This can happen if a stream is
646 closed, but the remote sends multiple data frames. There is no
647 reason to send a RST_STREAM for each frame in succession).
648
6492.5. Data flow
650
651 Because TCP provides a single stream of data on which SPDY
652 multiplexes multiple logical streams, clients and servers must
653 intelligently interleave data messages for concurrent sessions.
654
6552.6. Control frame types
656
6572.6.1. SYN_STREAM
658
659 The SYN_STREAM control frame allows the sender to asynchronously
660 create a stream between the endpoints. See Stream Creation
661 (Section 2.3.2)
662
663
664
665
666
667
668
669
670
671Belshe & Peon Expires August 4, 2012 [Page 12]
672
673Internet-Draft SPDY Feb 2012
674
675
676+------------------------------------+
677|1| version | 1 |
678+------------------------------------+
679| Flags (8) | Length (24 bits) |
680+------------------------------------+
681|X| Stream-ID (31bits) |
682+------------------------------------+
683|X| Associated-To-Stream-ID (31bits) |
684+------------------------------------+
685| Pri|Unused | Slot | |
686+-------------------+ |
687| Number of Name/Value pairs (int32) | <+
688+------------------------------------+ |
689| Length of name (int32) | | This section is the "Name/Value
690+------------------------------------+ | Header Block", and is compressed.
691| Name (string) | |
692+------------------------------------+ |
693| Length of value (int32) | |
694+------------------------------------+ |
695| Value (string) | |
696+------------------------------------+ |
697| (repeats) | <+
698
699 Flags: Flags related to this frame. Valid flags are:
700
701 0x01 = FLAG_FIN - marks this frame as the last frame to be
702 transmitted on this stream and puts the sender in the half-closed
703 (Section 2.3.6) state.
704
705 0x02 = FLAG_UNIDIRECTIONAL - a stream created with this flag puts
706 the recipient in the half-closed (Section 2.3.6) state.
707
708 Length: The length is the number of bytes which follow the length
709 field in the frame. For SYN_STREAM frames, this is 10 bytes plus the
710 length of the compressed Name/Value block.
711
712 Stream-ID: The 31-bit identifier for this stream. This stream-id
713 will be used in frames which are part of this stream.
714
715 Associated-To-Stream-ID: The 31-bit identifier for a stream which
716 this stream is associated to. If this stream is independent of all
717 other streams, it should be 0.
718
719 Priority: A 3-bit priority (Section 2.3.3) field.
720
721 Unused: 5 bits of unused space, reserved for future use.
722
723 Slot: An 8 bit unsigned integer specifying the index in the server's
724
725
726
727Belshe & Peon Expires August 4, 2012 [Page 13]
728
729Internet-Draft SPDY Feb 2012
730
731
732 CREDENTIAL vector of the client certificate to be used for this
733 request. see CREDENTIAL frame (Section 2.6.9). The value 0 means no
734 client certificate should be associated with this stream.
735
736 Name/Value Header Block: A set of name/value pairs carried as part of
737 the SYN_STREAM. see Name/Value Header Block (Section 2.6.10).
738
739 If an endpoint receives a SYN_STREAM which is larger than the
740 implementation supports, it MAY send a RST_STREAM with error code
741 FRAME_TOO_LARGE. All implementations MUST support the minimum size
742 limits defined in the Control Frames section (Section 2.2.1).
743
7442.6.2. SYN_REPLY
745
746 SYN_REPLY indicates the acceptance of a stream creation by the
747 recipient of a SYN_STREAM frame.
748
749+------------------------------------+
750|1| version | 2 |
751+------------------------------------+
752| Flags (8) | Length (24 bits) |
753+------------------------------------+
754|X| Stream-ID (31bits) |
755+------------------------------------+
756| Number of Name/Value pairs (int32) | <+
757+------------------------------------+ |
758| Length of name (int32) | | This section is the "Name/Value
759+------------------------------------+ | Header Block", and is compressed.
760| Name (string) | |
761+------------------------------------+ |
762| Length of value (int32) | |
763+------------------------------------+ |
764| Value (string) | |
765+------------------------------------+ |
766| (repeats) | <+
767
768 Flags: Flags related to this frame. Valid flags are:
769
770 0x01 = FLAG_FIN - marks this frame as the last frame to be
771 transmitted on this stream and puts the sender in the half-closed
772 (Section 2.3.6) state.
773
774 Length: The length is the number of bytes which follow the length
775 field in the frame. For SYN_REPLY frames, this is 4 bytes plus the
776 length of the compressed Name/Value block.
777
778 Stream-ID: The 31-bit identifier for this stream.
779
780
781
782
783Belshe & Peon Expires August 4, 2012 [Page 14]
784
785Internet-Draft SPDY Feb 2012
786
787
788 If an endpoint receives multiple SYN_REPLY frames for the same active
789 stream ID, it MUST issue a stream error (Section 2.4.2) with the
790 error code STREAM_IN_USE.
791
792 Name/Value Header Block: A set of name/value pairs carried as part of
793 the SYN_STREAM. see Name/Value Header Block (Section 2.6.10).
794
795 If an endpoint receives a SYN_REPLY which is larger than the
796 implementation supports, it MAY send a RST_STREAM with error code
797 FRAME_TOO_LARGE. All implementations MUST support the minimum size
798 limits defined in the Control Frames section (Section 2.2.1).
799
8002.6.3. RST_STREAM
801
802 The RST_STREAM frame allows for abnormal termination of a stream.
803 When sent by the creator of a stream, it indicates the creator wishes
804 to cancel the stream. When sent by the recipient of a stream, it
805 indicates an error or that the recipient did not want to accept the
806 stream, so the stream should be closed.
807
808 +----------------------------------+
809 |1| version | 3 |
810 +----------------------------------+
811 | Flags (8) | 8 |
812 +----------------------------------+
813 |X| Stream-ID (31bits) |
814 +----------------------------------+
815 | Status code |
816 +----------------------------------+
817
818 Flags: Flags related to this frame. RST_STREAM does not define any
819 flags. This value must be 0.
820
821 Length: An unsigned 24-bit value representing the number of bytes
822 after the length field. For RST_STREAM control frames, this value is
823 always 8.
824
825 Stream-ID: The 31-bit identifier for this stream.
826
827 Status code: (32 bits) An indicator for why the stream is being
828 terminated.The following status codes are defined:
829
830 1 - PROTOCOL_ERROR. This is a generic error, and should only be
831 used if a more specific error is not available.
832
833 2 - INVALID_STREAM. This is returned when a frame is received for
834 a stream which is not active.
835
836
837
838
839Belshe & Peon Expires August 4, 2012 [Page 15]
840
841Internet-Draft SPDY Feb 2012
842
843
844 3 - REFUSED_STREAM. Indicates that the stream was refused before
845 any processing has been done on the stream.
846
847 4 - UNSUPPORTED_VERSION. Indicates that the recipient of a stream
848 does not support the SPDY version requested.
849
850 5 - CANCEL. Used by the creator of a stream to indicate that the
851 stream is no longer needed.
852
853 6 - INTERNAL_ERROR. This is a generic error which can be used
854 when the implementation has internally failed, not due to anything
855 in the protocol.
856
857 7 - FLOW_CONTROL_ERROR. The endpoint detected that its peer
858 violated the flow control protocol.
859
860 8 - STREAM_IN_USE. The endpoint received a SYN_REPLY for a stream
861 already open.
862
863 9 - STREAM_ALREADY_CLOSED. The endpoint received a data or
864 SYN_REPLY frame for a stream which is half closed.
865
866 10 - INVALID_CREDENTIALS. The server received a request for a
867 resource whose origin does not have valid credentials in the
868 client certificate vector.
869
870 11 - FRAME_TOO_LARGE. The endpoint received a frame which this
871 implementation could not support. If FRAME_TOO_LARGE is sent for
872 a SYN_STREAM, HEADERS, or SYN_REPLY frame without fully processing
873 the compressed portion of those frames, then the compression state
874 will be out-of-sync with the other endpoint. In this case,
875 senders of FRAME_TOO_LARGE MUST close the session.
876
877 Note: 0 is not a valid status code for a RST_STREAM.
878
879 After receiving a RST_STREAM on a stream, the recipient must not send
880 additional frames for that stream, and the stream moves into the
881 closed state.
882
8832.6.4. SETTINGS
884
885 A SETTINGS frame contains a set of id/value pairs for communicating
886 configuration data about how the two endpoints may communicate.
887 SETTINGS frames can be sent at any time by either endpoint, are
888 optionally sent, and are fully asynchronous. When the server is the
889 sender, the sender can request that configuration data be persisted
890 by the client across SPDY sessions and returned to the server in
891 future communications.
892
893
894
895Belshe & Peon Expires August 4, 2012 [Page 16]
896
897Internet-Draft SPDY Feb 2012
898
899
900 Persistence of SETTINGS ID/Value pairs is done on a per origin/IP
901 pair (the "origin" is the set of scheme, host, and port from the URI.
902 See [RFC6454]). That is, when a client connects to a server, and the
903 server persists settings within the client, the client SHOULD return
904 the persisted settings on future connections to the same origin AND
905 IP address and TCP port. Clients MUST NOT request servers to use the
906 persistence features of the SETTINGS frames, and servers MUST ignore
907 persistence related flags sent by a client.
908
909 +----------------------------------+
910 |1| version | 4 |
911 +----------------------------------+
912 | Flags (8) | Length (24 bits) |
913 +----------------------------------+
914 | Number of entries |
915 +----------------------------------+
916 | ID/Value Pairs |
917 | ... |
918
919 Control bit: The control bit is always 1 for this message.
920
921 Version: The SPDY version number.
922
923 Type: The message type for a SETTINGS message is 4.
924
925 Flags: FLAG_SETTINGS_CLEAR_SETTINGS (0x1): When set, the client
926 should clear any previously persisted SETTINGS ID/Value pairs. If
927 this frame contains ID/Value pairs with the
928 FLAG_SETTINGS_PERSIST_VALUE set, then the client will first clear its
929 existing, persisted settings, and then persist the values with the
930 flag set which are contained within this frame. Because persistence
931 is only implemented on the client, this flag can only be used when
932 the sender is the server.
933
934 Length: An unsigned 24-bit value representing the number of bytes
935 after the length field. The total size of a SETTINGS frame is 8
936 bytes + length.
937
938 Number of entries: A 32-bit value representing the number of ID/value
939 pairs in this message.
940
941 ID: A 32-bit ID number, comprised of 8 bits of flags and 24 bits of
942 unique ID.
943
944 ID.flags:
945
946 FLAG_SETTINGS_PERSIST_VALUE (0x1): When set, the sender of this
947 SETTINGS frame is requesting that the recipient persist the ID/
948
949
950
951Belshe & Peon Expires August 4, 2012 [Page 17]
952
953Internet-Draft SPDY Feb 2012
954
955
956 Value and return it in future SETTINGS frames sent from the
957 sender to this recipient. Because persistence is only
958 implemented on the client, this flag is only sent by the
959 server.
960
961 FLAG_SETTINGS_PERSISTED (0x2): When set, the sender is
962 notifying the recipient that this ID/Value pair was previously
963 sent to the sender by the recipient with the
964 FLAG_SETTINGS_PERSIST_VALUE, and the sender is returning it.
965 Because persistence is only implemented on the client, this
966 flag is only sent by the client.
967
968 Defined IDs:
969
970 1 - SETTINGS_UPLOAD_BANDWIDTH allows the sender to send its
971 expected upload bandwidth on this channel. This number is an
972 estimate. The value should be the integral number of kilobytes
973 per second that the sender predicts as an expected maximum
974 upload channel capacity.
975
976 2 - SETTINGS_DOWNLOAD_BANDWIDTH allows the sender to send its
977 expected download bandwidth on this channel. This number is an
978 estimate. The value should be the integral number of kilobytes
979 per second that the sender predicts as an expected maximum
980 download channel capacity.
981
982 3 - SETTINGS_ROUND_TRIP_TIME allows the sender to send its
983 expected round-trip-time on this channel. The round trip time
984 is defined as the minimum amount of time to send a control
985 frame from this client to the remote and receive a response.
986 The value is represented in milliseconds.
987
988 4 - SETTINGS_MAX_CONCURRENT_STREAMS allows the sender to inform
989 the remote endpoint the maximum number of concurrent streams
990 which it will allow. By default there is no limit. For
991 implementors it is recommended that this value be no smaller
992 than 100.
993
994 5 - SETTINGS_CURRENT_CWND allows the sender to inform the
995 remote endpoint of the current TCP CWND value.
996
997 6 - SETTINGS_DOWNLOAD_RETRANS_RATE allows the sender to inform
998 the remote endpoint the retransmission rate (bytes
999 retransmitted / total bytes transmitted).
1000
1001 7 - SETTINGS_INITIAL_WINDOW_SIZE allows the sender to inform
1002 the remote endpoint the initial window size (in bytes) for new
1003 streams.
1004
1005
1006
1007Belshe & Peon Expires August 4, 2012 [Page 18]
1008
1009Internet-Draft SPDY Feb 2012
1010
1011
1012 8 - SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE allows the server
1013 to inform the client if the new size of the client certificate
1014 vector.
1015
1016 Value: A 32-bit value.
1017
1018 The message is intentionally extensible for future information which
1019 may improve client-server communications. The sender does not need
1020 to send every type of ID/value. It must only send those for which it
1021 has accurate values to convey. When multiple ID/value pairs are
1022 sent, they should be sent in order of lowest id to highest id. A
1023 single SETTINGS frame MUST not contain multiple values for the same
1024 ID. If the recipient of a SETTINGS frame discovers multiple values
1025 for the same ID, it MUST ignore all values except the first one.
1026
1027 A server may send multiple SETTINGS frames containing different ID/
1028 Value pairs. When the same ID/Value is sent twice, the most recent
1029 value overrides any previously sent values. If the server sends IDs
1030 1, 2, and 3 with the FLAG_SETTINGS_PERSIST_VALUE in a first SETTINGS
1031 frame, and then sends IDs 4 and 5 with the
1032 FLAG_SETTINGS_PERSIST_VALUE, when the client returns the persisted
1033 state on its next SETTINGS frame, it SHOULD send all 5 settings (1,
1034 2, 3, 4, and 5 in this example) to the server.
1035
10362.6.5. PING
1037
1038 The PING control frame is a mechanism for measuring a minimal round-
1039 trip time from the sender. It can be sent from the client or the
1040 server. Recipients of a PING frame should send an identical frame to
1041 the sender as soon as possible (if there is other pending data
1042 waiting to be sent, PING should take highest priority). Each ping
1043 sent by a sender should use a unique ID.
1044
1045 +----------------------------------+
1046 |1| version | 6 |
1047 +----------------------------------+
1048 | 0 (flags) | 4 (length) |
1049 +----------------------------------|
1050 | 32-bit ID |
1051 +----------------------------------+
1052
1053 Control bit: The control bit is always 1 for this message.
1054
1055 Version: The SPDY version number.
1056
1057 Type: The message type for a PING message is 6.
1058
1059 Length: This frame is always 4 bytes long.
1060
1061
1062
1063Belshe & Peon Expires August 4, 2012 [Page 19]
1064
1065Internet-Draft SPDY Feb 2012
1066
1067
1068 ID: A unique ID for this ping, represented as an unsigned 32 bit
1069 value. When the client initiates a ping, it must use an odd numbered
1070 ID. When the server initiates a ping, it must use an even numbered
1071 ping. Use of odd/even IDs is required in order to avoid accidental
1072 looping on PINGs (where each side initiates an identical PING at the
1073 same time).
1074
1075 Note: If a sender uses all possible PING ids (e.g. has sent all 2^31
1076 possible IDs), it can wrap and start re-using IDs.
1077
1078 If a server receives an even numbered PING which it did not initiate,
1079 it must ignore the PING. If a client receives an odd numbered PING
1080 which it did not initiate, it must ignore the PING.
1081
10822.6.6. GOAWAY
1083
1084 The GOAWAY control frame is a mechanism to tell the remote side of
1085 the connection to stop creating streams on this session. It can be
1086 sent from the client or the server. Once sent, the sender will not
1087 respond to any new SYN_STREAMs on this session. Recipients of a
1088 GOAWAY frame must not send additional streams on this session,
1089 although a new session can be established for new streams. The
1090 purpose of this message is to allow an endpoint to gracefully stop
1091 accepting new streams (perhaps for a reboot or maintenance), while
1092 still finishing processing of previously established streams.
1093
1094 There is an inherent race condition between an endpoint sending
1095 SYN_STREAMs and the remote sending a GOAWAY message. To deal with
1096 this case, the GOAWAY contains a last-stream-id indicating the
1097 stream-id of the last stream which was created on the sending
1098 endpoint in this session. If the receiver of the GOAWAY sent new
1099 SYN_STREAMs for sessions after this last-stream-id, they were not
1100 processed by the server and the receiver may treat the stream as
1101 though it had never been created at all (hence the receiver may want
1102 to re-create the stream later on a new session).
1103
1104 Endpoints should always send a GOAWAY message before closing a
1105 connection so that the remote can know whether a stream has been
1106 partially processed or not. (For example, if an HTTP client sends a
1107 POST at the same time that a server closes a connection, the client
1108 cannot know if the server started to process that POST request if the
1109 server does not send a GOAWAY frame to indicate where it stopped
1110 working).
1111
1112 After sending a GOAWAY message, the sender must ignore all SYN_STREAM
1113 frames for new streams.
1114
1115
1116
1117
1118
1119Belshe & Peon Expires August 4, 2012 [Page 20]
1120
1121Internet-Draft SPDY Feb 2012
1122
1123
1124 +----------------------------------+
1125 |1| version | 7 |
1126 +----------------------------------+
1127 | 0 (flags) | 8 (length) |
1128 +----------------------------------|
1129 |X| Last-good-stream-ID (31 bits) |
1130 +----------------------------------+
1131 | Status code |
1132 +----------------------------------+
1133
1134 Control bit: The control bit is always 1 for this message.
1135
1136 Version: The SPDY version number.
1137
1138 Type: The message type for a GOAWAY message is 7.
1139
1140 Length: This frame is always 8 bytes long.
1141
1142 Last-good-stream-Id: The last stream id which was replied to (with
1143 either a SYN_REPLY or RST_STREAM) by the sender of the GOAWAY
1144 message. If no streams were replied to, this value MUST be 0.
1145
1146 Status: The reason for closing the session.
1147
1148 0 - OK. This is a normal session teardown.
1149
1150 1 - PROTOCOL_ERROR. This is a generic error, and should only be
1151 used if a more specific error is not available.
1152
1153 11 - INTERNAL_ERROR. This is a generic error which can be used
1154 when the implementation has internally failed, not due to anything
1155 in the protocol.
1156
11572.6.7. HEADERS
1158
1159 The HEADERS frame augments a stream with additional headers. It may
1160 be optionally sent on an existing stream at any time. Specific
1161 application of the headers in this frame is application-dependent.
1162 The name/value header block within this frame is compressed.
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175Belshe & Peon Expires August 4, 2012 [Page 21]
1176
1177Internet-Draft SPDY Feb 2012
1178
1179
1180+------------------------------------+
1181|1| version | 8 |
1182+------------------------------------+
1183| Flags (8) | Length (24 bits) |
1184+------------------------------------+
1185|X| Stream-ID (31bits) |
1186+------------------------------------+
1187| Number of Name/Value pairs (int32) | <+
1188+------------------------------------+ |
1189| Length of name (int32) | | This section is the "Name/Value
1190+------------------------------------+ | Header Block", and is compressed.
1191| Name (string) | |
1192+------------------------------------+ |
1193| Length of value (int32) | |
1194+------------------------------------+ |
1195| Value (string) | |
1196+------------------------------------+ |
1197| (repeats) | <+
1198
1199 Flags: Flags related to this frame. Valid flags are:
1200
1201 0x01 = FLAG_FIN - marks this frame as the last frame to be
1202 transmitted on this stream and puts the sender in the half-closed
1203 (Section 2.3.6) state.
1204
1205 Length: An unsigned 24 bit value representing the number of bytes
1206 after the length field. The minimum length of the length field is 4
1207 (when the number of name value pairs is 0).
1208
1209 Stream-ID: The stream this HEADERS block is associated with.
1210
1211 Name/Value Header Block: A set of name/value pairs carried as part of
1212 the SYN_STREAM. see Name/Value Header Block (Section 2.6.10).
1213
12142.6.8. WINDOW_UPDATE
1215
1216 The WINDOW_UPDATE control frame is used to implement per stream flow
1217 control in SPDY. Flow control in SPDY is per hop, that is, only
1218 between the two endpoints of a SPDY connection. If there are one or
1219 more intermediaries between the client and the origin server, flow
1220 control signals are not explicitly forwarded by the intermediaries.
1221 (However, throttling of data transfer by any recipient may have the
1222 effect of indirectly propagating flow control information upstream
1223 back to the original sender.) Flow control only applies to the data
1224 portion of data frames. Recipients must buffer all control frames.
1225 If a recipient fails to buffer an entire control frame, it MUST issue
1226 a stream error (Section 2.4.2) with the status code
1227 FLOW_CONTROL_ERROR for the stream.
1228
1229
1230
1231Belshe & Peon Expires August 4, 2012 [Page 22]
1232
1233Internet-Draft SPDY Feb 2012
1234
1235
1236 Flow control in SPDY is implemented by a data transfer window kept by
1237 the sender of each stream. The data transfer window is a simple
1238 uint32 that indicates how many bytes of data the sender can transmit.
1239 After a stream is created, but before any data frames have been
1240 transmitted, the sender begins with the initial window size. This
1241 window size is a measure of the buffering capability of the
1242 recipient. The sender must not send a data frame with data length
1243 greater than the transfer window size. After sending each data
1244 frame, the sender decrements its transfer window size by the amount
1245 of data transmitted. When the window size becomes less than or equal
1246 to 0, the sender must pause transmitting data frames. At the other
1247 end of the stream, the recipient sends a WINDOW_UPDATE control back
1248 to notify the sender that it has consumed some data and freed up
1249 buffer space to receive more data.
1250
1251 +----------------------------------+
1252 |1| version | 9 |
1253 +----------------------------------+
1254 | 0 (flags) | 8 (length) |
1255 +----------------------------------+
1256 |X| Stream-ID (31-bits) |
1257 +----------------------------------+
1258 |X| Delta-Window-Size (31-bits) |
1259 +----------------------------------+
1260
1261 Control bit: The control bit is always 1 for this message.
1262
1263 Version: The SPDY version number.
1264
1265 Type: The message type for a WINDOW_UPDATE message is 9.
1266
1267 Length: The length field is always 8 for this frame (there are 8
1268 bytes after the length field).
1269
1270 Stream-ID: The stream ID that this WINDOW_UPDATE control frame is
1271 for.
1272
1273 Delta-Window-Size: The additional number of bytes that the sender can
1274 transmit in addition to existing remaining window size. The legal
1275 range for this field is 1 to 2^31 - 1 (0x7fffffff) bytes.
1276
1277 The window size as kept by the sender must never exceed 2^31
1278 (although it can become negative in one special case). If a sender
1279 receives a WINDOW_UPDATE that causes the its window size to exceed
1280 this limit, it must send RST_STREAM with status code
1281 FLOW_CONTROL_ERROR to terminate the stream.
1282
1283 When a SPDY connection is first established, the default initial
1284
1285
1286
1287Belshe & Peon Expires August 4, 2012 [Page 23]
1288
1289Internet-Draft SPDY Feb 2012
1290
1291
1292 window size for all streams is 64KB. An endpoint can use the
1293 SETTINGS control frame to adjust the initial window size for the
1294 connection. That is, its peer can start out using the 64KB default
1295 initial window size when sending data frames before receiving the
1296 SETTINGS. Because SETTINGS is asynchronous, there may be a race
1297 condition if the recipient wants to decrease the initial window size,
1298 but its peer immediately sends 64KB on the creation of a new
1299 connection, before waiting for the SETTINGS to arrive. This is one
1300 case where the window size kept by the sender will become negative.
1301 Once the sender detects this condition, it must stop sending data
1302 frames and wait for the recipient to catch up. The recipient has two
1303 choices:
1304
1305 immediately send RST_STREAM with FLOW_CONTROL_ERROR status code.
1306
1307 allow the head of line blocking (as there is only one stream for
1308 the session and the amount of data in flight is bounded by the
1309 default initial window size), and send WINDOW_UPDATE as it
1310 consumes data.
1311
1312 In the case of option 2, both sides must compute the window size
1313 based on the initial window size in the SETTINGS. For example, if
1314 the recipient sets the initial window size to be 16KB, and the sender
1315 sends 64KB immediately on connection establishment, the sender will
1316 discover its window size is -48KB on receipt of the SETTINGS. As the
1317 recipient consumes the first 16KB, it must send a WINDOW_UPDATE of
1318 16KB back to the sender. This interaction continues until the
1319 sender's window size becomes positive again, and it can resume
1320 transmitting data frames.
1321
1322 After the recipient reads in a data frame with FLAG_FIN that marks
1323 the end of the data stream, it should not send WINDOW_UPDATE frames
1324 as it consumes the last data frame. A sender should ignore all the
1325 WINDOW_UPDATE frames associated with the stream after it send the
1326 last frame for the stream.
1327
1328 The data frames from the sender and the WINDOW_UPDATE frames from the
1329 recipient are completely asynchronous with respect to each other.
1330 This property allows a recipient to aggressively update the window
1331 size kept by the sender to prevent the stream from stalling.
1332
13332.6.9. CREDENTIAL
1334
1335 The CREDENTIAL control frame is used by the client to send additional
1336 client certificates to the server. A SPDY client may decide to send
1337 requests for resources from different origins on the same SPDY
1338 session if it decides that that server handles both origins. For
1339 example if the IP address associated with both hostnames matches and
1340
1341
1342
1343Belshe & Peon Expires August 4, 2012 [Page 24]
1344
1345Internet-Draft SPDY Feb 2012
1346
1347
1348 the SSL server certificate presented in the initial handshake is
1349 valid for both hostnames. However, because the SSL connection can
1350 contain at most one client certificate, the client needs a mechanism
1351 to send additional client certificates to the server.
1352
1353 The server is required to maintain a vector of client certificates
1354 associated with a SPDY session. When the client needs to send a
1355 client certificate to the server, it will send a CREDENTIAL frame
1356 that specifies the index of the slot in which to store the
1357 certificate as well as proof that the client posesses the
1358 corresponding private key. The initial size of this vector must be
1359 8. If the client provides a client certificate during the first TLS
1360 handshake, the contents of this certificate must be copied into the
1361 first slot (index 1) in the CREDENTIAL vector, though it may be
1362 overwritten by subsequent CREDENTIAL frames. The server must
1363 exclusively use the CREDNETIAL vector when evaluating the client
1364 certificates associated with an origin. The server may change the
1365 size of this vector by sending a SETTINGS frame with the setting
1366 SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE value specified. In the
1367 event that the new size is smaller than the current size, truncation
1368 occurs preserving lower-index slots as possible.
1369
1370 TLS renegotiation with client authentication is incompatible with
1371 SPDY given the multiplexed nature of SPDY. Specifically, imagine
1372 that the client has 2 requests outstanding to the server for two
1373 different pages (in different tabs). When the renegotiation + client
1374 certificate request comes in, the browser is unable to determine
1375 which resource triggered the client certificate request, in order to
1376 prompt the user accordingly.
1377
1378 +----------------------------------+
1379 |1|000000000000001|0000000000001011|
1380 +----------------------------------+
1381 | flags (8) | Length (24 bits) |
1382 +----------------------------------+
1383 | Slot (16 bits) | |
1384 +-----------------+ |
1385 | Proof Length (32 bits) |
1386 +----------------------------------+
1387 | Proof |
1388 +----------------------------------+ <+
1389 | Certificate Length (32 bits) | |
1390 +----------------------------------+ | Repeated until end of frame
1391 | Certificate | |
1392 +----------------------------------+ <+
1393
1394 Slot: The index in the server's client certificate vector where this
1395 certificate should be stored. If there is already a certificate
1396
1397
1398
1399Belshe & Peon Expires August 4, 2012 [Page 25]
1400
1401Internet-Draft SPDY Feb 2012
1402
1403
1404 stored at this index, it will be overwritten. The index is one
1405 based, not zero based; zero is an invalid slot index.
1406
1407 Proof: Cryptographic proof that the client has possession of the
1408 private key associated with the certificate. The format is a TLS
1409 digitally-signed element
1410 (http://tools.ietf.org/html/rfc5246#section-4.7). The signature
1411 algorithm must be the same as that used in the CertificateVerify
1412 message. However, since the MD5+SHA1 signature type used in TLS 1.0
1413 connections can not be correctly encoded in a digitally-signed
1414 element, SHA1 must be used when MD5+SHA1 was used in the SSL
1415 connection. The signature is calculated over a 32 byte TLS extractor
1416 value (http://tools.ietf.org/html/rfc5705) with a label of "EXPORTER
1417 SPDY certificate proof" using the empty string as context. ForRSA
1418 certificates the signature would be a PKCS#1 v1.5 signature. For
1419 ECDSA, it would be an ECDSA-Sig-Value
1420 (http://tools.ietf.org/html/rfc5480#appendix-A). For a 1024-bit RSA
1421 key, the CREDENTIAL message would be ~500 bytes.
1422
1423 Certificate: The certificate chain, starting with the leaf
1424 certificate. Each certificate must be encoded as a 32 bit length,
1425 followed by a DER encoded certificate. The certificate must be of
1426 the same type (RSA, ECDSA, etc) as the client certificate associated
1427 with the SSL connection.
1428
1429 If the server receives a request for a resource with unacceptable
1430 credential (either missing or invalid), it must reply with a
1431 RST_STREAM frame with the status code INVALID_CREDENTIALS. Upon
1432 receipt of a RST_STREAM frame with INVALID_CREDENTIALS, the client
1433 should initiate a new stream directly to the requested origin and
1434 resend the request. Note, SPDY does not allow the server to request
1435 different client authentication for different resources in the same
1436 origin.
1437
1438 If the server receives an invalid CREDENTIAL frame, it MUST respond
1439 with a GOAWAY frame and shutdown the session.
1440
14412.6.10. Name/Value Header Block
1442
1443 The Name/Value Header Block is found in the SYN_STREAM, SYN_REPLY and
1444 HEADERS control frames, and shares a common format:
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455Belshe & Peon Expires August 4, 2012 [Page 26]
1456
1457Internet-Draft SPDY Feb 2012
1458
1459
1460 +------------------------------------+
1461 | Number of Name/Value pairs (int32) |
1462 +------------------------------------+
1463 | Length of name (int32) |
1464 +------------------------------------+
1465 | Name (string) |
1466 +------------------------------------+
1467 | Length of value (int32) |
1468 +------------------------------------+
1469 | Value (string) |
1470 +------------------------------------+
1471 | (repeats) |
1472
1473 Number of Name/Value pairs: The number of repeating name/value pairs
1474 following this field.
1475
1476 List of Name/Value pairs:
1477
1478 Length of Name: a 32-bit value containing the number of octets in
1479 the name field. Note that in practice, this length must not
1480 exceed 2^24, as that is the maximum size of a SPDY frame.
1481
1482 Name: 0 or more octets, 8-bit sequences of data, excluding 0.
1483
1484 Length of Value: a 32-bit value containing the number of octets in
1485 the value field. Note that in practice, this length must not
1486 exceed 2^24, as that is the maximum size of a SPDY frame.
1487
1488 Value: 0 or more octets, 8-bit sequences of data, excluding 0.
1489
1490 Each header name must have at least one value. Header names are
1491 encoded using the US-ASCII character set [ASCII] and must be all
1492 lower case. The length of each name must be greater than zero. A
1493 recipient of a zero-length name MUST issue a stream error
1494 (Section 2.4.2) with the status code PROTOCOL_ERROR for the
1495 stream-id.
1496
1497 Duplicate header names are not allowed. To send two identically
1498 named headers, send a header with two values, where the values are
1499 separated by a single NUL (0) byte. A header value can either be
1500 empty (e.g. the length is zero) or it can contain multiple, NUL-
1501 separated values, each with length greater than zero. The value
1502 never starts nor ends with a NUL character. Recipients of illegal
1503 value fields MUST issue a stream error (Section 2.4.2) with the
1504 status code PROTOCOL_ERROR for the stream-id.
1505
1506
1507
1508
1509
1510
1511Belshe & Peon Expires August 4, 2012 [Page 27]
1512
1513Internet-Draft SPDY Feb 2012
1514
1515
15162.6.10.1. Compression
1517
1518 The Name/Value Header Block is a section of the SYN_STREAM,
1519 SYN_REPLY, and HEADERS frames used to carry header meta-data. This
1520 block is always compressed using zlib compression. Within this
1521 specification, any reference to 'zlib' is referring to the ZLIB
1522 Compressed Data Format Specification Version 3.3 as part of RFC1950.
1523 [RFC1950]
1524
1525 For each HEADERS compression instance, the initial state is
1526 initialized using the following dictionary [UDELCOMPRESSION]:
1527
1528 const unsigned char SPDY_dictionary_txt[] = {
1529 0x00, 0x00, 0x00, 0x07, 0x6f, 0x70, 0x74, 0x69, \\ - - - - o p t i
1530 0x6f, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x04, 0x68, \\ o n s - - - - h
1531 0x65, 0x61, 0x64, 0x00, 0x00, 0x00, 0x04, 0x70, \\ e a d - - - - p
1532 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x03, 0x70, \\ o s t - - - - p
1533 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x64, 0x65, \\ u t - - - - d e
1534 0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x00, 0x05, \\ l e t e - - - -
1535 0x74, 0x72, 0x61, 0x63, 0x65, 0x00, 0x00, 0x00, \\ t r a c e - - -
1536 0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x00, \\ - a c c e p t -
1537 0x00, 0x00, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, \\ - - - a c c e p
1538 0x74, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, \\ t - c h a r s e
1539 0x74, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x63, 0x63, \\ t - - - - a c c
1540 0x65, 0x70, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, \\ e p t - e n c o
1541 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x0f, \\ d i n g - - - -
1542 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c, \\ a c c e p t - l
1543 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, \\ a n g u a g e -
1544 0x00, 0x00, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, \\ - - - a c c e p
1545 0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, \\ t - r a n g e s
1546 0x00, 0x00, 0x00, 0x03, 0x61, 0x67, 0x65, 0x00, \\ - - - - a g e -
1547 0x00, 0x00, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77, \\ - - - a l l o w
1548 0x00, 0x00, 0x00, 0x0d, 0x61, 0x75, 0x74, 0x68, \\ - - - - a u t h
1549 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, \\ o r i z a t i o
1550 0x6e, 0x00, 0x00, 0x00, 0x0d, 0x63, 0x61, 0x63, \\ n - - - - c a c
1551 0x68, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, \\ h e - c o n t r
1552 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f, \\ o l - - - - c o
1553 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, \\ n n e c t i o n
1554 0x00, 0x00, 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, \\ - - - - c o n t
1555 0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65, \\ e n t - b a s e
1556 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74, \\ - - - - c o n t
1557 0x65, 0x6e, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, \\ e n t - e n c o
1558 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, \\ d i n g - - - -
1559 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, \\ c o n t e n t -
1560 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, \\ l a n g u a g e
1561 0x00, 0x00, 0x00, 0x0e, 0x63, 0x6f, 0x6e, 0x74, \\ - - - - c o n t
1562 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, \\ e n t - l e n g
1563 0x74, 0x68, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, \\ t h - - - - c o
1564
1565
1566
1567Belshe & Peon Expires August 4, 2012 [Page 28]
1568
1569Internet-Draft SPDY Feb 2012
1570
1571
1572 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f, \\ n t e n t - l o
1573 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, \\ c a t i o n - -
1574 0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, \\ - - c o n t e n
1575 0x74, 0x2d, 0x6d, 0x64, 0x35, 0x00, 0x00, 0x00, \\ t - m d 5 - - -
1576 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, \\ - c o n t e n t
1577 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, \\ - r a n g e - -
1578 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, \\ - - c o n t e n
1579 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00, \\ t - t y p e - -
1580 0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x00, 0x00, \\ - - d a t e - -
1581 0x00, 0x04, 0x65, 0x74, 0x61, 0x67, 0x00, 0x00, \\ - - e t a g - -
1582 0x00, 0x06, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, \\ - - e x p e c t
1583 0x00, 0x00, 0x00, 0x07, 0x65, 0x78, 0x70, 0x69, \\ - - - - e x p i
1584 0x72, 0x65, 0x73, 0x00, 0x00, 0x00, 0x04, 0x66, \\ r e s - - - - f
1585 0x72, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x68, \\ r o m - - - - h
1586 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x69, \\ o s t - - - - i
1587 0x66, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, \\ f - m a t c h -
1588 0x00, 0x00, 0x11, 0x69, 0x66, 0x2d, 0x6d, 0x6f, \\ - - - i f - m o
1589 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73, \\ d i f i e d - s
1590 0x69, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x0d, \\ i n c e - - - -
1591 0x69, 0x66, 0x2d, 0x6e, 0x6f, 0x6e, 0x65, 0x2d, \\ i f - n o n e -
1592 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00, \\ m a t c h - - -
1593 0x08, 0x69, 0x66, 0x2d, 0x72, 0x61, 0x6e, 0x67, \\ - i f - r a n g
1594 0x65, 0x00, 0x00, 0x00, 0x13, 0x69, 0x66, 0x2d, \\ e - - - - i f -
1595 0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, \\ u n m o d i f i
1596 0x65, 0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65, \\ e d - s i n c e
1597 0x00, 0x00, 0x00, 0x0d, 0x6c, 0x61, 0x73, 0x74, \\ - - - - l a s t
1598 0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, \\ - m o d i f i e
1599 0x64, 0x00, 0x00, 0x00, 0x08, 0x6c, 0x6f, 0x63, \\ d - - - - l o c
1600 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, \\ a t i o n - - -
1601 0x0c, 0x6d, 0x61, 0x78, 0x2d, 0x66, 0x6f, 0x72, \\ - m a x - f o r
1602 0x77, 0x61, 0x72, 0x64, 0x73, 0x00, 0x00, 0x00, \\ w a r d s - - -
1603 0x06, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x00, \\ - p r a g m a -
1604 0x00, 0x00, 0x12, 0x70, 0x72, 0x6f, 0x78, 0x79, \\ - - - p r o x y
1605 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, \\ - a u t h e n t
1606 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, \\ i c a t e - - -
1607 0x13, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61, \\ - p r o x y - a
1608 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, \\ u t h o r i z a
1609 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x05, \\ t i o n - - - -
1610 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00, \\ r a n g e - - -
1611 0x07, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72, \\ - r e f e r e r
1612 0x00, 0x00, 0x00, 0x0b, 0x72, 0x65, 0x74, 0x72, \\ - - - - r e t r
1613 0x79, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x00, \\ y - a f t e r -
1614 0x00, 0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, \\ - - - s e r v e
1615 0x72, 0x00, 0x00, 0x00, 0x02, 0x74, 0x65, 0x00, \\ r - - - - t e -
1616 0x00, 0x00, 0x07, 0x74, 0x72, 0x61, 0x69, 0x6c, \\ - - - t r a i l
1617 0x65, 0x72, 0x00, 0x00, 0x00, 0x11, 0x74, 0x72, \\ e r - - - - t r
1618 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x65, \\ a n s f e r - e
1619 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00, \\ n c o d i n g -
1620
1621
1622
1623Belshe & Peon Expires August 4, 2012 [Page 29]
1624
1625Internet-Draft SPDY Feb 2012
1626
1627
1628 0x00, 0x00, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61, \\ - - - u p g r a
1629 0x64, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x73, \\ d e - - - - u s
1630 0x65, 0x72, 0x2d, 0x61, 0x67, 0x65, 0x6e, 0x74, \\ e r - a g e n t
1631 0x00, 0x00, 0x00, 0x04, 0x76, 0x61, 0x72, 0x79, \\ - - - - v a r y
1632 0x00, 0x00, 0x00, 0x03, 0x76, 0x69, 0x61, 0x00, \\ - - - - v i a -
1633 0x00, 0x00, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69, \\ - - - w a r n i
1634 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, 0x77, 0x77, \\ n g - - - - w w
1635 0x77, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, \\ w - a u t h e n
1636 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, \\ t i c a t e - -
1637 0x00, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, \\ - - m e t h o d
1638 0x00, 0x00, 0x00, 0x03, 0x67, 0x65, 0x74, 0x00, \\ - - - - g e t -
1639 0x00, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, \\ - - - s t a t u
1640 0x73, 0x00, 0x00, 0x00, 0x06, 0x32, 0x30, 0x30, \\ s - - - - 2 0 0
1641 0x20, 0x4f, 0x4b, 0x00, 0x00, 0x00, 0x07, 0x76, \\ - O K - - - - v
1642 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00, \\ e r s i o n - -
1643 0x00, 0x08, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, \\ - - H T T P - 1
1644 0x2e, 0x31, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72, \\ - 1 - - - - u r
1645 0x6c, 0x00, 0x00, 0x00, 0x06, 0x70, 0x75, 0x62, \\ l - - - - p u b
1646 0x6c, 0x69, 0x63, 0x00, 0x00, 0x00, 0x0a, 0x73, \\ l i c - - - - s
1647 0x65, 0x74, 0x2d, 0x63, 0x6f, 0x6f, 0x6b, 0x69, \\ e t - c o o k i
1648 0x65, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x65, 0x65, \\ e - - - - k e e
1649 0x70, 0x2d, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x00, \\ p - a l i v e -
1650 0x00, 0x00, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, \\ - - - o r i g i
1651 0x6e, 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x32, \\ n 1 0 0 1 0 1 2
1652 0x30, 0x31, 0x32, 0x30, 0x32, 0x32, 0x30, 0x35, \\ 0 1 2 0 2 2 0 5
1653 0x32, 0x30, 0x36, 0x33, 0x30, 0x30, 0x33, 0x30, \\ 2 0 6 3 0 0 3 0
1654 0x32, 0x33, 0x30, 0x33, 0x33, 0x30, 0x34, 0x33, \\ 2 3 0 3 3 0 4 3
1655 0x30, 0x35, 0x33, 0x30, 0x36, 0x33, 0x30, 0x37, \\ 0 5 3 0 6 3 0 7
1656 0x34, 0x30, 0x32, 0x34, 0x30, 0x35, 0x34, 0x30, \\ 4 0 2 4 0 5 4 0
1657 0x36, 0x34, 0x30, 0x37, 0x34, 0x30, 0x38, 0x34, \\ 6 4 0 7 4 0 8 4
1658 0x30, 0x39, 0x34, 0x31, 0x30, 0x34, 0x31, 0x31, \\ 0 9 4 1 0 4 1 1
1659 0x34, 0x31, 0x32, 0x34, 0x31, 0x33, 0x34, 0x31, \\ 4 1 2 4 1 3 4 1
1660 0x34, 0x34, 0x31, 0x35, 0x34, 0x31, 0x36, 0x34, \\ 4 4 1 5 4 1 6 4
1661 0x31, 0x37, 0x35, 0x30, 0x32, 0x35, 0x30, 0x34, \\ 1 7 5 0 2 5 0 4
1662 0x35, 0x30, 0x35, 0x32, 0x30, 0x33, 0x20, 0x4e, \\ 5 0 5 2 0 3 - N
1663 0x6f, 0x6e, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x6f, \\ o n - A u t h o
1664 0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, \\ r i t a t i v e
1665 0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, \\ - I n f o r m a
1666 0x74, 0x69, 0x6f, 0x6e, 0x32, 0x30, 0x34, 0x20, \\ t i o n 2 0 4 -
1667 0x4e, 0x6f, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65, \\ N o - C o n t e
1668 0x6e, 0x74, 0x33, 0x30, 0x31, 0x20, 0x4d, 0x6f, \\ n t 3 0 1 - M o
1669 0x76, 0x65, 0x64, 0x20, 0x50, 0x65, 0x72, 0x6d, \\ v e d - P e r m
1670 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x34, \\ a n e n t l y 4
1671 0x30, 0x30, 0x20, 0x42, 0x61, 0x64, 0x20, 0x52, \\ 0 0 - B a d - R
1672 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x34, 0x30, \\ e q u e s t 4 0
1673 0x31, 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68, \\ 1 - U n a u t h
1674 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x34, 0x30, \\ o r i z e d 4 0
1675 0x33, 0x20, 0x46, 0x6f, 0x72, 0x62, 0x69, 0x64, \\ 3 - F o r b i d
1676
1677
1678
1679Belshe & Peon Expires August 4, 2012 [Page 30]
1680
1681Internet-Draft SPDY Feb 2012
1682
1683
1684 0x64, 0x65, 0x6e, 0x34, 0x30, 0x34, 0x20, 0x4e, \\ d e n 4 0 4 - N
1685 0x6f, 0x74, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, \\ o t - F o u n d
1686 0x35, 0x30, 0x30, 0x20, 0x49, 0x6e, 0x74, 0x65, \\ 5 0 0 - I n t e
1687 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72, \\ r n a l - S e r
1688 0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6f, \\ v e r - E r r o
1689 0x72, 0x35, 0x30, 0x31, 0x20, 0x4e, 0x6f, 0x74, \\ r 5 0 1 - N o t
1690 0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, \\ - I m p l e m e
1691 0x6e, 0x74, 0x65, 0x64, 0x35, 0x30, 0x33, 0x20, \\ n t e d 5 0 3 -
1692 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, \\ S e r v i c e -
1693 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, \\ U n a v a i l a
1694 0x62, 0x6c, 0x65, 0x4a, 0x61, 0x6e, 0x20, 0x46, \\ b l e J a n - F
1695 0x65, 0x62, 0x20, 0x4d, 0x61, 0x72, 0x20, 0x41, \\ e b - M a r - A
1696 0x70, 0x72, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x4a, \\ p r - M a y - J
1697 0x75, 0x6e, 0x20, 0x4a, 0x75, 0x6c, 0x20, 0x41, \\ u n - J u l - A
1698 0x75, 0x67, 0x20, 0x53, 0x65, 0x70, 0x74, 0x20, \\ u g - S e p t -
1699 0x4f, 0x63, 0x74, 0x20, 0x4e, 0x6f, 0x76, 0x20, \\ O c t - N o v -
1700 0x44, 0x65, 0x63, 0x20, 0x30, 0x30, 0x3a, 0x30, \\ D e c - 0 0 - 0
1701 0x30, 0x3a, 0x30, 0x30, 0x20, 0x4d, 0x6f, 0x6e, \\ 0 - 0 0 - M o n
1702 0x2c, 0x20, 0x54, 0x75, 0x65, 0x2c, 0x20, 0x57, \\ - - T u e - - W
1703 0x65, 0x64, 0x2c, 0x20, 0x54, 0x68, 0x75, 0x2c, \\ e d - - T h u -
1704 0x20, 0x46, 0x72, 0x69, 0x2c, 0x20, 0x53, 0x61, \\ - F r i - - S a
1705 0x74, 0x2c, 0x20, 0x53, 0x75, 0x6e, 0x2c, 0x20, \\ t - - S u n - -
1706 0x47, 0x4d, 0x54, 0x63, 0x68, 0x75, 0x6e, 0x6b, \\ G M T c h u n k
1707 0x65, 0x64, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, \\ e d - t e x t -
1708 0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x69, 0x6d, 0x61, \\ h t m l - i m a
1709 0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67, 0x2c, 0x69, \\ g e - p n g - i
1710 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x6a, 0x70, 0x67, \\ m a g e - j p g
1711 0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, \\ - i m a g e - g
1712 0x69, 0x66, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, \\ i f - a p p l i
1713 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, \\ c a t i o n - x
1714 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, \\ m l - a p p l i
1715 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, \\ c a t i o n - x
1716 0x68, 0x74, 0x6d, 0x6c, 0x2b, 0x78, 0x6d, 0x6c, \\ h t m l - x m l
1717 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, \\ - t e x t - p l
1718 0x61, 0x69, 0x6e, 0x2c, 0x74, 0x65, 0x78, 0x74, \\ a i n - t e x t
1719 0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, \\ - j a v a s c r
1720 0x69, 0x70, 0x74, 0x2c, 0x70, 0x75, 0x62, 0x6c, \\ i p t - p u b l
1721 0x69, 0x63, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, \\ i c p r i v a t
1722 0x65, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65, \\ e m a x - a g e
1723 0x3d, 0x67, 0x7a, 0x69, 0x70, 0x2c, 0x64, 0x65, \\ - g z i p - d e
1724 0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64, \\ f l a t e - s d
1725 0x63, 0x68, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, \\ c h c h a r s e
1726 0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x63, \\ t - u t f - 8 c
1727 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x69, \\ h a r s e t - i
1728 0x73, 0x6f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d, \\ s o - 8 8 5 9 -
1729 0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x2c, 0x2a, \\ 1 - u t f - - -
1730 0x2c, 0x65, 0x6e, 0x71, 0x3d, 0x30, 0x2e \\ - e n q - 0 -
1731 };
1732
1733
1734
1735Belshe & Peon Expires August 4, 2012 [Page 31]
1736
1737Internet-Draft SPDY Feb 2012
1738
1739
1740 The entire contents of the name/value header block is compressed
1741 using zlib. There is a single zlib stream for all name value pairs
1742 in one direction on a connection. SPDY uses a SYNC_FLUSH between
1743 each compressed frame.
1744
1745 Implementation notes: the compression engine can be tuned to favor
1746 speed or size. Optimizing for size increases memory use and CPU
1747 consumption. Because header blocks are generally small, implementors
1748 may want to reduce the window-size of the compression engine from the
1749 default 15bits (a 32KB window) to more like 11bits (a 2KB window).
1750 The exact setting is chosen by the compressor, the decompressor will
1751 work with any setting.
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791Belshe & Peon Expires August 4, 2012 [Page 32]
1792
1793Internet-Draft SPDY Feb 2012
1794
1795
17963. HTTP Layering over SPDY
1797
1798 SPDY is intended to be as compatible as possible with current web-
1799 based applications. This means that, from the perspective of the
1800 server business logic or application API, the features of HTTP are
1801 unchanged. To achieve this, all of the application request and
1802 response header semantics are preserved, although the syntax of
1803 conveying those semantics has changed. Thus, the rules from the
1804 HTTP/1.1 specification in RFC2616 [RFC2616] apply with the changes in
1805 the sections below.
1806
18073.1. Connection Management
1808
1809 Clients SHOULD NOT open more than one SPDY session to a given origin
1810 [RFC6454] concurrently.
1811
1812 Note that it is possible for one SPDY session to be finishing (e.g. a
1813 GOAWAY message has been sent, but not all streams have finished),
1814 while another SPDY session is starting.
1815
18163.1.1. Use of GOAWAY
1817
1818 SPDY provides a GOAWAY message which can be used when closing a
1819 connection from either the client or server. Without a server GOAWAY
1820 message, HTTP has a race condition where the client sends a request
1821 (a new SYN_STREAM) just as the server is closing the connection, and
1822 the client cannot know if the server received the stream or not. By
1823 using the last-stream-id in the GOAWAY, servers can indicate to the
1824 client if a request was processed or not.
1825
1826 Note that some servers will choose to send the GOAWAY and immediately
1827 terminate the connection without waiting for active streams to
1828 finish. The client will be able to determine this because SPDY
1829 streams are determinstically closed. This abrupt termination will
1830 force the client to heuristically decide whether to retry the pending
1831 requests. Clients always need to be capable of dealing with this
1832 case because they must deal with accidental connection termination
1833 cases, which are the same as the server never having sent a GOAWAY.
1834
1835 More sophisticated servers will use GOAWAY to implement a graceful
1836 teardown. They will send the GOAWAY and provide some time for the
1837 active streams to finish before terminating the connection.
1838
1839 If a SPDY client closes the connection, it should also send a GOAWAY
1840 message. This allows the server to know if any server-push streams
1841 were received by the client.
1842
1843 If the endpoint closing the connection has not received any
1844
1845
1846
1847Belshe & Peon Expires August 4, 2012 [Page 33]
1848
1849Internet-Draft SPDY Feb 2012
1850
1851
1852 SYN_STREAMs from the remote, the GOAWAY will contain a last-stream-id
1853 of 0.
1854
18553.2. HTTP Request/Response
1856
18573.2.1. Request
1858
1859 The client initiates a request by sending a SYN_STREAM frame. For
1860 requests which do not contain a body, the SYN_STREAM frame MUST set
1861 the FLAG_FIN, indicating that the client intends to send no further
1862 data on this stream. For requests which do contain a body, the
1863 SYN_STREAM will not contain the FLAG_FIN, and the body will follow
1864 the SYN_STREAM in a series of DATA frames. The last DATA frame will
1865 set the FLAG_FIN to indicate the end of the body.
1866
1867 The SYN_STREAM Name/Value section will contain all of the HTTP
1868 headers which are associated with an HTTP request. The header block
1869 in SPDY is mostly unchanged from today's HTTP header block, with the
1870 following differences:
1871
1872 The first line of the request is unfolded into name/value pairs
1873 like other HTTP headers and MUST be present:
1874
1875 ":method" - the HTTP method for this request (e.g. "GET",
1876 "POST", "HEAD", etc)
1877
1878 ":path" - the url-path for this url with "/" prefixed. (See
1879 RFC1738 [RFC1738]). For example, for
1880 "http://www.google.com/search?q=dogs" the path would be
1881 "/search?q=dogs".
1882
1883 ":version" - the HTTP version of this request (e.g.
1884 "HTTP/1.1")
1885
1886 In addition, the following two name/value pairs must also be
1887 present in every request:
1888
1889 ":host" - the hostport (See RFC1738 [RFC1738]) portion of the
1890 URL for this request (e.g. "www.google.com:1234"). This header
1891 is the same as the HTTP 'Host' header.
1892
1893 ":scheme" - the scheme portion of the URL for this request
1894 (e.g. "https"))
1895
1896 Header names are all lowercase.
1897
1898 The Connection, Host, Keep-Alive, Proxy-Connection, and Transfer-
1899 Encoding headers are not valid and MUST not be sent.
1900
1901
1902
1903Belshe & Peon Expires August 4, 2012 [Page 34]
1904
1905Internet-Draft SPDY Feb 2012
1906
1907
1908 User-agents MUST support gzip compression. Regardless of the
1909 Accept-Encoding sent by the user-agent, the server may always send
1910 content encoded with gzip or deflate encoding.
1911
1912 If a server receives a request where the sum of the data frame
1913 payload lengths does not equal the size of the Content-Length
1914 header, the server MUST return a 400 (Bad Request) error.
1915
1916 POST-specific changes:
1917
1918 Although POSTs are inherently chunked, POST requests SHOULD
1919 also be accompanied by a Content-Length header. There are two
1920 reasons for this: First, it assists with upload progress meters
1921 for an improved user experience. But second, we know from
1922 early versions of SPDY that failure to send a content length
1923 header is incompatible with many existing HTTP server
1924 implementations. Existing user-agents do not omit the Content-
1925 Length header, and server implementations have come to depend
1926 upon this.
1927
1928 The user-agent is free to prioritize requests as it sees fit. If the
1929 user-agent cannot make progress without receiving a resource, it
1930 should attempt to raise the priority of that resource. Resources
1931 such as images, SHOULD generally use the lowest priority.
1932
1933 If a client sends a SYN_STREAM without all of the method, host, path,
1934 scheme, and version headers, the server MUST reply with a HTTP 400
1935 Bad Request reply.
1936
19373.2.2. Response
1938
1939 The server responds to a client request with a SYN_REPLY frame.
1940 Symmetric to the client's upload stream, server will send data after
1941 the SYN_REPLY frame via a series of DATA frames, and the last data
1942 frame will contain the FLAG_FIN to indicate successful end-of-stream.
1943 If a response (like a 202 or 204 response) contains no body, the
1944 SYN_REPLY frame may contain the FLAG_FIN flag to indicate no further
1945 data will be sent on the stream.
1946
1947 The response status line is unfolded into name/value pairs like
1948 other HTTP headers and must be present:
1949
1950 ":status" - The HTTP response status code (e.g. "200" or "200
1951 OK")
1952
1953 ":version" - The HTTP response version (e.g. "HTTP/1.1")
1954
1955
1956
1957
1958
1959Belshe & Peon Expires August 4, 2012 [Page 35]
1960
1961Internet-Draft SPDY Feb 2012
1962
1963
1964 All header names must be lowercase.
1965
1966 The Connection, Keep-Alive, Proxy-Connection, and Transfer-
1967 Encoding headers are not valid and MUST not be sent.
1968
1969 Responses MAY be accompanied by a Content-Length header for
1970 advisory purposes. (e.g. for UI progress meters)
1971
1972 If a client receives a response where the sum of the data frame
1973 payload lengths does not equal the size of the Content-Length
1974 header, the client MUST ignore the content length header.
1975
1976 If a client receives a SYN_REPLY without a status or without a
1977 version header, the client must reply with a RST_STREAM frame
1978 indicating a PROTOCOL ERROR.
1979
19803.2.3. Authentication
1981
1982 When a client sends a request to an origin server that requires
1983 authentication, the server can reply with a "401 Unauthorized"
1984 response, and include a WWW-Authenticate challenge header that
1985 defines the authentication scheme to be used. The client then
1986 retries the request with an Authorization header appropriate to the
1987 specified authentication scheme.
1988
1989 There are four options for proxy authentication, Basic, Digest, NTLM
1990 and Negotiate (SPNEGO). The first two options were defined in
1991 RFC2617 [RFC2617], and are stateless. The second two options were
1992 developed by Microsoft and specified in RFC4559 [RFC4559], and are
1993 stateful; otherwise known as multi-round authentication, or
1994 connection authentication.
1995
19963.2.3.1. Stateless Authentication
1997
1998 Stateless Authentication over SPDY is identical to how it is
1999 performed over HTTP. If multiple SPDY streams are concurrently sent
2000 to a single server, each will authenticate independently, similar to
2001 how two HTTP connections would independently authenticate to a proxy
2002 server.
2003
20043.2.3.2. Stateful Authentication
2005
2006 Unfortunately, the stateful authentication mechanisms were
2007 implemented and defined in a such a way that directly violates
2008 RFC2617 - they do not include a "realm" as part of the request. This
2009 is problematic in SPDY because it makes it impossible for a client to
2010 disambiguate two concurrent server authentication challenges.
2011
2012
2013
2014
2015Belshe & Peon Expires August 4, 2012 [Page 36]
2016
2017Internet-Draft SPDY Feb 2012
2018
2019
2020 To deal with this case, SPDY servers using Stateful Authentication
2021 MUST implement one of two changes:
2022
2023 Servers can add a "realm=<desired realm>" header so that the two
2024 authentication requests can be disambiguated and run concurrently.
2025 Unfortunately, given how these mechanisms work, this is probably
2026 not practical.
2027
2028 Upon sending the first stateful challenge response, the server
2029 MUST buffer and defer all further frames which are not part of
2030 completing the challenge until the challenge has completed.
2031 Completing the authentication challenge may take multiple round
2032 trips. Once the client receives a "401 Authenticate" response for
2033 a stateful authentication type, it MUST stop sending new requests
2034 to the server until the authentication has completed by receiving
2035 a non-401 response on at least one stream.
2036
20373.3. Server Push Transactions
2038
2039 SPDY enables a server to send multiple replies to a client for a
2040 single request. The rationale for this feature is that sometimes a
2041 server knows that it will need to send multiple resources in response
2042 to a single request. Without server push features, the client must
2043 first download the primary resource, then discover the secondary
2044 resource(s), and request them. Pushing of resources avoids the
2045 round-trip delay, but also creates a potential race where a server
2046 can be pushing content which a user-agent is in the process of
2047 requesting. The following mechanics attempt to prevent the race
2048 condition while enabling the performance benefit.
2049
2050 Browsers receiving a pushed response MUST validate that the server is
2051 authorized to push the URL using the browser same-origin [RFC6454]
2052 policy. For example, a SPDY connection to www.foo.com is generally
2053 not permitted to push a response for www.evil.com.
2054
2055 If the browser accepts a pushed response (e.g. it does not send a
2056 RST_STREAM), the browser MUST attempt to cache the pushed response in
2057 same way that it would cache any other response. This means
2058 validating the response headers and inserting into the disk cache.
2059
2060 Because pushed responses have no request, they have no request
2061 headers associated with them. At the framing layer, SPDY pushed
2062 streams contain an "associated-stream-id" which indicates the
2063 requested stream for which the pushed stream is related. The pushed
2064 stream inherits all of the headers from the associated-stream-id with
2065 the exception of ":host", ":scheme", and ":path", which are provided
2066 as part of the pushed response stream headers. The browser MUST
2067 store these inherited and implied request headers with the cached
2068
2069
2070
2071Belshe & Peon Expires August 4, 2012 [Page 37]
2072
2073Internet-Draft SPDY Feb 2012
2074
2075
2076 resource.
2077
2078 Implementation note: With server push, it is theoretically possible
2079 for servers to push unreasonable amounts of content or resources to
2080 the user-agent. Browsers MUST implement throttles to protect against
2081 unreasonable push attacks.
2082
20833.3.1. Server implementation
2084
2085 When the server intends to push a resource to the user-agent, it
2086 opens a new stream by sending a unidirectional SYN_STREAM. The
2087 SYN_STREAM MUST include an Associated-To-Stream-ID, and MUST set the
2088 FLAG_UNIDIRECTIONAL flag. The SYN_STREAM MUST include headers for
2089 ":scheme", ":host", ":path", which represent the URL for the resource
2090 being pushed. Subsequent headers may follow in HEADERS frames. The
2091 purpose of the association is so that the user-agent can
2092 differentiate which request induced the pushed stream; without it, if
2093 the user-agent had two tabs open to the same page, each pushing
2094 unique content under a fixed URL, the user-agent would not be able to
2095 differentiate the requests.
2096
2097 The Associated-To-Stream-ID must be the ID of an existing, open
2098 stream. The reason for this restriction is to have a clear endpoint
2099 for pushed content. If the user-agent requested a resource on stream
2100 11, the server replies on stream 11. It can push any number of
2101 additional streams to the client before sending a FLAG_FIN on stream
2102 11. However, once the originating stream is closed no further push
2103 streams may be associated with it. The pushed streams do not need to
2104 be closed (FIN set) before the originating stream is closed, they
2105 only need to be created before the originating stream closes.
2106
2107 It is illegal for a server to push a resource with the Associated-To-
2108 Stream-ID of 0.
2109
2110 To minimize race conditions with the client, the SYN_STREAM for the
2111 pushed resources MUST be sent prior to sending any content which
2112 could allow the client to discover the pushed resource and request
2113 it.
2114
2115 The server MUST only push resources which would have been returned
2116 from a GET request.
2117
2118 Note: If the server does not have all of the Name/Value Response
2119 headers available at the time it issues the HEADERS frame for the
2120 pushed resource, it may later use an additional HEADERS frame to
2121 augment the name/value pairs to be associated with the pushed stream.
2122 The subsequent HEADERS frame(s) must not contain a header for
2123 ':host', ':scheme', or ':path' (e.g. the server can't change the
2124
2125
2126
2127Belshe & Peon Expires August 4, 2012 [Page 38]
2128
2129Internet-Draft SPDY Feb 2012
2130
2131
2132 identity of the resource to be pushed). The HEADERS frame must not
2133 contain duplicate headers with a previously sent HEADERS frame. The
2134 server must send a HEADERS frame including the scheme/host/port
2135 headers before sending any data frames on the stream.
2136
21373.3.2. Client implementation
2138
2139 When fetching a resource the client has 3 possibilities:
2140
2141 the resource is not being pushed
2142
2143 the resource is being pushed, but the data has not yet arrived
2144
2145 the resource is being pushed, and the data has started to arrive
2146
2147 When a SYN_STREAM and HEADERS frame which contains an Associated-To-
2148 Stream-ID is received, the client must not issue GET requests for the
2149 resource in the pushed stream, and instead wait for the pushed stream
2150 to arrive.
2151
2152 If a client receives a server push stream with stream-id 0, it MUST
2153 issue a session error (Section 2.4.1) with the status code
2154 PROTOCOL_ERROR.
2155
2156 When a client receives a SYN_STREAM from the server without a the
2157 ':host', ':scheme', and ':path' headers in the Name/Value section, it
2158 MUST reply with a RST_STREAM with error code HTTP_PROTOCOL_ERROR.
2159
2160 To cancel individual server push streams, the client can issue a
2161 stream error (Section 2.4.2) with error code CANCEL. Upon receipt,
2162 the server MUST stop sending on this stream immediately (this is an
2163 Abrupt termination).
2164
2165 To cancel all server push streams related to a request, the client
2166 may issue a stream error (Section 2.4.2) with error code CANCEL on
2167 the associated-stream-id. By cancelling that stream, the server MUST
2168 immediately stop sending frames for any streams with
2169 in-association-to for the original stream.
2170
2171 If the server sends a HEADER frame containing duplicate headers with
2172 a previous HEADERS frame for the same stream, the client must issue a
2173 stream error (Section 2.4.2) with error code PROTOCOL ERROR.
2174
2175 If the server sends a HEADERS frame after sending a data frame for
2176 the same stream, the client MAY ignore the HEADERS frame. Ignoring
2177 the HEADERS frame after a data frame prevents handling of HTTP's
2178 trailing headers
2179 (http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.40).
2180
2181
2182
2183Belshe & Peon Expires August 4, 2012 [Page 39]
2184
2185Internet-Draft SPDY Feb 2012
2186
2187
21884. Design Rationale and Notes
2189
2190 Authors' notes: The notes in this section have no bearing on the SPDY
2191 protocol as specified within this document, and none of these notes
2192 should be considered authoritative about how the protocol works.
2193 However, these notes may prove useful in future debates about how to
2194 resolve protocol ambiguities or how to evolve the protocol going
2195 forward. They may be removed before the final draft.
2196
21974.1. Separation of Framing Layer and Application Layer
2198
2199 Readers may note that this specification sometimes blends the framing
2200 layer (Section 2) with requirements of a specific application - HTTP
2201 (Section 3). This is reflected in the request/response nature of the
2202 streams, the definition of the HEADERS and compression contexts which
2203 are very similar to HTTP, and other areas as well.
2204
2205 This blending is intentional - the primary goal of this protocol is
2206 to create a low-latency protocol for use with HTTP. Isolating the
2207 two layers is convenient for description of the protocol and how it
2208 relates to existing HTTP implementations. However, the ability to
2209 reuse the SPDY framing layer is a non goal.
2210
22114.2. Error handling - Framing Layer
2212
2213 Error handling at the SPDY layer splits errors into two groups: Those
2214 that affect an individual SPDY stream, and those that do not.
2215
2216 When an error is confined to a single stream, but general framing is
2217 in tact, SPDY attempts to use the RST_STREAM as a mechanism to
2218 invalidate the stream but move forward without aborting the
2219 connection altogether.
2220
2221 For errors occuring outside of a single stream context, SPDY assumes
2222 the entire session is hosed. In this case, the endpoint detecting
2223 the error should initiate a connection close.
2224
22254.3. One Connection Per Domain
2226
2227 SPDY attempts to use fewer connections than other protocols have
2228 traditionally used. The rationale for this behavior is because it is
2229 very difficult to provide a consistent level of service (e.g. TCP
2230 slow-start), prioritization, or optimal compression when the client
2231 is connecting to the server through multiple channels.
2232
2233 Through lab measurements, we have seen consistent latency benefits by
2234 using fewer connections from the client. The overall number of
2235 packets sent by SPDY can be as much as 40% less than HTTP. Handling
2236
2237
2238
2239Belshe & Peon Expires August 4, 2012 [Page 40]
2240
2241Internet-Draft SPDY Feb 2012
2242
2243
2244 large numbers of concurrent connections on the server also does
2245 become a scalability problem, and SPDY reduces this load.
2246
2247 The use of multiple connections is not without benefit, however.
2248 Because SPDY multiplexes multiple, independent streams onto a single
2249 stream, it creates a potential for head-of-line blocking problems at
2250 the transport level. In tests so far, the negative effects of head-
2251 of-line blocking (especially in the presence of packet loss) is
2252 outweighed by the benefits of compression and prioritization.
2253
22544.4. Fixed vs Variable Length Fields
2255
2256 SPDY favors use of fixed length 32bit fields in cases where smaller,
2257 variable length encodings could have been used. To some, this seems
2258 like a tragic waste of bandwidth. SPDY choses the simple encoding
2259 for speed and simplicity.
2260
2261 The goal of SPDY is to reduce latency on the network. The overhead
2262 of SPDY frames is generally quite low. Each data frame is only an 8
2263 byte overhead for a 1452 byte payload (~0.6%). At the time of this
2264 writing, bandwidth is already plentiful, and there is a strong trend
2265 indicating that bandwidth will continue to increase. With an average
2266 worldwide bandwidth of 1Mbps, and assuming that a variable length
2267 encoding could reduce the overhead by 50%, the latency saved by using
2268 a variable length encoding would be less than 100 nanoseconds. More
2269 interesting are the effects when the larger encodings force a packet
2270 boundary, in which case a round-trip could be induced. However, by
2271 addressing other aspects of SPDY and TCP interactions, we believe
2272 this is completely mitigated.
2273
22744.5. Compression Context(s)
2275
2276 When isolating the compression contexts used for communicating with
2277 multiple origins, we had a few choices to make. We could have
2278 maintained a map (or list) of compression contexts usable for each
2279 origin. The basic case is easy - each HEADERS frame would need to
2280 identify the context to use for that frame. However, compression
2281 contexts are not cheap, so the lifecycle of each context would need
2282 to be bounded. For proxy servers, where we could churn through many
2283 contexts, this would be a concern. We considered using a static set
2284 of contexts, say 16 of them, which would bound the memory use. We
2285 also considered dynamic contexts, which could be created on the fly,
2286 and would need to be subsequently destroyed. All of these are
2287 complicated, and ultimately we decided that such a mechanism creates
2288 too many problems to solve.
2289
2290 Alternatively, we've chosen the simple approach, which is to simply
2291 provide a flag for resetting the compression context. For the common
2292
2293
2294
2295Belshe & Peon Expires August 4, 2012 [Page 41]
2296
2297Internet-Draft SPDY Feb 2012
2298
2299
2300 case (no proxy), this fine because most requests are to the same
2301 origin and we never need to reset the context. For cases where we
2302 are using two different origins over a single SPDY session, we simply
2303 reset the compression state between each transition.
2304
23054.6. Unidirectional streams
2306
2307 Many readers notice that unidirectional streams are both a bit
2308 confusing in concept and also somewhat redundant. If the recipient
2309 of a stream doesn't wish to send data on a stream, it could simply
2310 send a SYN_REPLY with the FLAG_FIN bit set. The FLAG_UNIDIRECTIONAL
2311 is, therefore, not necessary.
2312
2313 It is true that we don't need the UNIDIRECTIONAL markings. It is
2314 added because it avoids the recipient of pushed streams from needing
2315 to send a set of empty frames (e.g. the SYN_STREAM w/ FLAG_FIN) which
2316 otherwise serve no purpose.
2317
23184.7. Data Compression
2319
2320 Generic compression of data portion of the streams (as opposed to
2321 compression of the headers) without knowing the content of the stream
2322 is redundant. There is no value in compressing a stream which is
2323 already compressed. Because of this, SPDY does allow data
2324 compression to be optional. We included it because study of existing
2325 websites shows that many sites are not using compression as they
2326 should, and users suffer because of it. We wanted a mechanism where,
2327 at the SPDY layer, site administrators could simply force compression
2328 - it is better to compress twice than to not compress.
2329
2330 Overall, however, with this feature being optional and sometimes
2331 redundant, it is unclear if it is useful at all. We will likely
2332 remove it from the specification.
2333
23344.8. Server Push
2335
2336 A subtle but important point is that server push streams must be
2337 declared before the associated stream is closed. The reason for this
2338 is so that proxies have a lifetime for which they can discard
2339 information about previous streams. If a pushed stream could
2340 associate itself with an already-closed stream, then endpoints would
2341 not have a specific lifecycle for when they could disavow knowledge
2342 of the streams which went before.
2343
2344
2345
2346
2347
2348
2349
2350
2351Belshe & Peon Expires August 4, 2012 [Page 42]
2352
2353Internet-Draft SPDY Feb 2012
2354
2355
23565. Security Considerations
2357
23585.1. Use of Same-origin constraints
2359
2360 This specification uses the same-origin policy [RFC6454] in all cases
2361 where verification of content is required.
2362
23635.2. HTTP Headers and SPDY Headers
2364
2365 At the application level, HTTP uses name/value pairs in its headers.
2366 Because SPDY merges the existing HTTP headers with SPDY headers,
2367 there is a possibility that some HTTP applications already use a
2368 particular header name. To avoid any conflicts, all headers
2369 introduced for layering HTTP over SPDY are prefixed with ":". ":" is
2370 not a valid sequence in HTTP header naming, preventing any possible
2371 conflict.
2372
23735.3. Cross-Protocol Attacks
2374
2375 By utilizing TLS, we believe that SPDY introduces no new cross-
2376 protocol attacks. TLS encrypts the contents of all transmission
2377 (except the handshake itself), making it difficult for attackers to
2378 control the data which could be used in a cross-protocol attack.
2379
23805.4. Server Push Implicit Headers
2381
2382 Pushed resources do not have an associated request. In order for
2383 existing HTTP cache control validations (such as the Vary header) to
2384 work, however, all cached resources must have a set of request
2385 headers. For this reason, browsers MUST be careful to inherit
2386 request headers from the associated stream for the push. This
2387 includes the 'Cookie' header.
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407Belshe & Peon Expires August 4, 2012 [Page 43]
2408
2409Internet-Draft SPDY Feb 2012
2410
2411
24126. Privacy Considerations
2413
24146.1. Long Lived Connections
2415
2416 SPDY aims to keep connections open longer between clients and servers
2417 in order to reduce the latency when a user makes a request. The
2418 maintenance of these connections over time could be used to expose
2419 private information. For example, a user using a browser hours after
2420 the previous user stopped using that browser may be able to learn
2421 about what the previous user was doing. This is a problem with HTTP
2422 in its current form as well, however the short lived connections make
2423 it less of a risk.
2424
24256.2. SETTINGS frame
2426
2427 The SPDY SETTINGS frame allows servers to store out-of-band
2428 transmitted information about the communication between client and
2429 server on the client. Although this is intended only to be used to
2430 reduce latency, renegade servers could use it as a mechanism to store
2431 identifying information about the client in future requests.
2432
2433 Clients implementing privacy modes, such as Google Chrome's
2434 "incognito mode", may wish to disable client-persisted SETTINGS
2435 storage.
2436
2437 Clients MUST clear persisted SETTINGS information when clearing the
2438 cookies.
2439
2440 TODO: Put range maximums on each type of setting to limit
2441 inappropriate uses.
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463Belshe & Peon Expires August 4, 2012 [Page 44]
2464
2465Internet-Draft SPDY Feb 2012
2466
2467
24687. Incompatibilities with SPDY draft #2
2469
2470 Here is a list of the major changes between this draft and draft #2.
2471
2472 Addition of flow control
2473
2474 Increased 16 bit length fields in SYN_STREAM and SYN_REPLY to 32
2475 bits.
2476
2477 Changed definition of compression for DATA frames
2478
2479 Updated compression dictionary
2480
2481 Fixed off-by-one on the compression dictionary for headers
2482
2483 Increased priority field from 2bits to 3bits.
2484
2485 Removed NOOP frame
2486
2487 Split the request "url" into "scheme", "host", and "path"
2488
2489 Added the requirement that POSTs contain content-length.
2490
2491 Removed wasted 16bits of unused space from the end of the
2492 SYN_REPLY and HEADERS frames.
2493
2494 Fixed bug: Priorities were described backward (0 was lowest
2495 instead of highest).
2496
2497 Fixed bug: Name/Value header counts were duplicated in both the
2498 Name Value header block and also the containing frame.
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519Belshe & Peon Expires August 4, 2012 [Page 45]
2520
2521Internet-Draft SPDY Feb 2012
2522
2523
25248. Requirements Notation
2525
2526 The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
2527 "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
2528 document are to be interpreted as described in RFC 2119 [RFC2119].
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575Belshe & Peon Expires August 4, 2012 [Page 46]
2576
2577Internet-Draft SPDY Feb 2012
2578
2579
25809. Acknowledgements
2581
2582 Many individuals have contributed to the design and evolution of
2583 SPDY: Adam Langley, Wan-Teh Chang, Jim Morrison, Mark Nottingham,
2584 Alyssa Wilk, Costin Manolache, William Chan, Vitaliy Lvin, Joe Chan,
2585 Adam Barth, Ryan Hamilton, Gavin Peters, Kent Alstad, Kevin Lindsay,
2586 Paul Amer, Fan Yang, Jonathan Leighton
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631Belshe & Peon Expires August 4, 2012 [Page 47]
2632
2633Internet-Draft SPDY Feb 2012
2634
2635
263610. Normative References
2637
2638 [RFC0793] Postel, J., "Transmission Control Protocol", STD 7,
2639 RFC 793, September 1981.
2640
2641 [RFC1738] Berners-Lee, T., Masinter, L., and M. McCahill, "Uniform
2642 Resource Locators (URL)", RFC 1738, December 1994.
2643
2644 [RFC1950] Deutsch, L. and J-L. Gailly, "ZLIB Compressed Data Format
2645 Specification version 3.3", RFC 1950, May 1996.
2646
2647 [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
2648 Requirement Levels", BCP 14, RFC 2119, March 1997.
2649
2650 [RFC2285] Mandeville, R., "Benchmarking Terminology for LAN
2651 Switching Devices", RFC 2285, February 1998.
2652
2653 [RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, H.,
2654 Masinter, L., Leach, P., and T. Berners-Lee, "Hypertext
2655 Transfer Protocol -- HTTP/1.1", RFC 2616, June 1999.
2656
2657 [RFC2617] Franks, J., Hallam-Baker, P., Hostetler, J., Lawrence, S.,
2658 Leach, P., Luotonen, A., and L. Stewart, "HTTP
2659 Authentication: Basic and Digest Access Authentication",
2660 RFC 2617, June 1999.
2661
2662 [RFC4559] Jaganathan, K., Zhu, L., and J. Brezak, "SPNEGO-based
2663 Kerberos and NTLM HTTP Authentication in Microsoft
2664 Windows", RFC 4559, June 2006.
2665
2666 [RFC4366] Blake-Wilson, S., Nystrom, M., Hopwood, D., Mikkelsen, J.,
2667 and T. Wright, "Transport Layer Security (TLS)
2668 Extensions", RFC 4366, April 2006.
2669
2670 [RFC5246] Dierks, T. and E. Rescorla, "The Transport Layer Security
2671 (TLS) Protocol Version 1.2", RFC 5246, August 2008.
2672
2673 [RFC6454] Barth, A., "The Web Origin Concept", RFC 6454,
2674 December 2011.
2675
2676 [TLSNPN] Langley, A., "TLS Next Protocol Negotiation",
2677 <http://tools.ietf.org/html/
2678 draft-agl-tls-nextprotoneg-01>.
2679
2680 [ASCII] "US-ASCII. Coded Character Set - 7-Bit American Standard
2681 Code for Information Interchange. Standard ANSI X3.4-1986,
2682 ANSI, 1986.".
2683
2684
2685
2686
2687Belshe & Peon Expires August 4, 2012 [Page 48]
2688
2689Internet-Draft SPDY Feb 2012
2690
2691
2692 [UDELCOMPRESSION]
2693 Yang, F., Amer, P., and J. Leighton, "A Methodology to
2694 Derive SPDY's Initial Dictionary for Zlib Compression",
2695 <http://www.eecis.udel.edu/~amer/PEL/poc/pdf/
2696 SPDY-Fan.pdf>.
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743Belshe & Peon Expires August 4, 2012 [Page 49]
2744
2745Internet-Draft SPDY Feb 2012
2746
2747
2748Appendix A. Changes
2749
2750 To be removed by RFC Editor before publication
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799Belshe & Peon Expires August 4, 2012 [Page 50]
2800
2801Internet-Draft SPDY Feb 2012
2802
2803
2804Authors' Addresses
2805
2806 Mike Belshe
2807 Twist
2808
2809 Email: mbelshe@chromium.org
2810
2811
2812 Roberto Peon
2813 Google, Inc
2814
2815 Email: fenix@google.com
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855Belshe & Peon Expires August 4, 2012 [Page 51]
2856
diff --git a/src/examples/Makefile.am b/src/examples/Makefile.am
index edc80dc1..c3330e6f 100644
--- a/src/examples/Makefile.am
+++ b/src/examples/Makefile.am
@@ -13,17 +13,6 @@ if USE_COVERAGE
13 AM_CFLAGS += --coverage 13 AM_CFLAGS += --coverage
14endif 14endif
15 15
16if ENABLE_SPDY
17spdyex = \
18 spdy_event_loop \
19 spdy_fileserver \
20 spdy_response_with_callback
21
22if HAVE_SPDYLAY
23spdyex += mhd2spdy
24endif
25endif
26
27 16
28# example programs 17# example programs
29noinst_PROGRAMS = \ 18noinst_PROGRAMS = \
@@ -37,8 +26,7 @@ noinst_PROGRAMS = \
37 fileserver_example \ 26 fileserver_example \
38 fileserver_example_dirs \ 27 fileserver_example_dirs \
39 fileserver_example_external_select \ 28 fileserver_example_external_select \
40 refuse_post_example \ 29 refuse_post_example
41 $(spdyex)
42 30
43 31
44if ENABLE_HTTPS 32if ENABLE_HTTPS
@@ -98,15 +86,6 @@ demo_https_LDADD = \
98 $(top_builddir)/src/microhttpd/libmicrohttpd.la \ 86 $(top_builddir)/src/microhttpd/libmicrohttpd.la \
99 $(PTHREAD_LIBS) -lmagic 87 $(PTHREAD_LIBS) -lmagic
100 88
101mhd2spdy_SOURCES = \
102 mhd2spdy.c \
103 mhd2spdy_spdy.c mhd2spdy_spdy.h \
104 mhd2spdy_http.c mhd2spdy_http.h \
105 mhd2spdy_structures.c mhd2spdy_structures.h
106mhd2spdy_LDADD = \
107 $(top_builddir)/src/microhttpd/libmicrohttpd.la \
108 -lssl -lcrypto -lspdylay
109
110benchmark_SOURCES = \ 89benchmark_SOURCES = \
111 benchmark.c 90 benchmark.c
112benchmark_CPPFLAGS = \ 91benchmark_CPPFLAGS = \
@@ -179,20 +158,3 @@ https_fileserver_example_LDADD = \
179 $(top_builddir)/src/microhttpd/libmicrohttpd.la 158 $(top_builddir)/src/microhttpd/libmicrohttpd.la
180 159
181 160
182spdy_event_loop_SOURCES = \
183 spdy_event_loop.c
184spdy_event_loop_LDADD = \
185 $(top_builddir)/src/microspdy/libmicrospdy.la \
186 -lz
187
188spdy_fileserver_SOURCES = \
189 spdy_fileserver.c
190spdy_fileserver_LDADD = \
191 $(top_builddir)/src/microspdy/libmicrospdy.la \
192 -lz
193
194spdy_response_with_callback_SOURCES = \
195 spdy_response_with_callback.c
196spdy_response_with_callback_LDADD = \
197 $(top_builddir)/src/microspdy/libmicrospdy.la \
198 -lz
diff --git a/src/examples/mhd2spdy.c b/src/examples/mhd2spdy.c
deleted file mode 100644
index a2275087..00000000
--- a/src/examples/mhd2spdy.c
+++ /dev/null
@@ -1,322 +0,0 @@
1/*
2 Copyright Copyright (C) 2013 Andrey Uzunov
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17
18/**
19 * @file mhd2spdy.c
20 * @brief The main file of the HTTP-to-SPDY proxy with the 'main' function
21 * and event loop. No threads are used.
22 * Currently only GET is supported.
23 * TODOs:
24 * - non blocking SSL connect
25 * - check certificate
26 * - on closing spdy session, close sockets for all requests
27 * @author Andrey Uzunov
28 */
29
30
31#include "mhd2spdy_structures.h"
32#include "mhd2spdy_spdy.h"
33#include "mhd2spdy_http.h"
34
35
36static int run = 1;
37//static int spdy_close = 0;
38
39
40static void
41catch_signal(int signal)
42{
43 (void)signal;
44 //spdy_close = 1;
45 run = 0;
46}
47
48
49void
50print_stat()
51{
52 if(!glob_opt.statistics)
53 return;
54
55 printf("--------------------------\n");
56 printf("Statistics (TLS overhead is ignored when used):\n");
57 //printf("HTTP bytes received: %lld\n", glob_stat.http_bytes_received);
58 //printf("HTTP bytes sent: %lld\n", glob_stat.http_bytes_sent);
59 printf("SPDY bytes sent: %lld\n", glob_stat.spdy_bytes_sent);
60 printf("SPDY bytes received: %lld\n", glob_stat.spdy_bytes_received);
61 printf("SPDY bytes received and dropped: %lld\n", glob_stat.spdy_bytes_received_and_dropped);
62}
63
64
65int
66run_everything ()
67{
68 unsigned long long timeoutlong=0;
69 struct timeval timeout;
70 int ret;
71 fd_set rs;
72 fd_set ws;
73 fd_set es;
74 int maxfd = -1;
75 int maxfd_s = -1;
76 struct MHD_Daemon *daemon;
77 nfds_t spdy_npollfds = 1;
78 struct URI * spdy2http_uri = NULL;
79 struct SPDY_Connection *connection;
80 struct SPDY_Connection *connections[MAX_SPDY_CONNECTIONS];
81 struct SPDY_Connection *connection_for_delete;
82
83 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
84 PRINT_INFO("signal failed");
85
86 if (signal(SIGINT, catch_signal) == SIG_ERR)
87 PRINT_INFO("signal failed");
88
89 glob_opt.streams_opened = 0;
90 glob_opt.responses_pending = 0;
91 //glob_opt.global_memory = 0;
92
93 srand(time(NULL));
94
95 if(init_parse_uri(&glob_opt.uri_preg))
96 DIE("Regexp compilation failed");
97
98 if(NULL != glob_opt.spdy2http_str)
99 {
100 ret = parse_uri(&glob_opt.uri_preg, glob_opt.spdy2http_str, &spdy2http_uri);
101 if(ret != 0)
102 DIE("spdy_parse_uri failed");
103 }
104
105 SSL_load_error_strings();
106 SSL_library_init();
107 glob_opt.ssl_ctx = SSL_CTX_new(SSLv23_client_method());
108 if(glob_opt.ssl_ctx == NULL) {
109 PRINT_INFO2("SSL_CTX_new %s", ERR_error_string(ERR_get_error(), NULL));
110 abort();
111 }
112 spdy_ssl_init_ssl_ctx(glob_opt.ssl_ctx, &glob_opt.spdy_proto_version);
113
114 daemon = MHD_start_daemon (
115 MHD_SUPPRESS_DATE_NO_CLOCK,
116 glob_opt.listen_port,
117 NULL, NULL, &http_cb_request, NULL,
118 MHD_OPTION_URI_LOG_CALLBACK, &http_cb_log, NULL,
119 MHD_OPTION_NOTIFY_COMPLETED, &http_cb_request_completed, NULL,
120 MHD_OPTION_END);
121 if(NULL==daemon)
122 DIE("MHD_start_daemon failed");
123
124 do
125 {
126 timeout.tv_sec = 0;
127 timeout.tv_usec = 0;
128
129 if(NULL == glob_opt.spdy_connection && NULL != glob_opt.spdy2http_str)
130 {
131 glob_opt.spdy_connection = spdy_connect(spdy2http_uri, spdy2http_uri->port, strcmp("https", spdy2http_uri->scheme)==0);
132 if(NULL == glob_opt.spdy_connection && glob_opt.only_proxy)
133 PRINT_INFO("cannot connect to the proxy");
134 }
135
136 FD_ZERO(&rs);
137 FD_ZERO(&ws);
138 FD_ZERO(&es);
139
140 ret = MHD_get_timeout(daemon, &timeoutlong);
141 if(MHD_NO == ret || timeoutlong > 5000)
142 timeout.tv_sec = 5;
143 else
144 {
145 timeout.tv_sec = timeoutlong / 1000;
146 timeout.tv_usec = (timeoutlong % 1000) * 1000;
147 }
148
149 if(MHD_NO == MHD_get_fdset (daemon,
150 &rs,
151 &ws,
152 &es,
153 &maxfd))
154 {
155 PRINT_INFO("MHD_get_fdset error");
156 }
157 assert(-1 != maxfd);
158
159 maxfd_s = spdy_get_selectfdset(
160 &rs,
161 &ws,
162 &es,
163 connections, MAX_SPDY_CONNECTIONS, &spdy_npollfds);
164 if(maxfd_s > maxfd)
165 maxfd = maxfd_s;
166
167 PRINT_INFO2("MHD timeout %lld %lld", (unsigned long long)timeout.tv_sec, (unsigned long long)timeout.tv_usec);
168
169 glob_opt.spdy_data_received = false;
170
171 ret = select(maxfd+1, &rs, &ws, &es, &timeout);
172 PRINT_INFO2("timeout now %lld %lld ret is %i", (unsigned long long)timeout.tv_sec, (unsigned long long)timeout.tv_usec, ret);
173
174 switch(ret)
175 {
176 case -1:
177 PRINT_INFO2("select error: %i", errno);
178 break;
179 case 0:
180 //break;
181 default:
182 PRINT_INFO("run");
183 //MHD_run_from_select(daemon,&rs, &ws, &es); //not closing FDs at some time in past
184 MHD_run(daemon);
185 spdy_run_select(&rs, &ws, &es, connections, spdy_npollfds);
186 if(glob_opt.spdy_data_received)
187 {
188 PRINT_INFO("MHD run again");
189 //MHD_run_from_select(daemon,&rs, &ws, &es); //not closing FDs at some time in past
190 MHD_run(daemon);
191 }
192 break;
193 }
194 }
195 while(run);
196
197 MHD_stop_daemon (daemon);
198
199 //TODO SSL_free brakes
200 spdy_free_connection(glob_opt.spdy_connection);
201
202 connection = glob_opt.spdy_connections_head;
203 while(NULL != connection)
204 {
205 connection_for_delete = connection;
206 connection = connection_for_delete->next;
207 glob_opt.streams_opened -= connection_for_delete->streams_opened;
208 DLL_remove(glob_opt.spdy_connections_head, glob_opt.spdy_connections_tail, connection_for_delete);
209 spdy_free_connection(connection_for_delete);
210 }
211
212 free_uri(spdy2http_uri);
213
214 deinit_parse_uri(&glob_opt.uri_preg);
215
216 SSL_CTX_free(glob_opt.ssl_ctx);
217 ERR_free_strings();
218 EVP_cleanup();
219
220 PRINT_INFO2("spdy streams: %i; http requests: %i", glob_opt.streams_opened, glob_opt.responses_pending);
221 //PRINT_INFO2("memory allocated %zu bytes", glob_opt.global_memory);
222
223 print_stat();
224
225 return 0;
226}
227
228
229void
230display_usage()
231{
232 printf(
233 "Usage: mhd2spdy [-ovs] [-b <SPDY2HTTP-PROXY>] -p <PORT>\n\n"
234 "OPTIONS:\n"
235 " -p, --port Listening port.\n"
236 " -b, --backend-proxy If set, he proxy will send requests to\n"
237 " that SPDY server or proxy. Set the address\n"
238 " in the form 'http://host:port'. Use 'https'\n"
239 " for SPDY over TLS, or 'http' for plain SPDY\n"
240 " communication with the backend.\n"
241 " -o, --only-proxy If set, the proxy will always forward the\n"
242 " requests to the backend proxy. If not set,\n"
243 " the proxy will first try to establsh SPDY\n"
244 " connection to the requested server. If the\n"
245 " server does not support SPDY and TLS, the\n"
246 " backend proxy will be used for the request.\n"
247 " -v, --verbose Print debug information.\n"
248 " -s, --statistics Print simple statistics on exit.\n\n"
249
250 );
251}
252
253
254int
255main (int argc,
256 char *const *argv)
257{
258 int getopt_ret;
259 int option_index;
260 struct option long_options[] = {
261 {"port", required_argument, 0, 'p'},
262 {"backend-proxy", required_argument, 0, 'b'},
263 {"verbose", no_argument, 0, 'v'},
264 {"only-proxy", no_argument, 0, 'o'},
265 {"statistics", no_argument, 0, 's'},
266 {0, 0, 0, 0}
267 };
268
269 while (1)
270 {
271 getopt_ret = getopt_long( argc, argv, "p:b:vos", long_options, &option_index);
272 if (getopt_ret == -1)
273 break;
274
275 switch(getopt_ret)
276 {
277 case 'p':
278 glob_opt.listen_port = atoi(optarg);
279 break;
280
281 case 'b':
282 glob_opt.spdy2http_str = strdup(optarg);
283 if(NULL == glob_opt.spdy2http_str)
284 return 1;
285 break;
286
287 case 'v':
288 glob_opt.verbose = true;
289 break;
290
291 case 'o':
292 glob_opt.only_proxy = true;
293 break;
294
295 case 's':
296 glob_opt.statistics = true;
297 break;
298
299 case 0:
300 PRINT_INFO("0 from getopt");
301 break;
302
303 case '?':
304 display_usage();
305 return 1;
306
307 default:
308 DIE("default from getopt");
309 }
310 }
311
312 if(
313 0 == glob_opt.listen_port
314 || (glob_opt.only_proxy && NULL == glob_opt.spdy2http_str)
315 )
316 {
317 display_usage();
318 return 1;
319 }
320
321 return run_everything();
322}
diff --git a/src/examples/mhd2spdy_http.c b/src/examples/mhd2spdy_http.c
deleted file mode 100644
index 895f07fa..00000000
--- a/src/examples/mhd2spdy_http.c
+++ /dev/null
@@ -1,422 +0,0 @@
1/*
2 Copyright Copyright (C) 2013 Andrey Uzunov
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17
18/**
19 * @file mhd2spdy_http.c
20 * @brief HTTP part of the proxy. libmicrohttpd is used for the server side.
21 * @author Andrey Uzunov
22 */
23
24#include "mhd2spdy_structures.h"
25#include "mhd2spdy_http.h"
26#include "mhd2spdy_spdy.h"
27
28
29void *
30http_cb_log(void * cls,
31const char * uri)
32{
33 (void)cls;
34
35 struct HTTP_URI * http_uri;
36
37 PRINT_INFO2("log uri '%s'\n", uri);
38
39 //TODO not freed once in a while
40 if(NULL == (http_uri = au_malloc(sizeof(struct HTTP_URI ))))
41 return NULL;
42 http_uri->uri = strdup(uri);
43 return http_uri;
44}
45
46
47static int
48http_cb_iterate(void *cls,
49 enum MHD_ValueKind kind,
50 const char *name,
51 const char *value)
52{
53 (void)kind;
54
55 static char * const forbidden[] = {"Transfer-Encoding",
56 "Proxy-Connection",
57 "Keep-Alive",
58 "Connection"};
59 static int forbidden_size = 4;
60 int i;
61 struct SPDY_Headers *spdy_headers = (struct SPDY_Headers *)cls;
62
63 if(0 == strcasecmp(name, "Host"))
64 spdy_headers->nv[9] = (char *)value;
65 else
66 {
67 for(i=0; i<forbidden_size; ++i)
68 if(0 == strcasecmp(forbidden[i], name))
69 return MHD_YES;
70 spdy_headers->nv[spdy_headers->cnt++] = (char *)name;
71 spdy_headers->nv[spdy_headers->cnt++] = (char *)value;
72 }
73
74 return MHD_YES;
75}
76
77
78static ssize_t
79http_cb_response (void *cls,
80 uint64_t pos,
81 char *buffer,
82 size_t max)
83{
84 (void)pos;
85
86 int ret;
87 struct Proxy *proxy = (struct Proxy *)cls;
88 void *newbody;
89 const union MHD_ConnectionInfo *info;
90 int val = 1;
91
92 PRINT_INFO2("http_cb_response for %s", proxy->url);
93
94 if(proxy->spdy_error)
95 return MHD_CONTENT_READER_END_WITH_ERROR;
96
97 if(0 == proxy->http_body_size && (proxy->done || !proxy->spdy_active))
98 {
99 PRINT_INFO("sent end of stream");
100 return MHD_CONTENT_READER_END_OF_STREAM;
101 }
102
103 if(!proxy->http_body_size)//nothing to write now
104 {
105 //flush data
106 info = MHD_get_connection_info (proxy->http_connection,
107 MHD_CONNECTION_INFO_CONNECTION_FD);
108 ret = setsockopt(info->connect_fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val));
109 if(ret == -1) {
110 DIE("setsockopt");
111 }
112
113 PRINT_INFO("FLUSH data");
114 return 0;
115 }
116
117 if(max >= proxy->http_body_size)
118 {
119 ret = proxy->http_body_size;
120 newbody = NULL;
121 }
122 else
123 {
124 ret = max;
125 if(NULL == (newbody = au_malloc(proxy->http_body_size - max)))
126 {
127 PRINT_INFO("no memory");
128 return MHD_CONTENT_READER_END_WITH_ERROR;
129 }
130 memcpy(newbody, proxy->http_body + max, proxy->http_body_size - max);
131 }
132 memcpy(buffer, proxy->http_body, ret);
133 free(proxy->http_body);
134 proxy->http_body = newbody;
135 proxy->http_body_size -= ret;
136
137 if(proxy->length >= 0)
138 {
139 proxy->length -= ret;
140 }
141
142 PRINT_INFO2("response_callback, size: %i",ret);
143
144 return ret;
145}
146
147
148static void
149http_cb_response_done(void *cls)
150{
151 (void)cls;
152 //TODO remove
153}
154
155int
156http_cb_request (void *cls,
157 struct MHD_Connection *connection,
158 const char *url,
159 const char *method,
160 const char *version,
161 const char *upload_data,
162 size_t *upload_data_size,
163 void **ptr)
164{
165 (void)cls;
166 (void)url;
167 (void)upload_data;
168 (void)upload_data_size;
169
170 int ret;
171 struct Proxy *proxy;
172 struct SPDY_Headers spdy_headers;
173 bool with_body = false;
174 struct HTTP_URI *http_uri;
175 const char *header_value;
176
177 if (NULL == ptr || NULL == *ptr)
178 return MHD_NO;
179
180 http_uri = (struct HTTP_URI *)*ptr;
181
182 if(NULL == http_uri->proxy)
183 {
184 //first call for this request
185 if (0 != strcmp (method, MHD_HTTP_METHOD_GET) && 0 != strcmp (method, MHD_HTTP_METHOD_POST))
186 {
187 free(http_uri->uri);
188 free(http_uri);
189 PRINT_INFO2("unexpected method %s", method);
190 return MHD_NO;
191 }
192
193 if(NULL == (proxy = au_malloc(sizeof(struct Proxy))))
194 {
195 free(http_uri->uri);
196 free(http_uri);
197 PRINT_INFO("No memory");
198 return MHD_NO;
199 }
200
201 ++glob_opt.responses_pending;
202 proxy->id = rand();
203 proxy->http_active = true;
204 proxy->http_connection = connection;
205 http_uri->proxy = proxy;
206 return MHD_YES;
207 }
208
209 proxy = http_uri->proxy;
210
211 if(proxy->spdy_error || proxy->http_error)
212 return MHD_NO; // handled at different place TODO? leaks?
213
214 if(proxy->spdy_active)
215 {
216 if(0 == strcmp (method, MHD_HTTP_METHOD_POST))
217 {
218 PRINT_INFO("POST processing");
219
220 int rc= spdylay_session_resume_data(proxy->spdy_connection->session, proxy->stream_id);
221 PRINT_INFO2("rc is %i stream is %i", rc, proxy->stream_id);
222 proxy->spdy_connection->want_io |= WANT_WRITE;
223
224 if(0 == *upload_data_size)
225 {
226 PRINT_INFO("POST http EOF");
227 proxy->receiving_done = true;
228 return MHD_YES;
229 }
230
231 if(!copy_buffer(upload_data, *upload_data_size, &proxy->received_body, &proxy->received_body_size))
232 {
233 //TODO handle it better?
234 PRINT_INFO("not enough memory (malloc/realloc returned NULL)");
235 return MHD_NO;
236 }
237
238 *upload_data_size = 0;
239
240 return MHD_YES;
241 }
242
243 //already handled
244 PRINT_INFO("unnecessary call to http_cb_request");
245 return MHD_YES;
246 }
247
248 //second call for this request
249
250 PRINT_INFO2("received request for '%s %s %s'", method, http_uri->uri, version);
251
252 proxy->url = http_uri->uri;
253
254 header_value = MHD_lookup_connection_value(connection,
255 MHD_HEADER_KIND, MHD_HTTP_HEADER_CONTENT_LENGTH);
256
257 with_body = 0 == strcmp (method, MHD_HTTP_METHOD_POST)
258 && (NULL == header_value || 0 != strcmp ("0", header_value));
259
260 PRINT_INFO2("body will be sent %i", with_body);
261
262 ret = parse_uri(&glob_opt.uri_preg, proxy->url, &proxy->uri);
263 if(ret != 0)
264 DIE("parse_uri failed");
265 proxy->http_uri = http_uri;
266
267 spdy_headers.num = MHD_get_connection_values (connection,
268 MHD_HEADER_KIND,
269 NULL,
270 NULL);
271 if(NULL == (spdy_headers.nv = au_malloc(((spdy_headers.num + 5) * 2 + 1) * sizeof(char *))))
272 DIE("no memory");
273 spdy_headers.nv[0] = ":method"; spdy_headers.nv[1] = method;
274 spdy_headers.nv[2] = ":path"; spdy_headers.nv[3] = proxy->uri->path_and_more;
275 spdy_headers.nv[4] = ":version"; spdy_headers.nv[5] = (char *)version;
276 spdy_headers.nv[6] = ":scheme"; spdy_headers.nv[7] = proxy->uri->scheme;
277 spdy_headers.nv[8] = ":host"; spdy_headers.nv[9] = NULL;
278 //nv[14] = NULL;
279 spdy_headers.cnt = 10;
280 MHD_get_connection_values (connection,
281 MHD_HEADER_KIND,
282 &http_cb_iterate,
283 &spdy_headers);
284
285 spdy_headers.nv[spdy_headers.cnt] = NULL;
286 if(NULL == spdy_headers.nv[9])
287 spdy_headers.nv[9] = proxy->uri->host_and_port;
288
289 if(0 != spdy_request(spdy_headers.nv, proxy, with_body))
290 {
291 free(spdy_headers.nv);
292 //free_proxy(proxy);
293
294 return MHD_NO;
295 }
296 free(spdy_headers.nv);
297
298 proxy->http_response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
299 4096,
300 &http_cb_response,
301 proxy,
302 &http_cb_response_done);
303
304 if (NULL == proxy->http_response)
305 DIE("no response");
306
307 if(MHD_NO == MHD_add_response_header (proxy->http_response,
308 "Proxy-Connection", "keep-alive"))
309 PRINT_INFO("SPDY_name_value_add failed: ");
310 if(MHD_NO == MHD_add_response_header (proxy->http_response,
311 "Connection", "Keep-Alive"))
312 PRINT_INFO("SPDY_name_value_add failed: ");
313 if(MHD_NO == MHD_add_response_header (proxy->http_response,
314 "Keep-Alive", "timeout=5, max=100"))
315 PRINT_INFO("SPDY_name_value_add failed: ");
316
317 proxy->spdy_active = true;
318
319 return MHD_YES;
320}
321
322
323void
324http_create_response(struct Proxy* proxy,
325 char **nv)
326{
327 size_t i;
328
329 if(!proxy->http_active)
330 return;
331
332 for(i = 0; nv[i]; i += 2) {
333 if(0 == strcmp(":status", nv[i]))
334 {
335 char tmp[4];
336 memcpy(&tmp,nv[i+1],3);
337 tmp[3]=0;
338 proxy->status = atoi(tmp);
339 continue;
340 }
341 else if(0 == strcmp(":version", nv[i]))
342 {
343 proxy->version = nv[i+1];
344 continue;
345 }
346 else if(0 == strcmp("content-length", nv[i]))
347 {
348 continue;
349 }
350
351 char *header = *(nv+i);
352 if(MHD_NO == MHD_add_response_header (proxy->http_response,
353 header, nv[i+1]))
354 {
355 PRINT_INFO2("SPDY_name_value_add failed: '%s' '%s'", header, nv[i+1]);
356 }
357 PRINT_INFO2("adding '%s: %s'",header, nv[i+1]);
358 }
359
360 if(MHD_NO == MHD_queue_response (proxy->http_connection, proxy->status, proxy->http_response)){
361 PRINT_INFO("No queue");
362 //TODO
363 //abort();
364 proxy->http_error = true;
365 }
366
367 MHD_destroy_response (proxy->http_response);
368 proxy->http_response = NULL;
369}
370
371void
372http_cb_request_completed (void *cls,
373 struct MHD_Connection *connection,
374 void **con_cls,
375 enum MHD_RequestTerminationCode toe)
376{
377 (void)cls;
378 (void)connection;
379 struct HTTP_URI *http_uri;
380 struct Proxy *proxy;
381
382 http_uri = (struct HTTP_URI *)*con_cls;
383 if(NULL == http_uri)
384 return;
385 proxy = (struct Proxy *)http_uri->proxy;
386 assert(NULL != proxy);
387
388 PRINT_INFO2("http_cb_request_completed %i for %s; id %i",toe, http_uri->uri, proxy->id);
389
390 if(NULL != proxy->http_response)
391 {
392 MHD_destroy_response (proxy->http_response);
393 proxy->http_response = NULL;
394 }
395
396 if(proxy->spdy_active)
397 {
398 proxy->http_active = false;
399 if(MHD_REQUEST_TERMINATED_COMPLETED_OK != toe)
400 {
401 proxy->http_error = true;
402 if(proxy->stream_id > 0 /*&& NULL != proxy->spdy_connection->session*/)
403 {
404 //send RST_STREAM_STATUS_CANCEL
405 PRINT_INFO2("send rst_stream %i %i",proxy->spdy_active, proxy->stream_id );
406 spdylay_submit_rst_stream(proxy->spdy_connection->session, proxy->stream_id, 5);
407 }
408 /*else
409 {
410 DLL_remove(proxy->spdy_connection->proxies_head, proxy->spdy_connection->proxies_tail, proxy);
411 free_proxy(proxy);
412 }*/
413 }
414 }
415 else
416 {
417 PRINT_INFO2("proxy free http id %i ", proxy->id);
418 free_proxy(proxy);
419 }
420
421 --glob_opt.responses_pending;
422}
diff --git a/src/examples/mhd2spdy_http.h b/src/examples/mhd2spdy_http.h
deleted file mode 100644
index 89d38897..00000000
--- a/src/examples/mhd2spdy_http.h
+++ /dev/null
@@ -1,54 +0,0 @@
1/*
2 Copyright Copyright (C) 2013 Andrey Uzunov
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17
18/**
19 * @file mhd2spdy_http.h
20 * @brief HTTP part of the proxy. libmicrohttpd is used for the server side.
21 * @author Andrey Uzunov
22 */
23
24#ifndef HTTP_H
25#define HTTP_H
26
27#include "mhd2spdy_structures.h"
28
29
30int
31http_cb_request (void *cls,
32 struct MHD_Connection *connection,
33 const char *url,
34 const char *method,
35 const char *version,
36 const char *upload_data,
37 size_t *upload_data_size,
38 void **ptr);
39
40
41void * http_cb_log(void * cls, const char * uri);
42
43
44void
45http_create_response(struct Proxy* proxy, char **nv);
46
47
48void
49http_cb_request_completed (void *cls,
50 struct MHD_Connection *connection,
51 void **con_cls,
52 enum MHD_RequestTerminationCode toe);
53
54#endif
diff --git a/src/examples/mhd2spdy_spdy.c b/src/examples/mhd2spdy_spdy.c
deleted file mode 100644
index 9092da11..00000000
--- a/src/examples/mhd2spdy_spdy.c
+++ /dev/null
@@ -1,1150 +0,0 @@
1/*
2 *
3 * Copyright (c) 2012 Tatsuhiro Tsujikawa
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25/**
26 * @file mhd2spdy_spdy.c
27 * @brief SPDY part of the proxy. libspdylay is used for the client side.
28 * The example spdycli.c from spdylay was used as basis;
29 * however, multiple changes were made.
30 * @author Tatsuhiro Tsujikawa
31 * @author Andrey Uzunov
32 */
33
34#include "mhd2spdy_structures.h"
35#include "mhd2spdy_spdy.h"
36#include "mhd2spdy_http.h"
37
38
39/*
40 * Prints error containing the function name |func| and message |msg|
41 * and exit.
42 */
43static void
44spdy_dief(const char *func,
45 const char *msg)
46{
47 fprintf(stderr, "FATAL: %s: %s\n", func, msg);
48 exit(EXIT_FAILURE);
49}
50
51
52/*
53 * Prints error containing the function name |func| and error code
54 * |error_code| and exit.
55 */
56void
57spdy_diec(const char *func,
58 int error_code)
59{
60 fprintf(stderr, "FATAL: %s: error_code=%d, msg=%s\n", func, error_code,
61 spdylay_strerror(error_code));
62 exit(EXIT_FAILURE);
63}
64
65
66static ssize_t
67spdy_cb_data_source_read(spdylay_session *session, int32_t stream_id, uint8_t *buf, size_t length, int *eof, spdylay_data_source *source, void *user_data)
68{
69 (void)session;
70 (void)stream_id;
71 (void)user_data;
72
73 ssize_t ret;
74 assert(NULL != source);
75 assert(NULL != source->ptr);
76 struct Proxy *proxy = (struct Proxy *)(source->ptr);
77 void *newbody;
78
79
80 if(length < 1)
81 {
82 PRINT_INFO("spdy_cb_data_source_read: length is 0");
83 return 0;
84 }
85
86 if(!proxy->received_body_size)//nothing to write now
87 {
88 if(proxy->receiving_done)
89 {
90 PRINT_INFO("POST spdy EOF");
91 *eof = 1;
92 }
93 PRINT_INFO("POST SPDYLAY_ERR_DEFERRED");
94 return SPDYLAY_ERR_DEFERRED;//TODO SPDYLAY_ERR_DEFERRED should be used
95 }
96
97 if(length >= proxy->received_body_size)
98 {
99 ret = proxy->received_body_size;
100 newbody = NULL;
101 }
102 else
103 {
104 ret = length;
105 if(NULL == (newbody = malloc(proxy->received_body_size - length)))
106 {
107 PRINT_INFO("no memory");
108 return SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE;
109 }
110 memcpy(newbody, proxy->received_body + length, proxy->received_body_size - length);
111 }
112 memcpy(buf, proxy->received_body, ret);
113 free(proxy->received_body);
114 proxy->received_body = newbody;
115 proxy->received_body_size -= ret;
116
117 if(0 == proxy->received_body_size && proxy->receiving_done)
118 {
119 PRINT_INFO("POST spdy EOF");
120 *eof = 1;
121 }
122
123 PRINT_INFO2("given POST bytes to spdylay: %zd", ret);
124
125 return ret;
126}
127
128
129/*
130 * The implementation of spdylay_send_callback type. Here we write
131 * |data| with size |length| to the network and return the number of
132 * bytes actually written. See the documentation of
133 * spdylay_send_callback for the details.
134 */
135static ssize_t
136spdy_cb_send(spdylay_session *session,
137 const uint8_t *data,
138 size_t length,
139 int flags,
140 void *user_data)
141{
142 (void)session;
143 (void)flags;
144
145 //PRINT_INFO("spdy_cb_send called");
146 struct SPDY_Connection *connection;
147 ssize_t rv;
148 connection = (struct SPDY_Connection*)user_data;
149 connection->want_io = IO_NONE;
150
151 if(glob_opt.ignore_rst_stream
152 && 16 == length
153 && 0x80 == data[0]
154 && 0x00 == data[2]
155 && 0x03 == data[3]
156 )
157 {
158 PRINT_INFO2("ignoring RST_STREAM for stream_id %i %i %i %i", data[8], data[9], data[10], data[11]);
159 glob_opt.ignore_rst_stream = false;
160 return 16;
161 }
162 glob_opt.ignore_rst_stream = false;
163
164 if(connection->is_tls)
165 {
166 ERR_clear_error();
167 rv = SSL_write(connection->ssl, data, length);
168 if(rv < 0) {
169 int err = SSL_get_error(connection->ssl, rv);
170 if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
171 connection->want_io |= (err == SSL_ERROR_WANT_READ ?
172 WANT_READ : WANT_WRITE);
173 rv = SPDYLAY_ERR_WOULDBLOCK;
174 } else {
175 rv = SPDYLAY_ERR_CALLBACK_FAILURE;
176 }
177 }
178 }
179 else
180 {
181 rv = write(connection->fd,
182 data,
183 length);
184
185 if (rv < 0)
186 {
187 switch(errno)
188 {
189 case EAGAIN:
190 #if EAGAIN != EWOULDBLOCK
191 case EWOULDBLOCK:
192 #endif
193 connection->want_io |= WANT_WRITE;
194 rv = SPDYLAY_ERR_WOULDBLOCK;
195 break;
196
197 default:
198 rv = SPDYLAY_ERR_CALLBACK_FAILURE;
199 }
200 }
201 }
202
203 PRINT_INFO2("%zd bytes written by spdy", rv);
204
205 if(rv > 0)
206 UPDATE_STAT(glob_stat.spdy_bytes_sent, rv);
207
208 return rv;
209}
210
211
212/*
213 * The implementation of spdylay_recv_callback type. Here we read data
214 * from the network and write them in |buf|. The capacity of |buf| is
215 * |length| bytes. Returns the number of bytes stored in |buf|. See
216 * the documentation of spdylay_recv_callback for the details.
217 */
218static ssize_t
219spdy_cb_recv(spdylay_session *session,
220 uint8_t *buf,
221 size_t length,
222 int flags,
223 void *user_data)
224{
225 (void)session;
226 (void)flags;
227
228 struct SPDY_Connection *connection;
229 ssize_t rv;
230
231 connection = (struct SPDY_Connection*)user_data;
232 //prevent monopolizing everything
233 if(!(++connection->counter % 10)) return SPDYLAY_ERR_WOULDBLOCK;
234 connection->want_io = IO_NONE;
235 if(connection->is_tls)
236 {
237 ERR_clear_error();
238 rv = SSL_read(connection->ssl, buf, length);
239 if(rv < 0) {
240 int err = SSL_get_error(connection->ssl, rv);
241 if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
242 connection->want_io |= (err == SSL_ERROR_WANT_READ ?
243 WANT_READ : WANT_WRITE);
244 rv = SPDYLAY_ERR_WOULDBLOCK;
245 } else {
246 rv = SPDYLAY_ERR_CALLBACK_FAILURE;
247 }
248 } else if(rv == 0) {
249 rv = SPDYLAY_ERR_EOF;
250 }
251 }
252 else
253 {
254 rv = read(connection->fd,
255 buf,
256 length);
257
258 if (rv < 0)
259 {
260 switch(errno)
261 {
262 case EAGAIN:
263 #if EAGAIN != EWOULDBLOCK
264 case EWOULDBLOCK:
265 #endif
266 connection->want_io |= WANT_READ;
267 rv = SPDYLAY_ERR_WOULDBLOCK;
268 break;
269
270 default:
271 rv = SPDYLAY_ERR_CALLBACK_FAILURE;
272 }
273 }
274 else if(rv == 0)
275 rv = SPDYLAY_ERR_EOF;
276 }
277
278 if(rv > 0)
279 UPDATE_STAT(glob_stat.spdy_bytes_received, rv);
280
281 return rv;
282}
283
284
285static void
286spdy_cb_before_ctrl_send(spdylay_session *session,
287 spdylay_frame_type type,
288 spdylay_frame *frame,
289 void *user_data)
290{
291 (void)user_data;
292
293 int32_t stream_id;
294 struct Proxy *proxy;
295
296 switch(type) {
297 case SPDYLAY_SYN_STREAM:
298 stream_id = frame->syn_stream.stream_id;
299 proxy = spdylay_session_get_stream_user_data(session, stream_id);
300 proxy->stream_id = stream_id;
301 ++glob_opt.streams_opened;
302 ++proxy->spdy_connection->streams_opened;
303 PRINT_INFO2("opening stream: str open %i; %s", glob_opt.streams_opened, proxy->url);
304 break;
305 case SPDYLAY_RST_STREAM:
306 //try to ignore duplicate RST_STREAMs
307 //TODO this will ignore RST_STREAMs also for bogus data
308 glob_opt.ignore_rst_stream = NULL==spdylay_session_get_stream_user_data(session, frame->rst_stream.stream_id);
309 PRINT_INFO2("sending RST_STREAM for %i; ignore %i; status %i",
310 frame->rst_stream.stream_id,
311 glob_opt.ignore_rst_stream,
312 frame->rst_stream.status_code);
313 break;
314 default:
315 break;
316 }
317}
318
319
320void
321spdy_cb_on_ctrl_recv(spdylay_session *session,
322 spdylay_frame_type type,
323 spdylay_frame *frame,
324 void *user_data)
325{
326 (void)user_data;
327
328 char **nv;
329 int32_t stream_id;
330 struct Proxy * proxy;
331
332 switch(type) {
333 case SPDYLAY_SYN_REPLY:
334 nv = frame->syn_reply.nv;
335 stream_id = frame->syn_reply.stream_id;
336 break;
337 case SPDYLAY_RST_STREAM:
338 stream_id = frame->rst_stream.stream_id;
339 break;
340 case SPDYLAY_HEADERS:
341 nv = frame->headers.nv;
342 stream_id = frame->headers.stream_id;
343 break;
344 default:
345 return;
346 break;
347 }
348
349 proxy = spdylay_session_get_stream_user_data(session, stream_id);
350 if(NULL == proxy)
351 {
352 PRINT_INFO2("received frame type %i for unkonwn stream id %i", type, stream_id);
353 return;
354 //DIE("no proxy obj");
355 }
356
357 switch(type) {
358 case SPDYLAY_SYN_REPLY:
359 PRINT_INFO2("received headers for %s", proxy->url);
360 http_create_response(proxy, nv);
361 break;
362 case SPDYLAY_RST_STREAM:
363 PRINT_INFO2("received reset stream for %s", proxy->url);
364 proxy->spdy_error = true;
365 break;
366 case SPDYLAY_HEADERS:
367 PRINT_INFO2("received headers for %s", proxy->url);
368 http_create_response(proxy, nv);
369 break;
370 default:
371 return;
372 break;
373 }
374
375 glob_opt.spdy_data_received = true;
376}
377
378
379/*
380 * The implementation of spdylay_on_stream_close_callback type. We use
381 * this function to know the response is fully received. Since we just
382 * fetch 1 resource in this program, after reception of the response,
383 * we submit GOAWAY and close the session.
384 */
385static void
386spdy_cb_on_stream_close(spdylay_session *session,
387 int32_t stream_id,
388 spdylay_status_code status_code,
389 void *user_data)
390{
391 (void)status_code;
392 (void)user_data;
393
394 struct Proxy * proxy = spdylay_session_get_stream_user_data(session, stream_id);
395
396 assert(NULL != proxy);
397
398 --glob_opt.streams_opened;
399 --proxy->spdy_connection->streams_opened;
400 PRINT_INFO2("closing stream: str opened %i; remove proxy %i", glob_opt.streams_opened, proxy->id);
401
402 DLL_remove(proxy->spdy_connection->proxies_head, proxy->spdy_connection->proxies_tail, proxy);
403 if(proxy->http_active)
404 {
405 proxy->spdy_active = false;
406 }
407 else
408 {
409 free_proxy(proxy);
410 }
411}
412
413
414/*
415 * The implementation of spdylay_on_data_chunk_recv_callback type. We
416 * use this function to print the received response body.
417 */
418static void
419spdy_cb_on_data_chunk_recv(spdylay_session *session,
420 uint8_t flags,
421 int32_t stream_id,
422 const uint8_t *data,
423 size_t len,
424 void *user_data)
425{
426 (void)flags;
427 (void)user_data;
428
429 struct Proxy *proxy;
430 proxy = spdylay_session_get_stream_user_data(session, stream_id);
431
432 if(NULL == proxy)
433 {
434 PRINT_INFO("proxy in spdy_cb_on_data_chunk_recv is NULL)");
435 return;
436 }
437
438 if(!copy_buffer(data, len, &proxy->http_body, &proxy->http_body_size))
439 {
440 //TODO handle it better?
441 PRINT_INFO("not enough memory (malloc/realloc returned NULL)");
442 return;
443 }
444 /*
445 if(NULL == proxy->http_body)
446 proxy->http_body = au_malloc(len);
447 else
448 proxy->http_body = realloc(proxy->http_body, proxy->http_body_size + len);
449 if(NULL == proxy->http_body)
450 {
451 PRINT_INFO("not enough memory (realloc returned NULL)");
452 return ;
453 }
454
455 memcpy(proxy->http_body + proxy->http_body_size, data, len);
456 proxy->http_body_size += len;
457 */
458 PRINT_INFO2("received data for %s; %zu bytes", proxy->url, len);
459 glob_opt.spdy_data_received = true;
460}
461
462
463static void
464spdy_cb_on_data_recv(spdylay_session *session,
465 uint8_t flags,
466 int32_t stream_id,
467 int32_t length,
468 void *user_data)
469{
470 (void)length;
471 (void)user_data;
472
473 if(flags & SPDYLAY_DATA_FLAG_FIN)
474 {
475 struct Proxy *proxy;
476 proxy = spdylay_session_get_stream_user_data(session, stream_id);
477 proxy->done = true;
478 PRINT_INFO2("last data frame received for %s", proxy->url);
479 }
480}
481
482
483/*
484 * Setup callback functions. Spdylay API offers many callback
485 * functions, but most of them are optional. The send_callback is
486 * always required. Since we use spdylay_session_recv(), the
487 * recv_callback is also required.
488 */
489static void
490spdy_setup_spdylay_callbacks(spdylay_session_callbacks *callbacks)
491{
492 memset(callbacks, 0, sizeof(spdylay_session_callbacks));
493 callbacks->send_callback = spdy_cb_send;
494 callbacks->recv_callback = spdy_cb_recv;
495 callbacks->before_ctrl_send_callback = spdy_cb_before_ctrl_send;
496 callbacks->on_ctrl_recv_callback = spdy_cb_on_ctrl_recv;
497 callbacks->on_stream_close_callback = spdy_cb_on_stream_close;
498 callbacks->on_data_chunk_recv_callback = spdy_cb_on_data_chunk_recv;
499 callbacks->on_data_recv_callback = spdy_cb_on_data_recv;
500}
501
502
503/*
504 * Callback function for SSL/TLS NPN. Since this program only supports
505 * SPDY protocol, if server does not offer SPDY protocol the Spdylay
506 * library supports, we terminate program.
507 */
508static int
509spdy_cb_ssl_select_next_proto(SSL* ssl,
510 unsigned char **out,
511 unsigned char *outlen,
512 const unsigned char *in,
513 unsigned int inlen,
514 void *arg)
515{
516 (void)ssl;
517
518 int rv;
519 uint16_t *spdy_proto_version;
520
521 /* spdylay_select_next_protocol() selects SPDY protocol version the
522 Spdylay library supports. */
523 rv = spdylay_select_next_protocol(out, outlen, in, inlen);
524 if(rv <= 0) {
525 PRINT_INFO("Server did not advertise spdy/2 or spdy/3 protocol.");
526 return rv;
527 }
528 spdy_proto_version = (uint16_t*)arg;
529 *spdy_proto_version = rv;
530 return SSL_TLSEXT_ERR_OK;
531}
532
533
534/*
535 * Setup SSL context. We pass |spdy_proto_version| to get negotiated
536 * SPDY protocol version in NPN callback.
537 */
538void
539spdy_ssl_init_ssl_ctx(SSL_CTX *ssl_ctx,
540 uint16_t *spdy_proto_version)
541{
542 /* Disable SSLv2 and enable all workarounds for buggy servers */
543 SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2 | SSL_OP_NO_COMPRESSION);
544 SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
545 SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
546 /* Set NPN callback */
547 SSL_CTX_set_next_proto_select_cb(ssl_ctx, spdy_cb_ssl_select_next_proto,
548 spdy_proto_version);
549}
550
551
552static int
553spdy_ssl_handshake(SSL *ssl,
554 int fd)
555{
556 int rv;
557
558 if(SSL_set_fd(ssl, fd) == 0)
559 spdy_dief("SSL_set_fd", ERR_error_string(ERR_get_error(), NULL));
560
561 ERR_clear_error();
562 rv = SSL_connect(ssl);
563 if(rv <= 0)
564 PRINT_INFO2("SSL_connect %s", ERR_error_string(ERR_get_error(), NULL));
565
566 return rv;
567}
568
569
570/*
571 * Connects to the host |host| and port |port|. This function returns
572 * the file descriptor of the client socket.
573 */
574static int
575spdy_socket_connect_to(const char *host,
576 uint16_t port)
577{
578 struct addrinfo hints;
579 int fd = -1;
580 int rv;
581 char service[NI_MAXSERV];
582 struct addrinfo *res, *rp;
583
584 //TODO checks
585 snprintf(service, sizeof(service), "%u", port);
586 memset(&hints, 0, sizeof(struct addrinfo));
587 hints.ai_family = AF_UNSPEC;
588 hints.ai_socktype = SOCK_STREAM;
589 rv = getaddrinfo(host, service, &hints, &res);
590 if(rv != 0)
591 {
592 printf("%s\n",host);
593 spdy_dief("getaddrinfo", gai_strerror(rv));
594 }
595 for(rp = res; rp; rp = rp->ai_next)
596 {
597 fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
598 if(fd == -1)
599 continue;
600 while((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 &&
601 errno == EINTR);
602 if(rv == 0)
603 break;
604 MHD_socket_close_ (fd);
605 fd = -1;
606 }
607 freeaddrinfo(res);
608
609 return fd;
610}
611
612
613static void
614spdy_socket_make_non_block(int fd)
615{
616 int flags;
617 int rv;
618
619 while((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR);
620
621 if(flags == -1)
622 spdy_dief("fcntl", strerror(errno));
623
624 while((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR);
625
626 if(rv == -1)
627 spdy_dief("fcntl", strerror(errno));
628}
629
630
631/*
632 * Setting TCP_NODELAY is not mandatory for the SPDY protocol.
633 */
634static void
635spdy_socket_set_tcp_nodelay(int fd)
636{
637 int val = 1;
638 int rv;
639
640 rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val));
641 if(rv == -1)
642 spdy_dief("setsockopt", strerror(errno));
643}
644
645/*
646 * Update |pollfd| based on the state of |connection|.
647 */
648 /*
649void
650spdy_ctl_poll(struct pollfd *pollfd,
651 struct SPDY_Connection *connection)
652{
653 pollfd->events = 0;
654 if(spdylay_session_want_read(connection->session) ||
655 connection->want_io & WANT_READ)
656 {
657 pollfd->events |= POLLIN;
658 }
659 if(spdylay_session_want_write(connection->session) ||
660 connection->want_io & WANT_WRITE)
661 {
662 pollfd->events |= POLLOUT;
663 }
664}*/
665
666
667/*
668 * Update |selectfd| based on the state of |connection|.
669 */
670bool
671spdy_ctl_select(fd_set * read_fd_set,
672 fd_set * write_fd_set,
673 fd_set * except_fd_set,
674 struct SPDY_Connection *connection)
675{
676 (void)except_fd_set;
677
678 bool ret = false;
679
680 if(spdylay_session_want_read(connection->session) ||
681 connection->want_io & WANT_READ)
682 {
683 FD_SET(connection->fd, read_fd_set);
684 ret = true;
685 }
686 if(spdylay_session_want_write(connection->session) ||
687 connection->want_io & WANT_WRITE)
688 {
689 FD_SET(connection->fd, write_fd_set);
690 ret = true;
691 }
692
693 return ret;
694}
695
696
697/*
698 * Performs the network I/O.
699 */
700int
701spdy_exec_io(struct SPDY_Connection *connection)
702{
703 int rv;
704
705 rv = spdylay_session_recv(connection->session);
706 if(rv != 0)
707 {
708 PRINT_INFO2("spdylay_session_recv %i", rv);
709 return rv;
710 }
711 rv = spdylay_session_send(connection->session);
712 if(rv != 0)
713 PRINT_INFO2("spdylay_session_send %i", rv);
714
715 return rv;
716}
717
718
719/*
720 * Fetches the resource denoted by |uri|.
721 */
722struct SPDY_Connection *
723spdy_connect(const struct URI *uri,
724 uint16_t port,
725 bool is_tls)
726{
727 spdylay_session_callbacks callbacks;
728 int fd;
729 SSL *ssl=NULL;
730 struct SPDY_Connection * connection = NULL;
731 int rv;
732
733 spdy_setup_spdylay_callbacks(&callbacks);
734
735 /* Establish connection and setup SSL */
736 PRINT_INFO2("connecting to %s:%i", uri->host, port);
737 fd = spdy_socket_connect_to(uri->host, port);
738 if(fd == -1)
739 {
740 PRINT_INFO("Could not open file descriptor");
741 return NULL;
742 }
743
744 if(is_tls)
745 {
746 ssl = SSL_new(glob_opt.ssl_ctx);
747 if(ssl == NULL) {
748 spdy_dief("SSL_new", ERR_error_string(ERR_get_error(), NULL));
749 }
750
751 //TODO non-blocking
752 /* To simplify the program, we perform SSL/TLS handshake in blocking
753 I/O. */
754 glob_opt.spdy_proto_version = 0;
755 rv = spdy_ssl_handshake(ssl, fd);
756 if(rv <= 0 || (glob_opt.spdy_proto_version != 3 && glob_opt.spdy_proto_version != 2))
757 {
758 PRINT_INFO("Closing SSL");
759 //no spdy on the other side
760 goto free_and_fail;
761 }
762 }
763 else
764 {
765 glob_opt.spdy_proto_version = 3;
766 }
767
768 if(NULL == (connection = au_malloc(sizeof(struct SPDY_Connection))))
769 goto free_and_fail;
770
771 connection->is_tls = is_tls;
772 connection->ssl = ssl;
773 connection->want_io = IO_NONE;
774 if(NULL == (connection->host = strdup(uri->host)))
775 goto free_and_fail;
776
777 /* Here make file descriptor non-block */
778 spdy_socket_make_non_block(fd);
779 spdy_socket_set_tcp_nodelay(fd);
780
781 PRINT_INFO2("[INFO] SPDY protocol version = %d\n", glob_opt.spdy_proto_version);
782 rv = spdylay_session_client_new(&(connection->session), glob_opt.spdy_proto_version,
783 &callbacks, connection);
784 if(rv != 0) {
785 spdy_diec("spdylay_session_client_new", rv);
786 }
787
788 connection->fd = fd;
789
790 return connection;
791
792 //for GOTO
793 free_and_fail:
794 if(NULL != connection)
795 {
796 free(connection->host);
797 free(connection);
798 }
799
800 if(is_tls)
801 SSL_shutdown(ssl);
802
803 MHD_socket_close_ (fd);
804
805 if(is_tls)
806 SSL_free(ssl);
807
808 return NULL;
809}
810
811
812void
813spdy_free_connection(struct SPDY_Connection * connection)
814{
815 struct Proxy *proxy;
816 struct Proxy *proxy_next;
817
818 if(NULL != connection)
819 {
820 for(proxy = connection->proxies_head; NULL != proxy; proxy=proxy_next)
821 {
822 proxy_next = proxy->next;
823 DLL_remove(connection->proxies_head, connection->proxies_tail, proxy);
824 proxy->spdy_active = false;
825 proxy->spdy_error = true;
826 PRINT_INFO2("spdy_free_connection for id %i", proxy->id);
827 if(!proxy->http_active)
828 {
829 free_proxy(proxy);
830 }
831 }
832 spdylay_session_del(connection->session);
833 SSL_free(connection->ssl);
834 free(connection->host);
835 free(connection);
836 //connection->session = NULL;
837 }
838}
839
840
841int
842spdy_request(const char **nv,
843 struct Proxy *proxy,
844 bool with_body)
845{
846 int ret;
847 uint16_t port;
848 struct SPDY_Connection *connection;
849 spdylay_data_provider post_data;
850
851 if(glob_opt.only_proxy)
852 {
853 connection = glob_opt.spdy_connection;
854 }
855 else
856 {
857 connection = glob_opt.spdy_connections_head;
858 while(NULL != connection)
859 {
860 if(0 == strcasecmp(proxy->uri->host, connection->host))
861 break;
862 connection = connection->next;
863 }
864
865 if(NULL == connection)
866 {
867 //connect to host
868 port = proxy->uri->port;
869 if(0 == port) port = 443;
870 connection = spdy_connect(proxy->uri, port, true);
871 if(NULL != connection)
872 {
873 DLL_insert(glob_opt.spdy_connections_head, glob_opt.spdy_connections_tail, connection);
874 glob_opt.total_spdy_connections++;
875 }
876 else
877 connection = glob_opt.spdy_connection;
878 }
879 }
880
881 if(NULL == connection)
882 {
883 PRINT_INFO("there is no proxy!");
884 return -1;
885 }
886
887 proxy->spdy_connection = connection;
888 if(with_body)
889 {
890 post_data.source.ptr = proxy;
891 post_data.read_callback = &spdy_cb_data_source_read;
892 ret = spdylay_submit_request(connection->session, 0, nv, &post_data, proxy);
893 }
894 else
895 ret = spdylay_submit_request(connection->session, 0, nv, NULL, proxy);
896
897 if(ret != 0) {
898 spdy_diec("spdylay_spdy_submit_request", ret);
899 }
900 PRINT_INFO2("adding proxy %i", proxy->id);
901 if(NULL != connection->proxies_head)
902 PRINT_INFO2("before proxy %i", connection->proxies_head->id);
903 DLL_insert(connection->proxies_head, connection->proxies_tail, proxy);
904
905 return ret;
906}
907
908/*
909void
910spdy_get_pollfdset(struct pollfd fds[],
911 struct SPDY_Connection *connections[],
912 unsigned int max_size,
913 nfds_t *real_size)
914{
915 struct SPDY_Connection *connection;
916 struct Proxy *proxy;
917
918 *real_size = 0;
919 if(max_size<1)
920 return;
921
922 if(NULL != glob_opt.spdy_connection)
923 {
924 spdy_ctl_poll(&(fds[*real_size]), glob_opt.spdy_connection);
925 if(!fds[*real_size].events)
926 {
927 //PRINT_INFO("TODO drop connection");
928 glob_opt.streams_opened -= glob_opt.spdy_connection->streams_opened;
929
930 for(proxy = glob_opt.spdy_connection->proxies_head; NULL != proxy; proxy=proxy->next)
931 {
932 abort();
933 DLL_remove(glob_opt.spdy_connection->proxies_head, glob_opt.spdy_connection->proxies_tail, proxy);
934 proxy->spdy_active = false;
935 }
936 spdy_free_connection(glob_opt.spdy_connection);
937 glob_opt.spdy_connection = NULL;
938 }
939 else
940 {
941 fds[*real_size].fd = glob_opt.spdy_connection->fd;
942 connections[*real_size] = glob_opt.spdy_connection;
943 ++(*real_size);
944 }
945 }
946
947 connection = glob_opt.spdy_connections_head;
948
949 while(NULL != connection && *real_size < max_size)
950 {
951 assert(!glob_opt.only_proxy);
952 spdy_ctl_poll(&(fds[*real_size]), connection);
953 if(!fds[*real_size].events)
954 {
955 //PRINT_INFO("TODO drop connection");
956 glob_opt.streams_opened -= connection->streams_opened;
957 DLL_remove(glob_opt.spdy_connections_head, glob_opt.spdy_connections_tail, connection);
958 glob_opt.total_spdy_connections--;
959
960 for(proxy = connection->proxies_head; NULL != proxy; proxy=proxy->next)
961 {
962 abort();
963 DLL_remove(connection->proxies_head, connection->proxies_tail, proxy);
964 proxy->spdy_active = false;
965 }
966 spdy_free_connection(connection);
967 }
968 else
969 {
970 fds[*real_size].fd = connection->fd;
971 connections[*real_size] = connection;
972 ++(*real_size);
973 }
974 connection = connection->next;
975 }
976
977 //, "TODO max num of conn reached; close something"
978 assert(NULL == connection);
979}
980*/
981
982int
983spdy_get_selectfdset(fd_set * read_fd_set,
984 fd_set * write_fd_set,
985 fd_set * except_fd_set,
986 struct SPDY_Connection *connections[],
987 unsigned int max_size,
988 nfds_t *real_size)
989{
990 struct SPDY_Connection *connection;
991 struct SPDY_Connection *next_connection;
992 bool ret;
993 int maxfd = 0;
994
995 *real_size = 0;
996 if(max_size<1)
997 return 0;
998
999 if(NULL != glob_opt.spdy_connection)
1000 {
1001 ret = spdy_ctl_select(read_fd_set,
1002 write_fd_set,
1003 except_fd_set, glob_opt.spdy_connection);
1004 if(!ret)
1005 {
1006 glob_opt.streams_opened -= glob_opt.spdy_connection->streams_opened;
1007
1008 PRINT_INFO("spdy_free_connection in spdy_get_selectfdset");
1009 spdy_free_connection(glob_opt.spdy_connection);
1010 glob_opt.spdy_connection = NULL;
1011 }
1012 else
1013 {
1014 connections[*real_size] = glob_opt.spdy_connection;
1015 ++(*real_size);
1016 if(maxfd < glob_opt.spdy_connection->fd) maxfd = glob_opt.spdy_connection->fd;
1017 }
1018 }
1019
1020 connection = glob_opt.spdy_connections_head;
1021
1022 while(NULL != connection && *real_size < max_size)
1023 {
1024 assert(!glob_opt.only_proxy);
1025 ret = spdy_ctl_select(read_fd_set,
1026 write_fd_set,
1027 except_fd_set, connection);
1028
1029 next_connection = connection->next;
1030 if(!ret)
1031 {
1032 glob_opt.streams_opened -= connection->streams_opened;
1033 DLL_remove(glob_opt.spdy_connections_head, glob_opt.spdy_connections_tail, connection);
1034 glob_opt.total_spdy_connections--;
1035
1036 PRINT_INFO("spdy_free_connection in spdy_get_selectfdset");
1037 spdy_free_connection(connection);
1038 }
1039 else
1040 {
1041 connections[*real_size] = connection;
1042 ++(*real_size);
1043 if(maxfd < connection->fd) maxfd = connection->fd;
1044 }
1045 connection = next_connection;
1046 }
1047
1048 //, "TODO max num of conn reached; close something"
1049 assert(NULL == connection);
1050
1051 return maxfd;
1052}
1053
1054/*
1055void
1056spdy_run(struct pollfd fds[],
1057 struct SPDY_Connection *connections[],
1058 int size)
1059{
1060 int i;
1061 int ret;
1062 struct Proxy *proxy;
1063
1064 for(i=0; i<size; ++i)
1065 {
1066 // PRINT_INFO2("exec about to be called for %s", connections[i]->host);
1067 if(fds[i].revents & (POLLIN | POLLOUT))
1068 {
1069 ret = spdy_exec_io(connections[i]);
1070 //PRINT_INFO2("%i",ret);
1071 //if((spdy_pollfds[i].revents & POLLHUP) || (spdy_pollfds[0].revents & POLLERR))
1072 // PRINT_INFO("SPDY SPDY_Connection error");
1073
1074 //TODO POLLRDHUP
1075 // always close on ret != 0?
1076
1077 if(0 != ret)
1078 {
1079 glob_opt.streams_opened -= connections[i]->streams_opened;
1080 if(connections[i] == glob_opt.spdy_connection)
1081 {
1082 glob_opt.spdy_connection = NULL;
1083 }
1084 else
1085 {
1086 DLL_remove(glob_opt.spdy_connections_head, glob_opt.spdy_connections_tail, connections[i]);
1087 glob_opt.total_spdy_connections--;
1088 }
1089 for(proxy = connections[i]->proxies_head; NULL != proxy; proxy=proxy->next)
1090 {
1091 abort();
1092 DLL_remove(connections[i]->proxies_head, connections[i]->proxies_tail, proxy);
1093 proxy->spdy_active = false;
1094 proxy->spdy_error = true;
1095 PRINT_INFO2("spdy_free_connection for id %i", proxy->id);
1096 }
1097 PRINT_INFO("spdy_free_connection in loop");
1098 spdy_free_connection(connections[i]);
1099 }
1100 }
1101 else
1102 PRINT_INFO("not called");
1103 }
1104}
1105*/
1106
1107void
1108spdy_run_select(fd_set * read_fd_set,
1109 fd_set * write_fd_set,
1110 fd_set * except_fd_set,
1111 struct SPDY_Connection *connections[],
1112 int size)
1113{
1114 int i;
1115 int ret;
1116
1117 for(i=0; i<size; ++i)
1118 {
1119 // PRINT_INFO2("exec about to be called for %s", connections[i]->host);
1120 if(FD_ISSET(connections[i]->fd, read_fd_set) || FD_ISSET(connections[i]->fd, write_fd_set) || FD_ISSET(connections[i]->fd, except_fd_set))
1121 {
1122 //raise(SIGINT);
1123 ret = spdy_exec_io(connections[i]);
1124
1125 if(0 != ret)
1126 {
1127 glob_opt.streams_opened -= connections[i]->streams_opened;
1128 if(connections[i] == glob_opt.spdy_connection)
1129 {
1130 glob_opt.spdy_connection = NULL;
1131 }
1132 else
1133 {
1134 DLL_remove(glob_opt.spdy_connections_head, glob_opt.spdy_connections_tail, connections[i]);
1135 glob_opt.total_spdy_connections--;
1136 }
1137 PRINT_INFO("in spdy_run_select");
1138 spdy_free_connection(connections[i]);
1139 }
1140 }
1141 else
1142 {
1143 PRINT_INFO("not called");
1144 //PRINT_INFO2("connection->want_io %i",connections[i]->want_io);
1145 //PRINT_INFO2("read %i",spdylay_session_want_read(connections[i]->session));
1146 //PRINT_INFO2("write %i",spdylay_session_want_write(connections[i]->session));
1147 //raise(SIGINT);
1148 }
1149 }
1150}
diff --git a/src/examples/mhd2spdy_spdy.h b/src/examples/mhd2spdy_spdy.h
deleted file mode 100644
index 4207c622..00000000
--- a/src/examples/mhd2spdy_spdy.h
+++ /dev/null
@@ -1,102 +0,0 @@
1/*
2 Copyright Copyright (C) 2013 Andrey Uzunov
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17
18/**
19 * @file mhd2spdy_spdy.h
20 * @brief SPDY part of the proxy. libspdylay is used for the client side.
21 * @author Andrey Uzunov
22 */
23
24#ifndef SPDY_H
25#define SPDY_H
26
27#include "mhd2spdy_structures.h"
28
29
30struct SPDY_Connection *
31spdy_connect(const struct URI *uri,
32 uint16_t port,
33 bool is_tls);
34
35
36void
37spdy_ctl_poll(struct pollfd *pollfd,
38 struct SPDY_Connection *connection);
39
40
41bool
42spdy_ctl_select(fd_set * read_fd_set,
43 fd_set * write_fd_set,
44 fd_set * except_fd_set,
45 struct SPDY_Connection *connection);
46
47
48int
49spdy_exec_io(struct SPDY_Connection *connection);
50
51
52void
53spdy_diec(const char *func,
54 int error_code);
55
56
57int
58spdy_request(const char **nv,
59 struct Proxy *proxy,
60 bool with_body);
61
62
63void
64spdy_ssl_init_ssl_ctx(SSL_CTX *ssl_ctx,
65 uint16_t *spdy_proto_version);
66
67
68void
69spdy_free_connection(struct SPDY_Connection * connection);
70
71
72void
73spdy_get_pollfdset(struct pollfd fds[],
74 struct SPDY_Connection *connections[],
75 unsigned int max_size,
76 nfds_t *real_size);
77
78
79int
80spdy_get_selectfdset(fd_set * read_fd_set,
81 fd_set * write_fd_set,
82 fd_set * except_fd_set,
83 struct SPDY_Connection *connections[],
84 unsigned int max_size,
85 nfds_t *real_size);
86
87
88void
89spdy_run(struct pollfd fds[],
90 struct SPDY_Connection *connections[],
91 int size);
92
93
94void
95spdy_run_select(fd_set * read_fd_set,
96 fd_set * write_fd_set,
97 fd_set * except_fd_set,
98 struct SPDY_Connection *connections[],
99 int size);
100
101
102#endif
diff --git a/src/examples/mhd2spdy_structures.c b/src/examples/mhd2spdy_structures.c
deleted file mode 100644
index 6d4a4076..00000000
--- a/src/examples/mhd2spdy_structures.c
+++ /dev/null
@@ -1,162 +0,0 @@
1/*
2 Copyright Copyright (C) 2013 Andrey Uzunov
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17
18/**
19 * @file mhd2spdy_structures.h
20 * @brief Common functions, macros.
21 * @author Andrey Uzunov
22 */
23
24#include "mhd2spdy_structures.h"
25
26
27void
28free_uri(struct URI * uri)
29{
30 if(NULL != uri)
31 {
32 free(uri->full_uri);
33 free(uri->scheme);
34 free(uri->host_and_port);
35 free(uri->host);
36 free(uri->path);
37 free(uri->path_and_more);
38 free(uri->query);
39 free(uri->fragment);
40 uri->port = 0;
41 free(uri);
42 }
43}
44
45
46int
47init_parse_uri(regex_t * preg)
48{
49 // RFC 2396
50 // ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
51 /*
52 scheme = $2
53 authority = $4
54 path = $5
55 query = $7
56 fragment = $9
57 */
58
59 return regcomp(preg, "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?", REG_EXTENDED);
60}
61
62void
63deinit_parse_uri(regex_t * preg)
64{
65 regfree(preg);
66}
67
68int
69parse_uri(regex_t * preg,
70 char * full_uri,
71 struct URI ** uri)
72{
73 int ret;
74 char *colon;
75 long long port;
76 size_t nmatch = 10;
77 regmatch_t pmatch[10];
78
79 if (0 != (ret = regexec(preg, full_uri, nmatch, pmatch, 0)))
80 return ret;
81
82 *uri = au_malloc(sizeof(struct URI));
83 if(NULL == *uri)
84 return -200;
85
86 (*uri)->full_uri = strdup(full_uri);
87
88 asprintf(&((*uri)->scheme), "%.*s",pmatch[2].rm_eo - pmatch[2].rm_so, &full_uri[pmatch[2].rm_so]);
89 asprintf(&((*uri)->host_and_port), "%.*s",pmatch[4].rm_eo - pmatch[4].rm_so, &full_uri[pmatch[4].rm_so]);
90 asprintf(&((*uri)->path), "%.*s",pmatch[5].rm_eo - pmatch[5].rm_so, &full_uri[pmatch[5].rm_so]);
91 asprintf(&((*uri)->path_and_more), "%.*s",pmatch[9].rm_eo - pmatch[5].rm_so, &full_uri[pmatch[5].rm_so]);
92 asprintf(&((*uri)->query), "%.*s",pmatch[7].rm_eo - pmatch[7].rm_so, &full_uri[pmatch[7].rm_so]);
93 asprintf(&((*uri)->fragment), "%.*s",pmatch[9].rm_eo - pmatch[9].rm_so, &full_uri[pmatch[9].rm_so]);
94
95 colon = strrchr((*uri)->host_and_port, ':');
96 if(NULL == colon)
97 {
98 (*uri)->host = strdup((*uri)->host_and_port);
99 (*uri)->port = 0;
100
101 return 0;
102 }
103
104 port = atoi(colon + 1);
105 if(port<1 || port >= 256 * 256)
106 {
107 free_uri(*uri);
108 return -100;
109 }
110 (*uri)->port = port;
111 asprintf(&((*uri)->host), "%.*s", (int)(colon - (*uri)->host_and_port), (*uri)->host_and_port);
112
113 return 0;
114}
115
116
117void
118free_proxy(struct Proxy *proxy)
119{
120 PRINT_INFO2("free proxy called for '%s'", proxy->url);
121 if(NULL != proxy->http_body && proxy->http_body_size > 0)
122 UPDATE_STAT(glob_stat.spdy_bytes_received_and_dropped, proxy->http_body_size);
123 free(proxy->http_body);
124 free_uri(proxy->uri);
125 free(proxy->url);
126 free(proxy->http_uri);
127 free(proxy);
128}
129
130
131void *au_malloc(size_t size)
132{
133 void *new_memory;
134
135 new_memory = malloc(size);
136 if(NULL != new_memory)
137 {
138 glob_opt.global_memory += size;
139 memset(new_memory, 0, size);
140 }
141 return new_memory;
142}
143
144
145bool
146copy_buffer(const void *src, size_t src_size, void **dst, size_t *dst_size)
147{
148 if(0 == src_size)
149 return true;
150
151 if(NULL == *dst)
152 *dst = malloc(src_size);
153 else
154 *dst = realloc(*dst, src_size + *dst_size);
155 if(NULL == *dst)
156 return false;
157
158 memcpy(*dst + *dst_size, src, src_size);
159 *dst_size += src_size;
160
161 return true;
162}
diff --git a/src/examples/mhd2spdy_structures.h b/src/examples/mhd2spdy_structures.h
deleted file mode 100644
index f5679341..00000000
--- a/src/examples/mhd2spdy_structures.h
+++ /dev/null
@@ -1,296 +0,0 @@
1/*
2 Copyright Copyright (C) 2013 Andrey Uzunov
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17
18/**
19 * @file mhd2spdy_structures.h
20 * @brief Common structures, functions, macros and global variables.
21 * @author Andrey Uzunov
22 */
23#ifndef STRUCTURES_H
24#define STRUCTURES_H
25
26#define _GNU_SOURCE
27
28#include <unistd.h>
29#include <stdlib.h>
30#include <stdint.h>
31#include <stdbool.h>
32#include <string.h>
33#include <stdio.h>
34#include <ctype.h>
35#include <errno.h>
36#include <assert.h>
37#include <microhttpd.h>
38#include <signal.h>
39#include <poll.h>
40#include <fcntl.h>
41#include <regex.h>
42#include <sys/types.h>
43#include <sys/socket.h>
44#include <netdb.h>
45#include <netinet/in.h>
46#include <netinet/tcp.h>
47#include <openssl/ssl.h>
48#include <openssl/err.h>
49#include <spdylay/spdylay.h>
50#include <getopt.h>
51
52
53/* WANT_READ if SSL connection needs more input; or WANT_WRITE if it
54 needs more output; or IO_NONE. This is necessary because SSL/TLS
55 re-negotiation is possible at any time. Spdylay API offers
56 similar functions like spdylay_session_want_read() and
57 spdylay_session_want_write() but they do not take into account
58 SSL connection. */
59enum
60{
61 IO_NONE,
62 WANT_READ,
63 WANT_WRITE
64};
65
66
67struct Proxy;
68
69
70struct SPDY_Connection {
71 SSL *ssl;
72 spdylay_session *session;
73 struct SPDY_Connection *prev;
74 struct SPDY_Connection *next;
75 struct Proxy *proxies_head;
76 struct Proxy *proxies_tail;
77 char *host;
78 int fd;
79 int want_io;
80 uint counter;
81 uint streams_opened;
82 bool is_tls;
83};
84
85
86struct URI
87{
88 char * full_uri;
89 char * scheme;
90 char * host_and_port;
91 char * host;
92 char * path;
93 char * path_and_more;
94 char * query;
95 char * fragment;
96 uint16_t port;
97};
98
99
100struct HTTP_URI;
101
102
103struct Proxy
104{
105 struct MHD_Connection *http_connection;
106 struct MHD_Response *http_response;
107 struct URI *uri;
108 struct HTTP_URI *http_uri;
109 struct SPDY_Connection *spdy_connection;
110 struct Proxy *next;
111 struct Proxy *prev;
112 char *url;
113 char *version;
114 void *http_body;
115 void *received_body;
116 size_t http_body_size;
117 size_t received_body_size;
118 ssize_t length;
119 int status;
120 int id;
121 int32_t stream_id;
122 bool done;
123 bool http_error;
124 bool spdy_error;
125 bool http_active;
126 bool spdy_active;
127 bool receiving_done;
128};
129
130
131struct HTTP_URI
132{
133 char * uri;
134 struct Proxy * proxy;
135};
136
137
138struct SPDY_Headers
139{
140 const char **nv;
141 int num;
142 int cnt;
143};
144
145
146struct global_options
147{
148 char *spdy2http_str;
149 struct SPDY_Connection *spdy_connection;
150 struct SPDY_Connection *spdy_connections_head;
151 struct SPDY_Connection *spdy_connections_tail;
152 int streams_opened;
153 int responses_pending;
154 regex_t uri_preg;
155 size_t global_memory;
156 SSL_CTX *ssl_ctx;
157 uint32_t total_spdy_connections;
158 uint16_t spdy_proto_version;
159 uint16_t listen_port;
160 bool verbose;
161 bool only_proxy;
162 bool spdy_data_received;
163 bool statistics;
164 bool ignore_rst_stream;
165}
166glob_opt;
167
168
169struct global_statistics
170{
171 //unsigned long long http_bytes_sent;
172 //unsigned long long http_bytes_received;
173 unsigned long long spdy_bytes_sent;
174 unsigned long long spdy_bytes_received;
175 unsigned long long spdy_bytes_received_and_dropped;
176}
177glob_stat;
178
179
180//forbidden headers
181#define SPDY_HTTP_HEADER_TRANSFER_ENCODING "transfer-encoding"
182#define SPDY_HTTP_HEADER_PROXY_CONNECTION "proxy-connection"
183#define SPDY_HTTP_HEADER_KEEP_ALIVE "keep-alive"
184#define SPDY_HTTP_HEADER_CONNECTION "connection"
185
186#define MAX_SPDY_CONNECTIONS 100
187
188#define SPDY_MAX_OUTLEN 4096
189
190/**
191 * Insert an element at the head of a DLL. Assumes that head, tail and
192 * element are structs with prev and next fields.
193 *
194 * @param head pointer to the head of the DLL (struct ? *)
195 * @param tail pointer to the tail of the DLL (struct ? *)
196 * @param element element to insert (struct ? *)
197 */
198#define DLL_insert(head,tail,element) do { \
199 (element)->next = (head); \
200 (element)->prev = NULL; \
201 if ((tail) == NULL) \
202 (tail) = element; \
203 else \
204 (head)->prev = element; \
205 (head) = (element); } while (0)
206
207
208/**
209 * Remove an element from a DLL. Assumes
210 * that head, tail and element are structs
211 * with prev and next fields.
212 *
213 * @param head pointer to the head of the DLL (struct ? *)
214 * @param tail pointer to the tail of the DLL (struct ? *)
215 * @param element element to remove (struct ? *)
216 */
217#define DLL_remove(head,tail,element) do { \
218 if ((element)->prev == NULL) \
219 (head) = (element)->next; \
220 else \
221 (element)->prev->next = (element)->next; \
222 if ((element)->next == NULL) \
223 (tail) = (element)->prev; \
224 else \
225 (element)->next->prev = (element)->prev; \
226 (element)->next = NULL; \
227 (element)->prev = NULL; } while (0)
228
229
230#define PRINT_INFO(msg) do{\
231 if(glob_opt.verbose){\
232 printf("%i:%s\n", __LINE__, msg);\
233 fflush(stdout);\
234 }\
235 }\
236 while(0)
237
238
239#define PRINT_INFO2(fmt, ...) do{\
240 if(glob_opt.verbose){\
241 printf("%i\n", __LINE__);\
242 printf(fmt,##__VA_ARGS__);\
243 printf("\n");\
244 fflush(stdout);\
245 }\
246 }\
247 while(0)
248
249
250#define DIE(msg) do{\
251 printf("FATAL ERROR (line %i): %s\n", __LINE__, msg);\
252 fflush(stdout);\
253 exit(EXIT_FAILURE);\
254 }\
255 while(0)
256
257
258#define UPDATE_STAT(stat, value) do{\
259 if(glob_opt.statistics)\
260 {\
261 stat += value;\
262 }\
263 }\
264 while(0)
265
266
267void
268free_uri(struct URI * uri);
269
270
271int
272init_parse_uri(regex_t * preg);
273
274
275void
276deinit_parse_uri(regex_t * preg);
277
278
279int
280parse_uri(regex_t * preg,
281 char * full_uri,
282 struct URI ** uri);
283
284
285void
286free_proxy(struct Proxy *proxy);
287
288
289void *
290au_malloc(size_t size);
291
292
293bool
294copy_buffer(const void *src, size_t src_size, void **dst, size_t *dst_size);
295
296#endif
diff --git a/src/examples/spdy_event_loop.c b/src/examples/spdy_event_loop.c
deleted file mode 100644
index 6b7192c3..00000000
--- a/src/examples/spdy_event_loop.c
+++ /dev/null
@@ -1,487 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2012 Andrey Uzunov
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file event_loop.c
21 * @brief shows how to use the daemon. THIS IS MAINLY A TEST AND DEBUG
22 * PROGRAM
23 * @author Andrey Uzunov
24 */
25
26#include "platform.h"
27#include <unistd.h>
28#include <stdlib.h>
29#include <stdint.h>
30#include <stdbool.h>
31#include <string.h>
32#include <stdio.h>
33#include <ctype.h>
34#include <errno.h>
35#include "microspdy.h"
36#include <sys/time.h>
37#include <time.h>
38#ifndef MINGW
39#include <arpa/inet.h>
40#endif
41//#include "../framinglayer/structures.h"
42//#include "../applicationlayer/alstructures.h"
43
44static int run = 1;
45
46static int run2 = 1;
47
48
49static uint64_t loops;
50
51static time_t start;
52
53
54static void
55new_session_callback (void *cls,
56 struct SPDY_Session * session)
57{
58 (void)cls;
59
60 char ipstr[1024];
61
62 struct sockaddr *addr;
63 socklen_t addr_len = SPDY_get_remote_addr(session, &addr);
64
65 if(!addr_len)
66 {
67 printf("SPDY_get_remote_addr");
68 abort();
69 }
70
71 if(AF_INET == addr->sa_family)
72 {
73 struct sockaddr_in * addr4 = (struct sockaddr_in *) addr;
74 if(NULL == inet_ntop(AF_INET, &(addr4->sin_addr), ipstr, sizeof(ipstr)))
75 {
76 printf("inet_ntop");
77 abort();
78 }
79 printf("New connection from: %s:%i\n", ipstr, ntohs(addr4->sin_port));
80
81 }
82 else if(AF_INET6 == addr->sa_family)
83 {
84 struct sockaddr_in6 * addr6 = (struct sockaddr_in6 *) addr;
85 if(NULL == inet_ntop(AF_INET6, &(addr6->sin6_addr), ipstr, sizeof(ipstr)))
86 {
87 printf("inet_ntop");
88 abort();
89 }
90 printf("New connection from: %s:%i\n", ipstr, ntohs(addr6->sin6_port));
91
92 }
93}
94
95
96static void
97session_closed_handler (void *cls,
98 struct SPDY_Session * session,
99 int by_client)
100{
101 (void)cls;
102 (void)session;
103
104 //printf("session_closed_handler called\n");
105
106 if(SPDY_YES != by_client)
107 {
108 //killchild(child,"wrong by_client");
109 printf("session closed by server\n");
110 }
111 else
112 {
113 printf("session closed by client\n");
114 }
115
116 //session_closed_called = 1;
117}
118
119
120static void
121response_done_callback(void *cls,
122 struct SPDY_Response *response,
123 struct SPDY_Request *request,
124 enum SPDY_RESPONSE_RESULT status,
125 bool streamopened)
126{
127 (void)streamopened;
128 if(strcmp(cls, "/close (daemon1)") == 0)
129 run = 0;
130 else {
131 if(strcmp(cls, "/close (daemon2)") == 0) run2 = 0;
132 loops = 0;
133 start = time(NULL);
134 }
135 if(SPDY_RESPONSE_RESULT_SUCCESS != status)
136 {
137 printf("not sent frame cause %i", status);
138 }
139 printf("answer for %s was sent\n", (char*)cls);
140 //printf("raw sent headers %s\n", (char *)(response->headers)+8);
141
142 SPDY_destroy_request(request);
143 SPDY_destroy_response(response);
144 free(cls);
145}
146
147/*
148static int
149print_headers (void *cls,
150 const char *name, const char *value)
151{
152 (void)cls;
153 printf("%s: %s\n",name,value);
154 return SPDY_YES;
155}
156 */
157
158
159/*
160void
161new_request_cb (void *cls,
162 struct SPDY_Request * request,
163 uint8_t priority,
164 const char *method,
165 const char *path,
166 const char *version,
167 const char *host,
168 const char *scheme,
169 struct SPDY_NameValue * headers)
170{
171 (void)cls;
172 (void)request;
173 printf("Priority: %i\nHTTP headers, scheme: %s\n\n%s %s %s\nHost: %s\n", priority,scheme,method,path,version,host);
174 SPDY_name_value_iterate(headers, &print_headers, NULL);
175}
176*/
177
178
179static int
180append_headers_to_data (void *cls,
181 const char *name, const char * const *value, int num_values)
182{
183 char **data = cls;
184 void *tofree = *data;
185 int i;
186
187 if(num_values)
188 for(i=0;i<num_values;++i)
189 {
190 asprintf(data,"%s%s: %s\n", *data,name,value[i]);
191 }
192 else
193 asprintf(data,"%s%s: \n", *data,name);
194
195 free(tofree);
196 return SPDY_YES;
197}
198
199
200static void
201standard_request_handler(void *cls,
202 struct SPDY_Request * request,
203 uint8_t priority,
204 const char *method,
205 const char *path,
206 const char *version,
207 const char *host,
208 const char *scheme,
209 struct SPDY_NameValue * headers,
210 bool more)
211{
212 (void)more;
213
214 char *html;
215 char *data;
216 struct SPDY_Response *response=NULL;
217
218 printf("received request for '%s %s %s'\n", method, path, version);
219 if(strcmp(path,"/main.css")==0)
220 {
221 if(NULL != cls)
222 asprintf(&html,"body{color:green;}");
223 else
224 asprintf(&html,"body{color:red;}");
225
226 //struct SPDY_NameValue *headers=SPDY_name_value_create();
227 //SPDY_name_value_add(headers,"content-type","text/css");
228
229 response = SPDY_build_response(200,NULL,SPDY_HTTP_VERSION_1_1,NULL,html,strlen(html));
230 free(html);
231 }
232 else
233 {
234 asprintf(&data,"Priority: %i\nHTTP headers, scheme: %s\n\n%s %s %s\nHost: %s\n", priority,scheme,method,path,version,host);
235
236 SPDY_name_value_iterate(headers, &append_headers_to_data, &data);
237
238 if(strcmp(path,"/close")==0)
239 {
240 asprintf(&html,"<html>"
241 "<body><b>Closing now!<br>This is an answer to the following "
242 "request:</b><br><br><pre>%s</pre></body></html>",data);
243 }
244 else
245 {
246 asprintf(&html,"<html><link href=\"main.css\" rel=\"stylesheet\" type=\"text/css\" />"
247 "<body><b>This is an answer to the following "
248 "request:</b><br><br><pre>%s</pre></body></html>",data);
249 }
250
251 free(data);
252
253 response = SPDY_build_response(200,NULL,SPDY_HTTP_VERSION_1_1,NULL,html,strlen(html));
254 free(html);
255 }
256
257 if(NULL==response){
258 fprintf(stdout,"no response obj\n");
259 abort();
260 }
261
262 char *pathcls;
263 asprintf(&pathcls, "%s (daemon%i)",path,NULL==cls ? 1 : 2);
264 if(SPDY_queue_response(request,response,true,false,&response_done_callback,pathcls)!=SPDY_YES)
265 {
266 fprintf(stdout,"queue\n");
267 abort();
268 }
269}
270
271
272static int
273new_post_data_cb (void * cls,
274 struct SPDY_Request *request,
275 const void * buf,
276 size_t size,
277 bool more)
278{
279 (void)cls;
280 (void)request;
281 (void)more;
282
283 printf("DATA:\n===============================\n");
284 write(0, buf, size);
285 printf("\n===============================\n");
286 return SPDY_YES;
287}
288
289
290static void
291sig_handler(int signo)
292{
293 (void)signo;
294
295 printf("received signal\n");
296}
297
298
299int
300main (int argc, char *const *argv)
301{
302 if(argc != 2) return 1;
303
304 #ifndef MINGW
305 if (signal(SIGPIPE, sig_handler) == SIG_ERR)
306 printf("\ncan't catch SIGPIPE\n");
307 #endif
308
309 SPDY_init();
310
311 /*
312 struct sockaddr_in addr4;
313 struct in_addr inaddr4;
314 inaddr4.s_addr = htonl(INADDR_ANY);
315 addr4.sin_family = AF_INET;
316 addr4.sin_addr = inaddr4;
317 addr4.sin_port = htons(atoi(argv[1]));
318 */
319
320 struct SPDY_Daemon *daemon = SPDY_start_daemon(atoi(argv[1]),
321 DATA_DIR "cert-and-key.pem",
322 DATA_DIR "cert-and-key.pem",
323 &new_session_callback,&session_closed_handler,&standard_request_handler,&new_post_data_cb,NULL,
324 SPDY_DAEMON_OPTION_SESSION_TIMEOUT, 10,
325 //SPDY_DAEMON_OPTION_SOCK_ADDR, (struct sockaddr *)&addr4,
326 SPDY_DAEMON_OPTION_END);
327
328 if(NULL==daemon){
329 printf("no daemon\n");
330 return 1;
331 }
332
333 /*
334 struct sockaddr_in6 addr6;
335 addr6.sin6_family = AF_INET6;
336 addr6.sin6_addr = in6addr_any;
337 addr6.sin6_port = htons(atoi(argv[1]) + 1);
338 */
339
340 struct SPDY_Daemon *daemon2 = SPDY_start_daemon(atoi(argv[1]) + 1,
341 DATA_DIR "cert-and-key.pem",
342 DATA_DIR "cert-and-key.pem",
343 &new_session_callback,NULL,&standard_request_handler,&new_post_data_cb,&main,
344 //SPDY_DAEMON_OPTION_SESSION_TIMEOUT, 0,
345 //SPDY_DAEMON_OPTION_SOCK_ADDR, (struct sockaddr *)&addr6,
346 //SPDY_DAEMON_OPTION_FLAGS, SPDY_DAEMON_FLAG_ONLY_IPV6,
347 SPDY_DAEMON_OPTION_END);
348
349 if(NULL==daemon2){
350 printf("no daemon\n");
351 return 1;
352 }
353
354 do
355 {
356 unsigned long long timeoutlong=0;
357 struct timeval timeout;
358 volatile int rc; /* select() return code */
359 volatile int ret;
360
361 fd_set read_fd_set;
362 fd_set write_fd_set;
363 fd_set except_fd_set;
364 int maxfd = -1;
365
366 if(run && daemon != NULL)
367 {
368 loops++;
369 FD_ZERO(&read_fd_set);
370 FD_ZERO(&write_fd_set);
371 FD_ZERO(&except_fd_set);
372
373 ret = SPDY_get_timeout(daemon, &timeoutlong);
374 if(SPDY_NO == ret || timeoutlong > 1000)
375 {
376 timeout.tv_sec = 1;
377 timeout.tv_usec = 0;
378 }
379 else
380 {
381 timeout.tv_sec = timeoutlong / 1000;
382 timeout.tv_usec = (timeoutlong % 1000) * 1000;
383 }
384
385 printf("ret=%i; timeoutlong=%llu; sec=%llu; usec=%llu\n", ret, timeoutlong, (long long unsigned)timeout.tv_sec, (long long unsigned)timeout.tv_usec);
386 //raise(SIGINT);
387
388 /* get file descriptors from the transfers */
389 maxfd = SPDY_get_fdset (daemon,
390 &read_fd_set,
391 &write_fd_set,
392 &except_fd_set);
393
394//struct timeval ts1,ts2;
395 //gettimeofday(&ts1, NULL);
396 rc = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout);
397 //gettimeofday(&ts2, NULL);
398 printf("rc %i\n",rc);
399 // printf("time for select %i\n",ts2.tv_usec - ts1.tv_usec);
400 // printf("%i %i %i %i\n",ts1.tv_sec, ts1.tv_usec,ts2.tv_sec, ts2.tv_usec);
401
402 switch(rc) {
403 case -1:
404 /* select error */
405 break;
406 case 0:
407
408 break;
409 default:
410 SPDY_run(daemon);
411
412 break;
413 }
414 }
415 else if(daemon != NULL){
416
417 printf("%lu loops in %llu secs\n", loops, (long long unsigned)(time(NULL) - start));
418 SPDY_stop_daemon(daemon);
419 daemon=NULL;
420 }
421
422 if(run2)
423 {
424 FD_ZERO(&read_fd_set);
425 FD_ZERO(&write_fd_set);
426 FD_ZERO(&except_fd_set);
427
428 ret = SPDY_get_timeout(daemon2, &timeoutlong);
429 //printf("tout %i\n",timeoutlong);
430 if(SPDY_NO == ret || timeoutlong > 1)
431 {
432 //do sth else
433 //sleep(1);
434
435 //try new connection
436 timeout.tv_sec = 1;
437 timeout.tv_usec = 0;
438 }
439 else
440 {
441 timeout.tv_sec = timeoutlong;
442 timeout.tv_usec = 0;//(timeoutlong % 1000) * 1000;
443 }
444
445 //printf("ret=%i; timeoutlong=%i; sec=%i; usec=%i\n", ret, timeoutlong, timeout.tv_sec, timeout.tv_usec);
446 //raise(SIGINT);
447
448 /* get file descriptors from the transfers */
449 maxfd = SPDY_get_fdset (daemon2,
450 &read_fd_set,
451 &write_fd_set,
452 &except_fd_set);
453
454 rc = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout);
455
456 switch(rc) {
457 case -1:
458 /* select error */
459 break;
460 case 0:
461
462 break;
463 default:
464 SPDY_run(daemon2);
465
466 break;
467 }
468 }
469 else if(daemon2 != NULL){
470 SPDY_stop_daemon(daemon2);
471 daemon2=NULL;
472 }
473 }
474 while(run || run2);
475
476 if(daemon != NULL){
477 SPDY_stop_daemon(daemon);
478 }
479 if(daemon2 != NULL){
480 SPDY_stop_daemon(daemon2);
481 }
482
483 SPDY_deinit();
484
485 return 0;
486}
487
diff --git a/src/examples/spdy_fileserver.c b/src/examples/spdy_fileserver.c
deleted file mode 100644
index 0a0254ff..00000000
--- a/src/examples/spdy_fileserver.c
+++ /dev/null
@@ -1,353 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2013 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 fileserver.c
21 * @brief Simple example how the lib can be used for serving
22 * files directly read from the system
23 * @author Andrey Uzunov
24 */
25
26//for asprintf
27#define _GNU_SOURCE
28
29#include <unistd.h>
30#include <stdlib.h>
31#include <stdint.h>
32#include <stdbool.h>
33#include <string.h>
34#include <stdio.h>
35#include <ctype.h>
36#include <errno.h>
37#include "microspdy.h"
38#include "time.h"
39
40
41int run = 1;
42char* basedir;
43
44
45#define GET_MIME_TYPE(fname, mime) do {\
46 unsigned int __len = strlen(fname);\
47 if (__len < 4 || '.' != (fname)[__len - 4]) \
48 { \
49 (mime) = strdup("application/octet-stream");\
50 printf("MIME for %s is applic...\n", (fname));\
51 }\
52 else {\
53 const char * __ext = &(fname)[__len - 3];\
54 if(0 == strcmp(__ext, "jpg")) (mime) = strdup("image/jpeg");\
55 else if(0 == strcmp(__ext, "png")) (mime) = strdup("image/png");\
56 else if(0 == strcmp(__ext, "css")) (mime) = strdup("text/css");\
57 else if(0 == strcmp(__ext, "gif")) (mime) = strdup("image/gif");\
58 else if(0 == strcmp(__ext, "htm")) (mime) = strdup("text/html");\
59 else \
60 { \
61 (mime) = strdup("application/octet-stream");\
62 printf("MIME for %s is applic...\n", (fname));\
63 }\
64 }\
65 if(NULL == (mime))\
66 {\
67 printf("no memory\n");\
68 abort();\
69 }\
70 } while (0)
71
72
73static const char *DAY_NAMES[] =
74 { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
75
76static const char *MONTH_NAMES[] =
77 { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
78 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
79
80//taken from http://stackoverflow.com/questions/2726975/how-can-i-generate-an-rfc1123-date-string-from-c-code-win32
81//and modified for linux
82char *Rfc1123_DateTimeNow()
83{
84 const int RFC1123_TIME_LEN = 29;
85 time_t t;
86 struct tm tm;
87 char * buf = malloc(RFC1123_TIME_LEN+1);
88
89 if (NULL == buf)
90 return NULL;
91 time(&t);
92 gmtime_r( &t, &tm);
93
94 strftime(buf, RFC1123_TIME_LEN+1, "---, %d --- %Y %H:%M:%S GMT", &tm);
95 memcpy(buf, DAY_NAMES[tm.tm_wday], 3);
96 memcpy(buf+8, MONTH_NAMES[tm.tm_mon], 3);
97
98 return buf;
99}
100
101
102ssize_t
103response_callback (void *cls,
104 void *buffer,
105 size_t max,
106 bool *more)
107{
108 FILE *fd =(FILE*)cls;
109
110 int ret = fread(buffer,1,max,fd);
111 *more = feof(fd) == 0;
112
113 //if(!(*more))
114 // fclose(fd);
115
116 return ret;
117}
118
119
120void
121response_done_callback(void *cls,
122 struct SPDY_Response *response,
123 struct SPDY_Request *request,
124 enum SPDY_RESPONSE_RESULT status,
125 bool streamopened)
126{
127 (void)streamopened;
128 (void)status;
129 //printf("answer for %s was sent\n", (char *)cls);
130
131 /*if(SPDY_RESPONSE_RESULT_SUCCESS != status)
132 {
133 printf("answer for %s was NOT sent, %i\n", (char *)cls,status);
134 }*/
135
136 SPDY_destroy_request(request);
137 SPDY_destroy_response(response);
138 if(NULL!=cls)fclose(cls);
139}
140
141void
142standard_request_handler(void *cls,
143 struct SPDY_Request * request,
144 uint8_t priority,
145 const char *method,
146 const char *path,
147 const char *version,
148 const char *host,
149 const char *scheme,
150 struct SPDY_NameValue * headers,
151 bool more)
152{
153 (void)cls;
154 (void)request;
155 (void)priority;
156 (void)host;
157 (void)scheme;
158 (void)headers;
159 (void)method;
160 (void)version;
161 (void)more;
162
163 struct SPDY_Response *response=NULL;
164 struct SPDY_NameValue *resp_headers;
165 char *fname;
166 char *fsize;
167 char *mime=NULL;
168 char *date=NULL;
169 ssize_t filesize = -666;
170 FILE *fd = NULL;
171 int ret = -666;
172
173 //printf("received request for '%s %s %s'\n", method, path, version);
174 if(strlen(path) > 1 && NULL == strstr(path, "..") && '/' == path[0])
175 {
176 asprintf(&fname,"%s%s",basedir,path);
177 if(0 == access(fname, R_OK))
178 {
179 if(NULL == (fd = fopen(fname,"r"))
180 || 0 != (ret = fseek(fd, 0L, SEEK_END))
181 || -1 == (filesize = ftell(fd))
182 || 0 != (ret = fseek(fd, 0L, SEEK_SET)))
183 {
184 printf("Error on opening %s\n%p %i %zd\n",fname, fd, ret, filesize);
185 response = SPDY_build_response(SPDY_HTTP_INTERNAL_SERVER_ERROR,NULL,SPDY_HTTP_VERSION_1_1,NULL,NULL,0);
186 }
187 else
188 {
189 if(NULL == (resp_headers = SPDY_name_value_create()))
190 {
191 printf("SPDY_name_value_create failed\n");
192 abort();
193 }
194
195 date = Rfc1123_DateTimeNow();
196 if(NULL == date
197 || SPDY_YES != SPDY_name_value_add(resp_headers,SPDY_HTTP_HEADER_DATE,date))
198 {
199 printf("SPDY_name_value_add or Rfc1123_DateTimeNow failed\n");
200 abort();
201 }
202 free(date);
203
204 if(-1 == asprintf(&fsize, "%zd", filesize)
205 || SPDY_YES != SPDY_name_value_add(resp_headers,SPDY_HTTP_HEADER_CONTENT_LENGTH,fsize))
206 {
207 printf("SPDY_name_value_add or asprintf failed\n");
208 abort();
209 }
210 free(fsize);
211
212 GET_MIME_TYPE(path,mime);
213 if(SPDY_YES != SPDY_name_value_add(resp_headers,SPDY_HTTP_HEADER_CONTENT_TYPE,mime))
214 {
215 printf("SPDY_name_value_add failed\n");
216 abort();
217 }
218 free(mime);
219
220 if(SPDY_YES != SPDY_name_value_add(resp_headers,SPDY_HTTP_HEADER_SERVER,"libmicrospdy/fileserver"))
221 {
222 printf("SPDY_name_value_add failed\n");
223 abort();
224 }
225
226 response = SPDY_build_response_with_callback(200,NULL,
227 SPDY_HTTP_VERSION_1_1,resp_headers,&response_callback,fd,SPDY_MAX_SUPPORTED_FRAME_SIZE);
228 SPDY_name_value_destroy(resp_headers);
229 }
230
231 if(NULL==response){
232 printf("no response obj\n");
233 abort();
234 }
235
236 if(SPDY_queue_response(request,response,true,false,&response_done_callback,fd)!=SPDY_YES)
237 {
238 printf("queue\n");
239 abort();
240 }
241
242 free(fname);
243 return;
244 }
245 free(fname);
246 }
247
248 if(strcmp(path,"/close")==0)
249 {
250 run = 0;
251 }
252
253 response = SPDY_build_response(SPDY_HTTP_NOT_FOUND,NULL,SPDY_HTTP_VERSION_1_1,NULL,NULL,0);
254 printf("Not found %s\n",path);
255
256 if(NULL==response){
257 printf("no response obj\n");
258 abort();
259 }
260
261 if(SPDY_queue_response(request,response,true,false,&response_done_callback,NULL)!=SPDY_YES)
262 {
263 printf("queue\n");
264 abort();
265 }
266}
267
268int
269main (int argc, char *const *argv)
270{
271 unsigned long long timeoutlong=0;
272 struct timeval timeout;
273 int ret;
274 fd_set read_fd_set;
275 fd_set write_fd_set;
276 fd_set except_fd_set;
277 int maxfd = -1;
278 struct SPDY_Daemon *daemon;
279
280 if(argc != 5)
281 {
282 printf("Usage: %s cert-file key-file base-dir port\n", argv[0]);
283 return 1;
284 }
285
286 SPDY_init();
287
288 daemon = SPDY_start_daemon(atoi(argv[4]),
289 argv[1],
290 argv[2],
291 NULL,
292 NULL,
293 &standard_request_handler,
294 NULL,
295 NULL,
296 SPDY_DAEMON_OPTION_SESSION_TIMEOUT,
297 1800,
298 SPDY_DAEMON_OPTION_END);
299
300 if(NULL==daemon){
301 printf("no daemon\n");
302 return 1;
303 }
304
305 basedir = argv[3];
306
307 do
308 {
309 FD_ZERO(&read_fd_set);
310 FD_ZERO(&write_fd_set);
311 FD_ZERO(&except_fd_set);
312
313 ret = SPDY_get_timeout(daemon, &timeoutlong);
314 if(SPDY_NO == ret || timeoutlong > 1000)
315 {
316 timeout.tv_sec = 1;
317 timeout.tv_usec = 0;
318 }
319 else
320 {
321 timeout.tv_sec = timeoutlong / 1000;
322 timeout.tv_usec = (timeoutlong % 1000) * 1000;
323 }
324
325 maxfd = SPDY_get_fdset (daemon,
326 &read_fd_set,
327 &write_fd_set,
328 &except_fd_set);
329
330 ret = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout);
331
332 switch(ret) {
333 case -1:
334 printf("select error: %i\n", errno);
335 break;
336 case 0:
337
338 break;
339 default:
340 SPDY_run(daemon);
341
342 break;
343 }
344 }
345 while(run);
346
347 SPDY_stop_daemon(daemon);
348
349 SPDY_deinit();
350
351 return 0;
352}
353
diff --git a/src/examples/spdy_response_with_callback.c b/src/examples/spdy_response_with_callback.c
deleted file mode 100644
index 5bd452d2..00000000
--- a/src/examples/spdy_response_with_callback.c
+++ /dev/null
@@ -1,236 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2013 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 response_with_callback.c
21 * @brief shows how to create responses with callbacks
22 * @author Andrey Uzunov
23 */
24
25//for asprintf
26#define _GNU_SOURCE
27
28#include <unistd.h>
29#include <stdlib.h>
30#include <stdint.h>
31#include <stdbool.h>
32#include <string.h>
33#include <stdio.h>
34#include <ctype.h>
35#include <errno.h>
36#include "microspdy.h"
37
38static int run = 1;
39
40
41static ssize_t
42response_callback (void *cls,
43 void *buffer,
44 size_t max,
45 bool *more)
46{
47 FILE *fd =(FILE*)cls;
48
49 int ret = fread(buffer,1,max,fd);
50 *more = feof(fd) == 0;
51
52 if(!(*more))
53 fclose(fd);
54
55 return ret;
56}
57
58
59static void
60response_done_callback(void *cls,
61 struct SPDY_Response *response,
62 struct SPDY_Request *request,
63 enum SPDY_RESPONSE_RESULT status,
64 bool streamopened)
65{
66 (void)streamopened;
67 (void)status;
68
69 printf("answer for %s was sent\n", (char *)cls);
70
71 SPDY_destroy_request(request);
72 SPDY_destroy_response(response);
73 free(cls);
74}
75
76
77static void
78standard_request_handler(void *cls,
79 struct SPDY_Request * request,
80 uint8_t priority,
81 const char *method,
82 const char *path,
83 const char *version,
84 const char *host,
85 const char *scheme,
86 struct SPDY_NameValue * headers,
87 bool more)
88{
89 (void)cls;
90 (void)request;
91 (void)priority;
92 (void)host;
93 (void)scheme;
94 (void)headers;
95 (void)more;
96
97 char *html;
98 struct SPDY_Response *response=NULL;
99 struct SPDY_NameValue *resp_headers;
100
101 printf("received request for '%s %s %s'\n", method, path, version);
102 if(strcmp(path,"/spdy-draft.txt")==0)
103 {
104 FILE *fd = fopen(DATA_DIR "spdy-draft.txt","r");
105
106 if(NULL == (resp_headers = SPDY_name_value_create()))
107 {
108 fprintf(stdout,"SPDY_name_value_create failed\n");
109 abort();
110 }
111 if(SPDY_YES != SPDY_name_value_add(resp_headers,SPDY_HTTP_HEADER_CONTENT_TYPE,"text/plain"))
112 {
113 fprintf(stdout,"SPDY_name_value_add failed\n");
114 abort();
115 }
116
117 response = SPDY_build_response_with_callback(200,NULL,
118 SPDY_HTTP_VERSION_1_1,resp_headers,&response_callback,fd,SPDY_MAX_SUPPORTED_FRAME_SIZE);
119 SPDY_name_value_destroy(resp_headers);
120 }
121 else
122 {
123 if(strcmp(path,"/close")==0)
124 {
125 asprintf(&html,"<html>"
126 "<body><b>Closing now!</body></html>");
127 run = 0;
128 }
129 else
130 {
131 asprintf(&html,"<html>"
132 "<body><a href=\"/spdy-draft.txt\">/spdy-draft.txt</a><br></body></html>");
133 }
134
135 response = SPDY_build_response(200,NULL,SPDY_HTTP_VERSION_1_1,NULL,html,strlen(html));
136 free(html);
137 }
138
139 if(NULL==response){
140 fprintf(stdout,"no response obj\n");
141 abort();
142 }
143
144 void *clspath = strdup(path);
145
146 if(SPDY_queue_response(request,response,true,false,&response_done_callback,clspath)!=SPDY_YES)
147 {
148 fprintf(stdout,"queue\n");
149 abort();
150 }
151}
152
153
154int
155main (int argc, char *const *argv)
156{
157 unsigned long long timeoutlong=0;
158 struct timeval timeout;
159 int ret;
160 fd_set read_fd_set;
161 fd_set write_fd_set;
162 fd_set except_fd_set;
163 int maxfd = -1;
164 struct SPDY_Daemon *daemon;
165
166 if(argc != 2)
167 {
168 return 1;
169 }
170
171 SPDY_init();
172
173 daemon = SPDY_start_daemon(atoi(argv[1]),
174 DATA_DIR "cert-and-key.pem",
175 DATA_DIR "cert-and-key.pem",
176 NULL,
177 NULL,
178 &standard_request_handler,
179 NULL,
180 NULL,
181 SPDY_DAEMON_OPTION_SESSION_TIMEOUT,
182 1800,
183 SPDY_DAEMON_OPTION_END);
184
185 if(NULL==daemon){
186 printf("no daemon\n");
187 return 1;
188 }
189
190 do
191 {
192 FD_ZERO(&read_fd_set);
193 FD_ZERO(&write_fd_set);
194 FD_ZERO(&except_fd_set);
195
196 ret = SPDY_get_timeout(daemon, &timeoutlong);
197 if(SPDY_NO == ret || timeoutlong > 1000)
198 {
199 timeout.tv_sec = 1;
200 timeout.tv_usec = 0;
201 }
202 else
203 {
204 timeout.tv_sec = timeoutlong / 1000;
205 timeout.tv_usec = (timeoutlong % 1000) * 1000;
206 }
207
208 maxfd = SPDY_get_fdset (daemon,
209 &read_fd_set,
210 &write_fd_set,
211 &except_fd_set);
212
213 ret = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout);
214
215 switch(ret) {
216 case -1:
217 printf("select error: %i\n", errno);
218 break;
219 case 0:
220
221 break;
222 default:
223 SPDY_run(daemon);
224
225 break;
226 }
227 }
228 while(run);
229
230 SPDY_stop_daemon(daemon);
231
232 SPDY_deinit();
233
234 return 0;
235}
236
diff --git a/src/include/Makefile.am b/src/include/Makefile.am
index 437008fd..42425ad8 100644
--- a/src/include/Makefile.am
+++ b/src/include/Makefile.am
@@ -1,10 +1,6 @@
1# This Makefile.am is in the public domain 1# This Makefile.am is in the public domain
2SUBDIRS = . 2SUBDIRS = .
3 3
4if ENABLE_SPDY 4include_HEADERS = microhttpd.h
5microspdy = microspdy.h
6endif
7
8include_HEADERS = microhttpd.h $(microspdy)
9 5
10EXTRA_DIST = platform.h platform_interface.h w32functions.h autoinit_funcs.h 6EXTRA_DIST = platform.h platform_interface.h w32functions.h autoinit_funcs.h
diff --git a/src/include/microspdy.h b/src/include/microspdy.h
deleted file mode 100644
index 7da5fbe6..00000000
--- a/src/include/microspdy.h
+++ /dev/null
@@ -1,1380 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2012, 2013 Christian Grothoff
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 microspdy.h
21 * @brief public interface to libmicrospdy
22 * @author Andrey Uzunov
23 * @author Christian Grothoff
24 *
25 * All symbols defined in this header start with SPDY_. libmisrospdy is a small
26 * SPDY daemon library. The application can start multiple daemons
27 * and they are independent.<p>
28 *
29 * The header file defines various constants used by the SPDY and the HTTP protocol.
30 * This does not mean that the lib actually interprets all of these
31 * values. Not everything is implemented. The provided constants are exported as a convenience
32 * for users of the library. The lib does not verify that provided
33 * HTTP headers and if their values conform to the SPDY protocol,
34 * it only checks if the required headers for the SPDY requests and
35 * responses are provided.<p>
36 *
37 * The library uses just a single thread.<p>
38 *
39 * Before including "microspdy.h" you should add the necessary
40 * includes to define the types used in this file (which headers are needed may
41 * depend on your platform; for possible suggestions consult
42 * "platform.h" in the libmicrospdy distribution).<p>
43 *
44 * All of the functions returning SPDY_YES/SPDY_NO return
45 * SPDY_INPUT_ERROR when any of the parameters are invalid, e.g.
46 * required parameter is NULL.<p>
47 *
48 * The library does not check if anything at the application layer --
49 * requests and responses -- is correct. For example, it
50 * is up to the user to check if a client is sending HTTP body but the
51 * method is GET.<p>
52 *
53 * The SPDY flow control is just partially implemented: the receiving
54 * window is updated, and the client is notified, to prevent a client
55 * from stop sending POST body data, for example.
56 */
57#ifndef SPDY_MICROSPDY_H
58#define SPDY_MICROSPDY_H
59
60#include <zlib.h>
61#include <stdbool.h>
62
63/* While we generally would like users to use a configure-driven
64 build process which detects which headers are present and
65 hence works on any platform, we use "standard" includes here
66 to build out-of-the-box for beginning users on common systems.
67
68 Once you have a proper build system and go for more exotic
69 platforms, you should define MHD_PLATFORM_H in some header that
70 you always include *before* "microhttpd.h". Then the following
71 "standard" includes won't be used (which might be a good
72 idea, especially on platforms where they do not exist). */
73#ifndef MHD_PLATFORM_H
74#include <unistd.h>
75#include <stdarg.h>
76#include <stdint.h>
77#ifdef __MINGW32__
78#include <ws2tcpip.h>
79#else
80#include <sys/time.h>
81#include <sys/types.h>
82#include <sys/socket.h>
83#endif
84#endif
85
86#ifndef _MHD_EXTERN
87#define _MHD_EXTERN extern
88#endif
89
90/**
91 * return code for "YES".
92 */
93#define SPDY_YES 1
94
95/**
96 * return code for "NO".
97 */
98#define SPDY_NO 0
99
100/**
101 * return code for error when input parameters are wrong. To be returned
102 * only by functions which return int. The others will return NULL on
103 * input error.
104 */
105#define SPDY_INPUT_ERROR -1
106
107/**
108 * SPDY version supported by the lib.
109 */
110#define SPDY_VERSION 3
111
112/**
113 * The maximum allowed size (without 8 byte headers) of
114 * SPDY frames (value length) is 8192. The lib will accept and
115 * send frames with length at most this value here.
116 */
117#define SPDY_MAX_SUPPORTED_FRAME_SIZE 8192
118
119/**
120 * HTTP response codes.
121 */
122#define SPDY_HTTP_CONTINUE 100
123#define SPDY_HTTP_SWITCHING_PROTOCOLS 101
124#define SPDY_HTTP_PROCESSING 102
125
126#define SPDY_HTTP_OK 200
127#define SPDY_HTTP_CREATED 201
128#define SPDY_HTTP_ACCEPTED 202
129#define SPDY_HTTP_NON_AUTHORITATIVE_INFORMATION 203
130#define SPDY_HTTP_NO_CONTENT 204
131#define SPDY_HTTP_RESET_CONTENT 205
132#define SPDY_HTTP_PARTIAL_CONTENT 206
133#define SPDY_HTTP_MULTI_STATUS 207
134
135#define SPDY_HTTP_MULTIPLE_CHOICES 300
136#define SPDY_HTTP_MOVED_PERMANENTLY 301
137#define SPDY_HTTP_FOUND 302
138#define SPDY_HTTP_SEE_OTHER 303
139#define SPDY_HTTP_NOT_MODIFIED 304
140#define SPDY_HTTP_USE_PROXY 305
141#define SPDY_HTTP_SWITCH_PROXY 306
142#define SPDY_HTTP_TEMPORARY_REDIRECT 307
143
144#define SPDY_HTTP_BAD_REQUEST 400
145#define SPDY_HTTP_UNAUTHORIZED 401
146#define SPDY_HTTP_PAYMENT_REQUIRED 402
147#define SPDY_HTTP_FORBIDDEN 403
148#define SPDY_HTTP_NOT_FOUND 404
149#define SPDY_HTTP_METHOD_NOT_ALLOWED 405
150#define SPDY_HTTP_METHOD_NOT_ACCEPTABLE 406
151#define SPDY_HTTP_PROXY_AUTHENTICATION_REQUIRED 407
152#define SPDY_HTTP_REQUEST_TIMEOUT 408
153#define SPDY_HTTP_CONFLICT 409
154#define SPDY_HTTP_GONE 410
155#define SPDY_HTTP_LENGTH_REQUIRED 411
156#define SPDY_HTTP_PRECONDITION_FAILED 412
157#define SPDY_HTTP_REQUEST_ENTITY_TOO_LARGE 413
158#define SPDY_HTTP_REQUEST_URI_TOO_LONG 414
159#define SPDY_HTTP_UNSUPPORTED_MEDIA_TYPE 415
160#define SPDY_HTTP_REQUESTED_RANGE_NOT_SATISFIABLE 416
161#define SPDY_HTTP_EXPECTATION_FAILED 417
162#define SPDY_HTTP_UNPROCESSABLE_ENTITY 422
163#define SPDY_HTTP_LOCKED 423
164#define SPDY_HTTP_FAILED_DEPENDENCY 424
165#define SPDY_HTTP_UNORDERED_COLLECTION 425
166#define SPDY_HTTP_UPGRADE_REQUIRED 426
167#define SPDY_HTTP_NO_RESPONSE 444
168#define SPDY_HTTP_RETRY_WITH 449
169#define SPDY_HTTP_BLOCKED_BY_WINDOWS_PARENTAL_CONTROLS 450
170#define SPDY_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS 451
171
172#define SPDY_HTTP_INTERNAL_SERVER_ERROR 500
173#define SPDY_HTTP_NOT_IMPLEMENTED 501
174#define SPDY_HTTP_BAD_GATEWAY 502
175#define SPDY_HTTP_SERVICE_UNAVAILABLE 503
176#define SPDY_HTTP_GATEWAY_TIMEOUT 504
177#define SPDY_HTTP_HTTP_VERSION_NOT_SUPPORTED 505
178#define SPDY_HTTP_VARIANT_ALSO_NEGOTIATES 506
179#define SPDY_HTTP_INSUFFICIENT_STORAGE 507
180#define SPDY_HTTP_BANDWIDTH_LIMIT_EXCEEDED 509
181#define SPDY_HTTP_NOT_EXTENDED 510
182
183/**
184 * HTTP headers are used in SPDY, but all of them MUST be lowercase.
185 * Some are not valid in SPDY and MUST not be used
186 */
187#define SPDY_HTTP_HEADER_ACCEPT "accept"
188#define SPDY_HTTP_HEADER_ACCEPT_CHARSET "accept-charset"
189#define SPDY_HTTP_HEADER_ACCEPT_ENCODING "accept-encoding"
190#define SPDY_HTTP_HEADER_ACCEPT_LANGUAGE "accept-language"
191#define SPDY_HTTP_HEADER_ACCEPT_RANGES "accept-ranges"
192#define SPDY_HTTP_HEADER_AGE "age"
193#define SPDY_HTTP_HEADER_ALLOW "allow"
194#define SPDY_HTTP_HEADER_AUTHORIZATION "authorization"
195#define SPDY_HTTP_HEADER_CACHE_CONTROL "cache-control"
196/* Connection header is forbidden in SPDY */
197#define SPDY_HTTP_HEADER_CONNECTION "connection"
198#define SPDY_HTTP_HEADER_CONTENT_ENCODING "content-encoding"
199#define SPDY_HTTP_HEADER_CONTENT_LANGUAGE "content-language"
200#define SPDY_HTTP_HEADER_CONTENT_LENGTH "content-length"
201#define SPDY_HTTP_HEADER_CONTENT_LOCATION "content-location"
202#define SPDY_HTTP_HEADER_CONTENT_MD5 "content-md5"
203#define SPDY_HTTP_HEADER_CONTENT_RANGE "content-range"
204#define SPDY_HTTP_HEADER_CONTENT_TYPE "content-type"
205#define SPDY_HTTP_HEADER_COOKIE "cookie"
206#define SPDY_HTTP_HEADER_DATE "date"
207#define SPDY_HTTP_HEADER_ETAG "etag"
208#define SPDY_HTTP_HEADER_EXPECT "expect"
209#define SPDY_HTTP_HEADER_EXPIRES "expires"
210#define SPDY_HTTP_HEADER_FROM "from"
211/* Host header is forbidden in SPDY */
212#define SPDY_HTTP_HEADER_HOST "host"
213#define SPDY_HTTP_HEADER_IF_MATCH "if-match"
214#define SPDY_HTTP_HEADER_IF_MODIFIED_SINCE "if-modified-since"
215#define SPDY_HTTP_HEADER_IF_NONE_MATCH "if-none-match"
216#define SPDY_HTTP_HEADER_IF_RANGE "if-range"
217#define SPDY_HTTP_HEADER_IF_UNMODIFIED_SINCE "if-unmodified-since"
218/* Keep-Alive header is forbidden in SPDY */
219#define SPDY_HTTP_HEADER_KEEP_ALIVE "keep-alive"
220#define SPDY_HTTP_HEADER_LAST_MODIFIED "last-modified"
221#define SPDY_HTTP_HEADER_LOCATION "location"
222#define SPDY_HTTP_HEADER_MAX_FORWARDS "max-forwards"
223#define SPDY_HTTP_HEADER_PRAGMA "pragma"
224#define SPDY_HTTP_HEADER_PROXY_AUTHENTICATE "proxy-authenticate"
225#define SPDY_HTTP_HEADER_PROXY_AUTHORIZATION "proxy-authorization"
226/* Proxy-Connection header is forbidden in SPDY */
227#define SPDY_HTTP_HEADER_PROXY_CONNECTION "proxy-connection"
228#define SPDY_HTTP_HEADER_RANGE "range"
229#define SPDY_HTTP_HEADER_REFERER "referer"
230#define SPDY_HTTP_HEADER_RETRY_AFTER "retry-after"
231#define SPDY_HTTP_HEADER_SERVER "server"
232#define SPDY_HTTP_HEADER_SET_COOKIE "set-cookie"
233#define SPDY_HTTP_HEADER_SET_COOKIE2 "set-cookie2"
234#define SPDY_HTTP_HEADER_TE "te"
235#define SPDY_HTTP_HEADER_TRAILER "trailer"
236/* Transfer-Encoding header is forbidden in SPDY */
237#define SPDY_HTTP_HEADER_TRANSFER_ENCODING "transfer-encoding"
238#define SPDY_HTTP_HEADER_UPGRADE "upgrade"
239#define SPDY_HTTP_HEADER_USER_AGENT "user-agent"
240#define SPDY_HTTP_HEADER_VARY "vary"
241#define SPDY_HTTP_HEADER_VIA "via"
242#define SPDY_HTTP_HEADER_WARNING "warning"
243#define SPDY_HTTP_HEADER_WWW_AUTHENTICATE "www-authenticate"
244
245/**
246 * HTTP versions (a value must be provided in SPDY requests/responses).
247 */
248#define SPDY_HTTP_VERSION_1_0 "HTTP/1.0"
249#define SPDY_HTTP_VERSION_1_1 "HTTP/1.1"
250
251/**
252 * HTTP methods
253 */
254#define SPDY_HTTP_METHOD_CONNECT "CONNECT"
255#define SPDY_HTTP_METHOD_DELETE "DELETE"
256#define SPDY_HTTP_METHOD_GET "GET"
257#define SPDY_HTTP_METHOD_HEAD "HEAD"
258#define SPDY_HTTP_METHOD_OPTIONS "OPTIONS"
259#define SPDY_HTTP_METHOD_POST "POST"
260#define SPDY_HTTP_METHOD_PUT "PUT"
261#define SPDY_HTTP_METHOD_TRACE "TRACE"
262
263/**
264 * HTTP POST encodings, see also
265 * http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4
266 */
267#define SPDY_HTTP_POST_ENCODING_FORM_URLENCODED "application/x-www-form-urlencoded"
268#define SPDY_HTTP_POST_ENCODING_MULTIPART_FORMDATA "multipart/form-data"
269
270
271/**
272 * Handle for the daemon (listening on a socket).
273 */
274struct SPDY_Daemon;
275
276
277/**
278 * Handle for a SPDY session/connection.
279 */
280struct SPDY_Session;
281
282
283/**
284 * Handle for a SPDY request sent by a client. The structure has pointer
285 * to the session's handler
286 */
287struct SPDY_Request;
288
289
290/**
291 * Handle for a response containing HTTP headers and data to be sent.
292 * The structure has pointer to the session's handler
293 * for this response.
294 */
295struct SPDY_Response;
296
297
298/**
299 * Collection of tuples of an HTTP header and values used in requests
300 * and responses.
301 */
302struct SPDY_NameValue;
303
304
305/**
306 * Collection of tuples of a SPDY setting ID, value
307 * and flags used to control the sessions.
308 */
309struct SPDY_Settings;
310
311
312/**
313 * SPDY IO sybsystem flags used by SPDY_init() and SPDY_deinit().<p>
314 *
315 * The values are used internally as flags, that is why they must be
316 * powers of 2.
317 */
318enum SPDY_IO_SUBSYSTEM
319{
320
321 /**
322 * No subsystem. For internal use.
323 */
324 SPDY_IO_SUBSYSTEM_NONE = 0,
325
326 /**
327 * Default TLS implementation provided by openSSL/libssl.
328 */
329 SPDY_IO_SUBSYSTEM_OPENSSL = 1,
330
331 /**
332 * No TLS is used.
333 */
334 SPDY_IO_SUBSYSTEM_RAW = 2
335};
336
337
338/**
339 * SPDY daemon options. Passed in the varargs portion of
340 * SPDY_start_daemon to customize the daemon. Each option must
341 * be followed by a value of a specific type.<p>
342 *
343 * The values are used internally as flags, that is why they must be
344 * powers of 2.
345 */
346enum SPDY_DAEMON_OPTION
347{
348
349 /**
350 * No more options / last option. This is used
351 * to terminate the VARARGs list.
352 */
353 SPDY_DAEMON_OPTION_END = 0,
354
355 /**
356 * Set a custom timeout for all connections. Must be followed by
357 * a number of seconds, given as an 'unsigned int'. Use
358 * zero for no timeout.
359 */
360 SPDY_DAEMON_OPTION_SESSION_TIMEOUT = 1,
361
362 /**
363 * Bind daemon to the supplied sockaddr. This option must be
364 * followed by a 'struct sockaddr *'. The 'struct sockaddr*'
365 * should point to a 'struct sockaddr_in6' or to a
366 * 'struct sockaddr_in'.
367 */
368 SPDY_DAEMON_OPTION_SOCK_ADDR = 2,
369
370 /**
371 * Flags for the daemon. Must be followed by a SPDY_DAEMON_FLAG value
372 * which is the result of bitwise OR of desired flags.
373 */
374 SPDY_DAEMON_OPTION_FLAGS = 4,
375
376 /**
377 * IO subsystem type used by daemon and all its sessions. If not set,
378 * TLS provided by openssl is used. Must be followed by a
379 * SPDY_IO_SUBSYSTEM value.
380 */
381 SPDY_DAEMON_OPTION_IO_SUBSYSTEM = 8,
382
383 /**
384 * Maximum number of frames to be written to the socket at once. The
385 * library tries to send max_num_frames in a single call to SPDY_run
386 * for a single session. This means no requests can be received nor
387 * other sessions can send data as long the current one has enough
388 * frames to send and there is no error on writing. Thus, a big value
389 * will affect the performance. Small value gives fairnes for sessions.
390 * Must be followed by a positive integer (uin32_t). If not set, the
391 * default value 10 will be used.
392 */
393 SPDY_DAEMON_OPTION_MAX_NUM_FRAMES = 16
394};
395
396
397/**
398 * Flags for starting SPDY daemon. They are used to set some settings
399 * for the daemon, which do not require values.
400 */
401enum SPDY_DAEMON_FLAG
402{
403 /**
404 * No flags selected.
405 */
406 SPDY_DAEMON_FLAG_NO = 0,
407
408 /**
409 * The server will bind only on IPv6 addresses. If the flag is set and
410 * the daemon is provided with IPv4 address or IPv6 is not supported,
411 * starting daemon will fail.
412 */
413 SPDY_DAEMON_FLAG_ONLY_IPV6 = 1,
414
415 /**
416 * All sessions' sockets will be set with TCP_NODELAY if the flag is
417 * used. Option considered only by SPDY_IO_SUBSYSTEM_RAW.
418 */
419 SPDY_DAEMON_FLAG_NO_DELAY = 2
420};
421
422
423/**
424 * SPDY settings IDs sent by both client and server in SPDY SETTINGS frame.
425 * They affect the whole SPDY session. Defined in SPDY Protocol - Draft 3.
426 */
427enum SPDY_SETTINGS
428{
429
430 /**
431 * Allows the sender to send its expected upload bandwidth on this
432 * channel. This number is an estimate. The value should be the
433 * integral number of kilobytes per second that the sender predicts
434 * as an expected maximum upload channel capacity.
435 */
436 SPDY_SETTINGS_UPLOAD_BANDWIDTH = 1,
437
438 /**
439 * Allows the sender to send its expected download bandwidth on this
440 * channel. This number is an estimate. The value should be the
441 * integral number of kilobytes per second that the sender predicts as
442 * an expected maximum download channel capacity.
443 */
444 SPDY_SETTINGS_DOWNLOAD_BANDWIDTH = 2,
445
446 /**
447 * Allows the sender to send its expected round-trip-time on this
448 * channel. The round trip time is defined as the minimum amount of
449 * time to send a control frame from this client to the remote and
450 * receive a response. The value is represented in milliseconds.
451 */
452 SPDY_SETTINGS_ROUND_TRIP_TIME = 3,
453
454 /**
455 * Allows the sender to inform the remote endpoint the maximum number
456 * of concurrent streams which it will allow. By default there is no
457 * limit. For implementors it is recommended that this value be no
458 * smaller than 100.
459 */
460 SPDY_SETTINGS_MAX_CONCURRENT_STREAMS = 4,
461
462 /**
463 * Allows the sender to inform the remote endpoint of the current TCP
464 * CWND value.
465 */
466 SPDY_SETTINGS_CURRENT_CWND = 5,
467
468 /**
469 * Allows the sender to inform the remote endpoint the retransmission
470 * rate (bytes retransmitted / total bytes transmitted).
471 */
472 SPDY_SETTINGS_DOWNLOAD_RETRANS_RATE = 6,
473
474 /**
475 * Allows the sender to inform the remote endpoint the initial window
476 * size (in bytes) for new streams.
477 */
478 SPDY_SETTINGS_INITIAL_WINDOW_SIZE = 7,
479
480 /**
481 * Allows the server to inform the client if the new size of the
482 * client certificate vector.
483 */
484 SPDY_SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE = 8
485};
486
487
488/**
489 * Flags for each individual SPDY setting in the SPDY SETTINGS frame.
490 * They affect only one setting to which they are set.
491 * Defined in SPDY Protocol - Draft 3.
492 */
493enum SPDY_FLAG_SETTINGS
494{
495
496 /**
497 * When set, the sender of this SETTINGS frame is requesting that the
498 * recipient persist the ID/Value and return it in future SETTINGS
499 * frames sent from the sender to this recipient. Because persistence
500 * is only implemented on the client, this flag is only sent by the
501 * server.
502 */
503 SPDY_FLAG_SETTINGS_PERSIST_VALUE = 1,
504
505 /**
506 * When set, the sender is notifying the recipient that this ID/Value
507 * pair was previously sent to the sender by the recipient with the
508 * #SPDY_FLAG_SETTINGS_PERSIST_VALUE, and the sender is returning it.
509 * Because persistence is only implemented on the client, this flag is
510 * only sent by the client.
511 */
512 SPDY_FLAG_SETTINGS_PERSISTED = 2
513};
514
515
516/**
517 * Flag associated with a whole SPDY SETTINGS frame. Affect all the
518 * settings in the frame. Defined in SPDY Protocol - Draft 3.
519 */
520enum SPDY_FLAG_SETTINGS_FRAME
521{
522
523 /**
524 * When set, the client should clear any previously persisted SETTINGS
525 * ID/Value pairs. If this frame contains ID/Value pairs with the
526 * #SPDY_FLAG_SETTINGS_PERSIST_VALUE set, then the client will first
527 * clear its existing, persisted settings, and then persist the values
528 * with the flag set which are contained within this frame. Because
529 * persistence is only implemented on the client, this flag can only
530 * be used when the sender is the server.
531 */
532 SPDY_FLAG_SETTINGS_CLEAR_SETTINGS = 1
533};
534
535
536/**
537 * SPDY settings function options. Passed in the varargs portion of
538 * SPDY_SettingsReceivedCallback and SPDY_send_settings to customize
539 * more the settings handling. Each option must
540 * be followed by a value of a specific type.<p>
541 *
542 * The values are used internally as flags, that is why they must be
543 * powers of 2.
544 */
545enum SPDY_SETTINGS_OPTION
546{
547
548 /**
549 * No more options / last option. This is used
550 * to terminate the VARARGs list.
551 */
552 SPDY_SETTINGS_OPTION_END = 0
553};
554
555
556/**
557 * Used as a parameter for SPDY_ResponseResultCallback and shows if the
558 * response was actually written to the TLS socket or discarded by the
559 * lib for any reason (and respectively the reason).
560 */
561enum SPDY_RESPONSE_RESULT
562{
563
564 /**
565 * The lib has written the full response to the TLS socket.
566 */
567 SPDY_RESPONSE_RESULT_SUCCESS = 0,
568
569 /**
570 * The session is being closed, so the data is being discarded
571 */
572 SPDY_RESPONSE_RESULT_SESSION_CLOSED = 1,
573
574 /**
575 * The stream for this response has been closed. May happen when the
576 * sender had sent first SYN_STREAM and after that RST_STREAM.
577 */
578 SPDY_RESPONSE_RESULT_STREAM_CLOSED = 2
579};
580
581
582/**
583 * Callback for serious error condition. The default action is to print
584 * an error message and abort().
585 *
586 * @param cls user specified value
587 * @param file where the error occured
588 * @param line where the error occured
589 * @param reason error details message, may be NULL
590 */
591typedef void
592(*SPDY_PanicCallback) (void * cls,
593 const char *file,
594 unsigned int line,
595 const char *reason);
596
597
598/**
599 * Callback for new SPDY session established by a client. Called
600 * immediately after the TCP connection was established.
601 *
602 * @param cls client-defined closure
603 * @param session handler for the new SPDY session
604 */
605typedef void
606(*SPDY_NewSessionCallback) (void * cls,
607 struct SPDY_Session * session);
608
609
610/**
611 * Callback for closed session. Called after the TCP connection was
612 * closed. In this callback function the user has the last
613 * chance to access the SPDY_Session structure. After that the latter
614 * will be cleaned!
615 *
616 * @param cls client-defined closure
617 * @param session handler for the closed SPDY session
618 * @param by_client #SPDY_YES if the session close was initiated by the
619 * client;
620 * #SPDY_NO if closed by the server
621 */
622typedef void
623(*SPDY_SessionClosedCallback) (void *cls,
624 struct SPDY_Session *session,
625 int by_client);
626
627
628/**
629 * Iterator over name-value pairs.
630 *
631 * @param cls client-defined closure
632 * @param name of the pair
633 * @param value of the pair
634 * @return #SPDY_YES to continue iterating,
635 * #SPDY_NO to abort the iteration
636 */
637typedef int
638(*SPDY_NameValueIterator) (void *cls,
639 const char *name,
640 const char * const * value,
641 int num_values);
642
643
644/**
645 * Callback for received SPDY request. The functions is called whenever
646 * a reqest comes, but will also be called if more headers/trailers are
647 * received.
648 *
649 * @param cls client-defined closure
650 * @param request handler. The request object is required for
651 * sending responses.
652 * @param priority of the SPDY stream which the request was
653 * sent over
654 * @param method HTTP method
655 * @param path HTTP path
656 * @param version HTTP version just like in HTTP request/response:
657 * "HTTP/1.0" or "HTTP/1.1" currently
658 * @param host called host as in HTTP
659 * @param scheme used ("http" or "https"). In SPDY 3 it is only "https".
660 * @param headers other HTTP headers from the request
661 * @param more a flag saying if more data related to the request is
662 * expected to be received. HTTP body may arrive (e.g. POST data);
663 * then SPDY_NewDataCallback will be called for the connection.
664 * It is also possible that more headers/trailers arrive;
665 * then the same callback will be invoked. The user should detect
666 * that it is not the first invocation of the function for that
667 * request.
668 */
669typedef void
670(*SPDY_NewRequestCallback) (void *cls,
671 struct SPDY_Request *request,
672 uint8_t priority,
673 const char *method,
674 const char *path,
675 const char *version,
676 const char *host,
677 const char *scheme,
678 struct SPDY_NameValue *headers,
679 bool more);
680
681
682/**
683 * Callback for received new data chunk (HTTP body) from a given
684 * request (e.g. POST data).
685 *
686 * @param cls client-defined closure
687 * @param request handler
688 * @param buf data chunk from the POST data
689 * @param size the size of the data chunk 'buf' in bytes. Note that it
690 * may be 0.
691 * @param more false if this is the last chunk from the data. Note:
692 * true does not mean that more data will come, exceptional
693 * situation is possible
694 * @return #SPDY_YES to continue calling the function,
695 * #SPDY_NO to stop calling the function for this request
696 */
697typedef int
698(*SPDY_NewDataCallback) (void *cls,
699 struct SPDY_Request *request,
700 const void *buf,
701 size_t size,
702 bool more);
703// How about passing POST encoding information
704// here as well?
705//TODO
706
707
708/**
709 * Callback to be used with SPDY_build_response_with_callback. The
710 * callback will be called when the lib wants to write to the TLS socket.
711 * The application should provide the data to be sent.
712 *
713 * @param cls client-defined closure
714 * @param max maximum number of bytes that are allowed to be written
715 * to the buffer.
716 * @param more true if more data will be sent (i.e. the function must
717 * be calleed again),
718 * false if this is the last chunk, the lib will close
719 * the stream
720 * @return number of bytes written to buffer. On error the call MUST
721 * return value less than 0 to indicate the library.
722 */
723typedef ssize_t
724(*SPDY_ResponseCallback) (void *cls,
725 void *buffer,
726 size_t max,
727 bool *more);
728
729
730/**
731 * Callback to be called when the last bytes from the response was sent
732 * to the client or when the response was discarded from the lib. This
733 * callback is a very good place to discard the request and the response
734 * objects, if they will not be reused (e.g., sending the same response
735 * again). If the stream is closed it is safe to discard the request
736 * object.
737 *
738 * @param cls client-defined closure
739 * @param response handler to the response that was just sent
740 * @param request handler to the request for which the response was sent
741 * @param status shows if actually the response was sent or it was
742 * discarded by the lib for any reason (e.g., closing session,
743 * closing stream, stopping daemon, etc.). It is possible that
744 * status indicates an error but parts of the response headers
745 * and/or body (in one
746 * or several frames) were already sent to the client.
747 * @param streamopened indicates if the the stream for this request/
748 * response pair is still opened. If yes, the server may want
749 * to use SPDY push to send something additional to the client
750 * and/or close the stream.
751 */
752typedef void
753(*SPDY_ResponseResultCallback) (void * cls,
754 struct SPDY_Response *response,
755 struct SPDY_Request *request,
756 enum SPDY_RESPONSE_RESULT status,
757 bool streamopened);
758
759
760/**
761 * Callback to notify when SPDY ping response is received.
762 *
763 * @param session handler for which the ping request was sent
764 * @param rtt the timespan between sending ping request and receiving it
765 * from the library
766 */
767typedef void
768(*SPDY_PingCallback) (void * cls,
769 struct SPDY_Session *session,
770 struct timeval *rtt);
771
772
773/**
774 * Iterator over settings ID/Value/Flags tuples.
775 *
776 * @param cls client-defined closure
777 * @param id SPDY settings ID
778 * @param value value for this setting
779 * @param flags flags for this tuple; use
780 * `enum SPDY_FLAG_SETTINGS`
781 * @return #SPDY_YES to continue iterating,
782 * #SPDY_NO to abort the iteration
783 */
784typedef int
785(*SPDY_SettingsIterator) (void *cls,
786 enum SPDY_SETTINGS id,
787 int32_t value,
788 uint8_t flags);
789
790
791/**
792 * Callback to notify when SPDY SETTINGS are received from the client.
793 *
794 * @param session handler for which settings are received
795 * @param settings ID/value/flags tuples of the settings
796 * @param flags for the whole settings frame; use
797 * enum SPDY_FLAG_SETTINGS_FRAME
798 * @param ... list of options (type-value pairs,
799 * terminated with #SPDY_SETTINGS_OPTION_END).
800 */
801typedef void
802(*SPDY_SettingsReceivedCallback) (struct SPDY_Session *session,
803 struct SPDY_Settings *settings,
804 uint8_t flags,
805 ...);
806
807
808/* Global functions for the library */
809
810
811/**
812 * Init function for the whole library. It MUST be called before any
813 * other function of the library to initialize things like TLS context
814 * and possibly other stuff needed by the lib. Currently the call
815 * always returns #SPDY_YES.
816 *
817 * @param io_subsystem the IO subsystem that will
818 * be initialized. Several can be used with bitwise OR. If no
819 * parameter is set, the default openssl subsystem will be used.
820 * @return #SPDY_YES if the library was correctly initialized and its
821 * functions can be used now;
822 * #SPDY_NO on error
823 */
824_MHD_EXTERN int
825(SPDY_init) (enum SPDY_IO_SUBSYSTEM io_subsystem, ...);
826#define SPDY_init() SPDY_init (SPDY_IO_SUBSYSTEM_OPENSSL)
827
828
829/**
830 * Deinit function for the whole lib. It can be called after finishing
831 * using the library. It frees and cleans up resources allocated in
832 * SPDY_init. Currently the function does not do anything.
833 */
834_MHD_EXTERN void
835SPDY_deinit (void);
836
837
838/**
839 * Sets the global error handler to a different implementation. "cb"
840 * will only be called in the case of typically fatal, serious
841 * internal consistency issues. These issues should only arise in the
842 * case of serious memory corruption or similar problems with the
843 * architecture as well as failed assertions. While "cb" is allowed to
844 * return and the lib will then try to continue, this is never safe.
845 *
846 * The default implementation that is used if no panic function is set
847 * simply prints an error message and calls "abort". Alternative
848 * implementations might call "exit" or other similar functions.
849 *
850 * @param cb new error handler
851 * @param cls passed to error handler
852 */
853_MHD_EXTERN void
854SPDY_set_panic_func (SPDY_PanicCallback cb,
855 void *cls);
856
857
858/* Daemon functions */
859
860
861/**
862 * Start a SPDY webserver on the given port.
863 *
864 * @param port to bind to. The value is ignored if address structure
865 * is passed as daemon option
866 * @param certfile path to the certificate that will be used by server
867 * @param keyfile path to the keyfile for the certificate
868 * @param nscb callback called when a new SPDY session is
869 * established by a client
870 * @param sccb callback called when a session is closed
871 * @param nrcb callback called when a client sends request
872 * @param npdcb callback called when HTTP body (POST data) is received
873 * after request
874 * @param cls common extra argument to all of the callbacks
875 * @param ... list of options (type-value pairs,
876 * terminated with #SPDY_DAEMON_OPTION_END).
877 * @return NULL on error, handle to daemon on success
878 */
879_MHD_EXTERN struct SPDY_Daemon *
880SPDY_start_daemon (uint16_t port,
881 const char *certfile,
882 const char *keyfile,
883 SPDY_NewSessionCallback nscb,
884 SPDY_SessionClosedCallback sccb,
885 SPDY_NewRequestCallback nrcb,
886 SPDY_NewDataCallback npdcb,
887 void *cls,
888 ...);
889
890
891/**
892 * Shutdown the daemon. First all sessions are closed. It is NOT safe
893 * to call this function in user callbacks.
894 *
895 * @param daemon to stop
896 */
897_MHD_EXTERN void
898SPDY_stop_daemon (struct SPDY_Daemon *daemon);
899
900
901/**
902 * Obtain the select sets for this daemon. Only those are retrieved,
903 * which some processing should be done for, i.e. not all sockets are
904 * added to write_fd_set.<p>
905 *
906 * It is possible that there is
907 * nothing to be read from a socket but there is data either in the
908 * TLS subsystem's read buffers or in libmicrospdy's read buffers, which
909 * waits for being processed. In such case the file descriptor will be
910 * added to write_fd_set. Since it is very likely for the socket to be
911 * ready for writing, the select used in the application's event loop
912 * will return with success, SPDY_run will be called, the data will be
913 * processed and maybe something will be written to the socket. Without
914 * this behaviour, considering a proper event loop, data may stay in the
915 * buffers, but run is never called.
916 *
917 * @param daemon to get sets from
918 * @param read_fd_set read set
919 * @param write_fd_set write set
920 * @param except_fd_set except set
921 * @return largest FD added to any of the sets
922 */
923_MHD_EXTERN int
924SPDY_get_fdset (struct SPDY_Daemon *daemon,
925 fd_set *read_fd_set,
926 fd_set *write_fd_set,
927 fd_set *except_fd_set);
928
929
930/**
931 * Obtain timeout value for select for this daemon. The returned value
932 * is how long select
933 * should at most block, not the timeout value set for connections.
934 *
935 * @param daemon to query for timeout
936 * @param timeout will be set to the timeout value (in milliseconds)
937 * @return #SPDY_YES on success
938 * #SPDY_NO if no connections exist that
939 * would necessiate the use of a timeout right now
940 */
941_MHD_EXTERN int
942SPDY_get_timeout (struct SPDY_Daemon *daemon,
943 unsigned long long *timeout);
944
945
946/**
947 * Run webserver operations. This method must be called in
948 * the client event loop.
949 *
950 * @param daemon to run
951 */
952_MHD_EXTERN void
953SPDY_run (struct SPDY_Daemon *daemon);
954
955
956/* SPDY Session handling functions */
957
958
959/**
960 * Closes a SPDY session. SPDY clients and servers are expected to keep
961 * sessions opened as long as possible. However, the server may want to
962 * close some connections, e.g. if there are too many, to free some
963 * resources. The function can also be used to close a specific session
964 * if the client is not desired.
965 *
966 * @param session handler to be closed
967 */
968_MHD_EXTERN void
969SPDY_close_session (struct SPDY_Session * session);
970
971
972/**
973 * Associate a void pointer with a session. The data accessible by the
974 * pointer can later be used wherever the session handler is available.
975 *
976 * @param session handler
977 * @param cls any data pointed by a pointer to be accessible later
978 */
979_MHD_EXTERN void
980SPDY_set_cls_to_session (struct SPDY_Session *session,
981 void *cls);
982
983
984/**
985 * Retrieves the pointer associated with SPDY_set_cls_to_session().
986 *
987 * @param session handler to get its cls
988 * @return same pointer added by SPDY_set_cls_to_session() or
989 * NULL when nothing was associated
990 */
991_MHD_EXTERN void *
992SPDY_get_cls_from_session (struct SPDY_Session *session);
993
994
995/**
996 * Retrieves the remote address of a given session.
997 *
998 * @param session handler to get its remote address
999 * @param addr out parameter; pointing to remote address
1000 * @return length of the address structure
1001 */
1002_MHD_EXTERN socklen_t
1003SPDY_get_remote_addr (struct SPDY_Session *session,
1004 struct sockaddr **addr);
1005
1006
1007/* SPDY name/value data structure handling functions */
1008
1009
1010/**
1011 * Create a new NameValue structure. It is needed for putting inside the
1012 * HTTP headers and their values for a response. The user should later
1013 * destroy alone the structure.
1014 *
1015 * @return handler to the new empty structure or NULL on error
1016 */
1017_MHD_EXTERN struct SPDY_NameValue *
1018SPDY_name_value_create (void);
1019
1020
1021/**
1022 * Add name/value pair to a NameValue structure. SPDY_NO will be returned
1023 * if the name/value pair is already in the structure. It is legal to
1024 * add different values for the same name.
1025 *
1026 * @param container structure to which the new pair is added
1027 * @param name for the value. Null-terminated string.
1028 * @param value the value itself. Null-terminated string.
1029 * @return #SPDY_NO on error or #SPDY_YES on success
1030 */
1031_MHD_EXTERN int
1032SPDY_name_value_add (struct SPDY_NameValue *container,
1033 const char *name,
1034 const char *value);
1035
1036
1037/**
1038 * Lookup value for a name in a name/value structure.
1039 *
1040 * @param container structure in which to lookup
1041 * @param name the name to look for
1042 * @param num_values length of the returned array with values
1043 * @return NULL if no such item was found, or an array containing the
1044 * values
1045 */
1046_MHD_EXTERN const char * const *
1047SPDY_name_value_lookup (struct SPDY_NameValue *container,
1048 const char *name,
1049 int *num_values);
1050
1051
1052/**
1053 * Iterate over name/value structure.
1054 *
1055 * @param container structure which to iterate over
1056 * @param iterator callback to call on each name/value pair;
1057 * maybe NULL (then just count headers)
1058 * @param iterator_cls extra argument to @a iterator
1059 * @return number of entries iterated over
1060 */
1061_MHD_EXTERN int
1062SPDY_name_value_iterate (struct SPDY_NameValue *container,
1063 SPDY_NameValueIterator iterator,
1064 void *iterator_cls);
1065
1066
1067/**
1068 * Destroy a NameValue structure. Use this function to destroy only
1069 * objects which, after passed to, will not be destroied by other
1070 * functions.
1071 *
1072 */
1073_MHD_EXTERN void
1074SPDY_name_value_destroy (struct SPDY_NameValue *container);
1075
1076
1077/* SPDY request handling functions */
1078
1079
1080/**
1081 * Gets the session responsible for the given
1082 * request.
1083 *
1084 * @param request for which the session is wanted
1085 * @return session handler for the request
1086 */
1087_MHD_EXTERN struct SPDY_Session *
1088SPDY_get_session_for_request (const struct SPDY_Request *request);
1089
1090
1091/**
1092 * Associate a void pointer with a request. The data accessible by the
1093 * pointer can later be used wherever the request handler is available.
1094 *
1095 * @param request with which to associate a pointer
1096 * @param cls any data pointed by a pointer to be accessible later
1097 */
1098_MHD_EXTERN void
1099SPDY_set_cls_to_request (struct SPDY_Request *request,
1100 void *cls);
1101
1102
1103/**
1104 * Retrieves the pointer associated with the request by
1105 * SPDY_set_cls_to_request().
1106 *
1107 * @param request to get its cls
1108 * @return same pointer added by SPDY_set_cls_to_request() or
1109 * NULL when nothing was associated
1110 */
1111_MHD_EXTERN void *
1112SPDY_get_cls_from_request (struct SPDY_Request *request);
1113
1114
1115/* SPDY response handling functions */
1116
1117
1118/**
1119 * Create response object containing all needed headers and data. The
1120 * response object is not bound to a request, so it can be used multiple
1121 * times with SPDY_queue_response() and schould be
1122 * destroied by calling the SPDY_destroy_response().<p>
1123 *
1124 * Currently the library does not provide compression of the body data.
1125 * It is up to the user to pass already compressed data and the
1126 * appropriate headers to this function when desired.
1127 *
1128 * @param status HTTP status code for the response (e.g. 404)
1129 * @param statustext HTTP status message for the response, which will
1130 * be appended to the status code (e.g. "OK"). Can be NULL
1131 * @param version HTTP version for the response (e.g. "http/1.1")
1132 * @param headers name/value structure containing additional HTTP headers.
1133 * Can be NULL. Can be used multiple times, it is up to
1134 * the user to destoy the object when not needed anymore.
1135 * @param data the body of the response. The lib will make a copy of it,
1136 * so it is up to the user to take care of the memory
1137 * pointed by data
1138 * @param size length of @a data. It can be 0, then the lib will send only
1139 * headers
1140 * @return NULL on error, handle to response object on success
1141 */
1142_MHD_EXTERN struct SPDY_Response *
1143SPDY_build_response (int status,
1144 const char *statustext,
1145 const char *version,
1146 struct SPDY_NameValue *headers,
1147 const void *data,
1148 size_t size);
1149
1150
1151/**
1152 * Create response object containing all needed headers. The data will
1153 * be provided later when the lib calls the callback function (just
1154 * before writing it to the TLS socket). The
1155 * response object is not bound to a request, so it can be used multiple
1156 * times with SPDY_queue_response() and schould be
1157 * destroied by calling the SPDY_destroy_response().<p>
1158 *
1159 * Currently the library does not provide compression of the body data.
1160 * It is up to the user to pass already compressed data and the
1161 * appropriate headers to this function and the callback when desired.
1162 *
1163 * @param status HTTP status code for the response (e.g. 404)
1164 * @param statustext HTTP status message for the response, which will
1165 * be appended to the status code (e.g. "OK"). Can be NULL
1166 * @param version HTTP version for the response (e.g. "http/1.1")
1167 * @param headers name/value structure containing additional HTTP headers.
1168 * Can be NULL. Can be used multiple times, it is up to
1169 * the user to destoy the object when not needed anymore.
1170 * @param rcb callback to use to obtain response data
1171 * @param rcb_cls extra argument to @a rcb
1172 * @param block_size preferred block size for querying rcb (advisory only,
1173 * the lib will call rcb specifying the block size); clients
1174 * should pick a value that is appropriate for IO and
1175 * memory performance requirements. The function will
1176 * fail if the value is bigger than the maximum
1177 * supported value (SPDY_MAX_SUPPORTED_FRAME_SIZE).
1178 * Can be 0, then the lib will use
1179 * #SPDY_MAX_SUPPORTED_FRAME_SIZE instead.
1180 * @return NULL on error, handle to response object on success
1181 */
1182_MHD_EXTERN struct SPDY_Response *
1183SPDY_build_response_with_callback(int status,
1184 const char *statustext,
1185 const char *version,
1186 struct SPDY_NameValue *headers,
1187 SPDY_ResponseCallback rcb,
1188 void *rcb_cls,
1189 uint32_t block_size);
1190
1191
1192/**
1193 * Queue response object to be sent to the client. A successfully queued
1194 * response may never be sent, e.g. when the stream gets closed. The
1195 * data will be added to the output queue. The call will fail, if the
1196 * output for this session
1197 * is closed (i.e. the session is closed, half or full) or the output
1198 * channel for the stream, on which the request was received, is closed
1199 * (i.e. the stream is closed, half or full).
1200 *
1201 * @param request object identifying the request to which the
1202 * response is returned
1203 * @param response object containg headers and data to be sent
1204 * @param closestream TRUE if the server does NOT intend to PUSH
1205 * something more associated to this request/response later,
1206 * FALSE otherwise
1207 * @param consider_priority if FALSE, the response will be added to the
1208 * end of the queue. If TRUE, the response will be added after
1209 * the last previously added response with priority of the
1210 * request grater or equal to that of the current one. This
1211 * means that the function should be called with TRUE each time
1212 * if one wants to be sure that the output queue behaves like
1213 * a priority queue
1214 * @param rrcb callback called when all the data was sent (last frame
1215 * from response) or when that frame was discarded (e.g. the
1216 * stream has been closed meanwhile)
1217 * @param rrcb_cls extra argument to @a rrcb
1218 * @return #SPDY_NO on error or #SPDY_YES on success
1219 */
1220_MHD_EXTERN int
1221SPDY_queue_response (struct SPDY_Request *request,
1222 struct SPDY_Response *response,
1223 bool closestream,
1224 bool consider_priority,
1225 SPDY_ResponseResultCallback rrcb,
1226 void *rrcb_cls);
1227
1228
1229/**
1230 * Destroy a response structure. It should be called for all objects
1231 * returned by SPDY_build_response*() functions to free the memory
1232 * associated with the prepared response. It is safe to call this
1233 * function not before being sure that the response will not be used by
1234 * the lib anymore, this means after SPDY_ResponseResultCallback
1235 * callbacks were called for all calls to SPDY_queue_response() passing
1236 * this response.
1237 *
1238 * @param response to destroy
1239 */
1240_MHD_EXTERN void
1241SPDY_destroy_response (struct SPDY_Response *response);
1242
1243
1244/* SPDY settings ID/value data structure handling functions */
1245
1246
1247/**
1248 * Create a new SettingsIDValue structure. It is needed for putting
1249 * inside tuples of SPDY option, flags and value for sending to the
1250 * client.
1251 *
1252 * @return hendler to the new empty structure or NULL on error
1253 */
1254_MHD_EXTERN const struct SPDY_Settings *
1255SPDY_settings_create (void);
1256
1257
1258/**
1259 * Add or update a tuple to a SettingsIDValue structure.
1260 *
1261 * @param container structure to which the new tuple is added
1262 * @param id SPDY settings ID that will be sent. If this ID already in
1263 * container, the tupple for it will be updated (value and/or
1264 * flags). If it is not in the container, a new tupple will be
1265 * added.
1266 * @param flags SPDY settings flags applied only to this setting
1267 * @param value of the setting
1268 * @return #SPDY_NO on error
1269 * or #SPDY_YES if a new setting was added
1270 */
1271_MHD_EXTERN int
1272SPDY_settings_add (struct SPDY_Settings *container,
1273 enum SPDY_SETTINGS id,
1274 enum SPDY_FLAG_SETTINGS flags,
1275 int32_t value);
1276
1277
1278/**
1279 * Lookup value and flags for an ID in a settings ID/value structure.
1280 *
1281 * @param container structure in which to lookup
1282 * @param id SPDY settings ID to search for
1283 * @param flags out param for SPDY settings flags for this setting;
1284 * check it against the flags in enum SPDY_FLAG_SETTINGS
1285 * @param value out param for the value of this setting
1286 * @return #SPDY_NO if the setting is not into the structure
1287 * or #SPDY_YES if it is into it
1288 */
1289_MHD_EXTERN int
1290SPDY_settings_lookup (const struct SPDY_Settings *container,
1291 enum SPDY_SETTINGS id,
1292 enum SPDY_FLAG_SETTINGS *flags,
1293 int32_t *value);
1294
1295
1296/**
1297 * Iterate over settings ID/value structure.
1298 *
1299 * @param container structure which to iterate over
1300 * @param iterator callback to call on each ID/value pair;
1301 * maybe NULL (then just count number of settings)
1302 * @param iterator_cls extra argument to iterator
1303 * @return number of entries iterated over
1304 */
1305_MHD_EXTERN int
1306SPDY_settings_iterate (const struct SPDY_Settings *container,
1307 SPDY_SettingsIterator iterator,
1308 void *iterator_cls);
1309
1310
1311/**
1312 * Destroy a settings ID/value structure. Use this function to destroy
1313 * only objects which, after passed to, will not be destroied by other
1314 * functions.
1315 *
1316 * @param container structure which to detroy
1317 */
1318_MHD_EXTERN void
1319SPDY_settings_destroy (struct SPDY_Settings * container);
1320
1321
1322/* SPDY SETTINGS handling functions */
1323
1324
1325/**
1326 * Send SPDY SETTINGS to the client. The call will return fail if there
1327 * in invald setting into the settings container (e.g. invalid setting
1328 * ID).
1329 *
1330 * @param session SPDY_Session handler for which settings are being sent
1331 * @param settings ID/value pairs of the settings to be sent.
1332 * Can be used multiple times, it is up to the user to destoy
1333 * the object when not needed anymore.
1334 * @param flags for the whole settings frame. They are valid for all tuples
1335 * @param ... list of options (type-value pairs,
1336 * terminated with #SPDY_SETTINGS_OPTION_END).
1337 * @return SPDY_NO on error or SPDY_YES on
1338 * success
1339 */
1340_MHD_EXTERN int
1341SPDY_send_settings (struct SPDY_Session *session,
1342 struct SPDY_Settings *settings,
1343 enum SPDY_FLAG_SETTINGS_FRAME flags,
1344 ...);
1345
1346
1347/* SPDY misc functions */
1348
1349
1350/**
1351 * Destroy a request structure. It should be called for all objects
1352 * received as a parameter in SPDY_NewRequestCallback to free the memory
1353 * associated with the request. It is safe to call this
1354 * function not before being sure that the request will not be used by
1355 * the lib anymore, this means after the stream, on which this request
1356 * had been sent, was closed and all SPDY_ResponseResultCallback
1357 * callbacks were called for all calls to SPDY_queue_response() passing
1358 * this request object.
1359 *
1360 * @param request to destroy
1361 */
1362_MHD_EXTERN void
1363SPDY_destroy_request (struct SPDY_Request * request);
1364
1365
1366/**
1367 * Send SPDY ping to the client
1368 *
1369 * @param session handler for which the ping request is sent
1370 * @param rttcb callback called when ping response to the request is
1371 * received
1372 * @param rttcb_cls extra argument to @a rttcb
1373 * @return #SPDY_NO on error or #SPDY_YES on success
1374 */
1375_MHD_EXTERN int
1376SPDY_send_ping (struct SPDY_Session *session,
1377 SPDY_PingCallback rttcb,
1378 void *rttcb_cls);
1379
1380#endif
diff --git a/src/microspdy/Makefile.am b/src/microspdy/Makefile.am
deleted file mode 100644
index 7fb2c37f..00000000
--- a/src/microspdy/Makefile.am
+++ /dev/null
@@ -1,40 +0,0 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = \
3 -I$(top_srcdir)/src/include \
4 -I$(top_srcdir)/src/microspdy
5
6AM_CFLAGS = $(HIDDEN_VISIBILITY_CFLAGS)
7
8
9lib_LTLIBRARIES = \
10 libmicrospdy.la
11
12libmicrospdy_la_SOURCES = \
13 io.h io.c \
14 io_openssl.h io_openssl.c \
15 io_raw.h io_raw.c \
16 structures.h structures.c \
17 internal.h internal.c \
18 daemon.h daemon.c \
19 stream.h stream.c \
20 compression.h compression.c \
21 session.h session.c \
22 applicationlayer.c applicationlayer.h \
23 alstructures.c alstructures.h
24libmicrospdy_la_LIBADD = \
25 $(SPDY_LIBDEPS)
26
27libmicrospdy_la_LDFLAGS = \
28 $(SPDY_LIB_LDFLAGS)
29
30libmicrospdy_la_CPPFLAGS = \
31 $(AM_CPPFLAGS) $(SPDY_LIB_CPPFLAGS) \
32 -DBUILDING_MHD_LIB=1
33
34libmicrospdy_la_CFLAGS = -Wextra \
35 $(AM_CFLAGS) $(SPDY_LIB_CFLAGS)
36
37
38if USE_COVERAGE
39 AM_CFLAGS += --coverage
40endif
diff --git a/src/microspdy/alstructures.c b/src/microspdy/alstructures.c
deleted file mode 100644
index b588a1c8..00000000
--- a/src/microspdy/alstructures.c
+++ /dev/null
@@ -1,41 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2012 Andrey Uzunov
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file 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
deleted file mode 100644
index 2eb36e82..00000000
--- a/src/microspdy/alstructures.h
+++ /dev/null
@@ -1,79 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2012 Andrey Uzunov
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file 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
deleted file mode 100644
index bf16b785..00000000
--- a/src/microspdy/applicationlayer.c
+++ /dev/null
@@ -1,748 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2012 Andrey Uzunov
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file 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
34void
35spdy_callback_response_done(void *cls,
36 struct SPDY_Response *response,
37 struct SPDY_Request *request,
38 enum SPDY_RESPONSE_RESULT status,
39 bool streamopened)
40{
41 (void)cls;
42 (void)status;
43 (void)streamopened;
44
45 SPDY_destroy_request(request);
46 SPDY_destroy_response(response);
47}
48
49
50/**
51 * Callback called when new stream is created. It extracts the info from
52 * the stream to create (HTTP) request object and pass it to the client.
53 *
54 * @param cls
55 * @param stream the new SPDY stream
56 * @return SPDY_YES on success, SPDY_NO on memomry error
57 */
58static int
59spdy_handler_new_stream (void *cls,
60 struct SPDYF_Stream * stream)
61{
62 (void)cls;
63 unsigned int i;
64 char *method = NULL;
65 char *path = NULL;
66 char *version = NULL;
67 char *host = NULL;
68 char *scheme = NULL;
69 struct SPDY_Request * request = NULL;
70 struct SPDY_NameValue * headers = NULL;
71 struct SPDY_NameValue * iterator = stream->headers;
72 struct SPDY_Daemon *daemon;
73
74 daemon = stream->session->daemon;
75
76 //if the user doesn't care, ignore it
77 if(NULL == daemon->new_request_cb)
78 return SPDY_YES;
79
80 if(NULL == (headers=SPDY_name_value_create()))
81 goto free_and_fail;
82
83 if(NULL==(request = malloc(sizeof(struct SPDY_Request))))
84 goto free_and_fail;
85
86 memset(request, 0, sizeof(struct SPDY_Request));
87 request->stream = stream;
88
89 /* extract the mandatory fields from stream->headers' structure
90 * to pass them to the client */
91 while(iterator != NULL)
92 {
93 if(strcmp(":method",iterator->name) == 0)
94 {
95 if(1 != iterator->num_values)
96 break;
97 method = iterator->value[0];
98 }
99 else if(strcmp(":path",iterator->name) == 0)
100 {
101 if(1 != iterator->num_values)
102 break;
103 path = iterator->value[0];
104 }
105 else if(strcmp(":version",iterator->name) == 0)
106 {
107 if(1 != iterator->num_values)
108 break;
109 version = iterator->value[0];
110 }
111 else if(strcmp(":host",iterator->name) == 0)
112 {
113 //TODO can it have more values?
114 if(1 != iterator->num_values)
115 break;
116 host = iterator->value[0];
117 }
118 else if(strcmp(":scheme",iterator->name) == 0)
119 {
120 if(1 != iterator->num_values)
121 break;
122 scheme = iterator->value[0];
123 }
124 else
125 for(i=0; i<iterator->num_values; ++i)
126 if (SPDY_YES != SPDY_name_value_add(headers,iterator->name,iterator->value[i]))
127 {
128 SPDY_destroy_request(request);
129 goto free_and_fail;
130 }
131
132 iterator = iterator->next;
133 }
134
135 request->method=method;
136 request->path=path;
137 request->version=version;
138 request->host=host;
139 request->scheme=scheme;
140 request->headers=headers;
141
142 //check request validity, all these fields are mandatory for a request
143 if(NULL == method || strlen(method) == 0
144 || NULL == path || strlen(path) == 0
145 || NULL == version || strlen(version) == 0
146 || NULL == host || strlen(host) == 0
147 || NULL == scheme || strlen(scheme) == 0
148 )
149 {
150 //TODO HTTP 400 Bad Request must be answered
151
152 SPDYF_DEBUG("Bad request");
153
154 SPDY_destroy_request(request);
155
156 return SPDY_YES;
157 }
158
159 //call client's callback function to notify
160 daemon->new_request_cb(daemon->cls,
161 request,
162 stream->priority,
163 method,
164 path,
165 version,
166 host,
167 scheme,
168 headers,
169 !stream->is_in_closed);
170
171 stream->cls = request;
172
173 return SPDY_YES;
174
175 //for GOTO
176 free_and_fail:
177
178 SPDY_name_value_destroy(headers);
179 return SPDY_NO;
180}
181
182
183/**
184 * TODO
185 */
186static int
187spdy_handler_new_data (void * cls,
188 struct SPDYF_Stream *stream,
189 const void * buf,
190 size_t size,
191 bool more)
192{
193 return stream->session->daemon->received_data_cb(cls, stream->cls, buf, size, more);
194}
195
196
197
198/**
199 * Callback to be called when the response queue object was handled and
200 * the data was already sent or discarded.
201 *
202 * @param cls
203 * @param response_queue the object which is being handled
204 * @param status shows if actually the response was sent or it was
205 * discarded by the lib for any reason (e.g., closing session,
206 * closing stream, stopping daemon, etc.). It is possible that
207 * status indicates an error but parts of the response headers
208 * and/or body (in one
209 * or several frames) were already sent to the client.
210 */
211static void
212spdy_handler_response_queue_result(void * cls,
213 struct SPDYF_Response_Queue *response_queue,
214 enum SPDY_RESPONSE_RESULT status)
215{
216 int streamopened;
217 struct SPDY_Request *request = (struct SPDY_Request *)cls;
218
219 SPDYF_ASSERT( ( (NULL == response_queue->data_frame) &&
220 (NULL != response_queue->control_frame) ) ||
221 ( (NULL != response_queue->data_frame) &&
222 (NULL == response_queue->control_frame) ),
223 "response queue must have either control frame or data frame");
224
225 streamopened = !response_queue->stream->is_out_closed;
226
227 response_queue->rrcb(response_queue->rrcb_cls, response_queue->response, request, status, streamopened);
228}
229
230
231int
232(SPDY_init) (enum SPDY_IO_SUBSYSTEM io_subsystem, ...)
233{
234 SPDYF_ASSERT(SPDYF_BUFFER_SIZE >= SPDY_MAX_SUPPORTED_FRAME_SIZE,
235 "Buffer size is less than max supported frame size!");
236 SPDYF_ASSERT(SPDY_MAX_SUPPORTED_FRAME_SIZE >= 32,
237 "Max supported frame size must be bigger than the minimal value!");
238 SPDYF_ASSERT(SPDY_IO_SUBSYSTEM_NONE == spdyf_io_initialized,
239 "SPDY_init must be called only once per program or after SPDY_deinit");
240
241 if(SPDY_IO_SUBSYSTEM_OPENSSL & io_subsystem)
242 {
243 SPDYF_openssl_global_init();
244 spdyf_io_initialized |= SPDY_IO_SUBSYSTEM_OPENSSL;
245 }
246 else if(SPDY_IO_SUBSYSTEM_RAW & io_subsystem)
247 {
248 SPDYF_raw_global_init();
249 spdyf_io_initialized |= SPDY_IO_SUBSYSTEM_RAW;
250 }
251
252 SPDYF_ASSERT(SPDY_IO_SUBSYSTEM_NONE != spdyf_io_initialized,
253 "SPDY_init could not find even one IO subsystem");
254
255 return SPDY_YES;
256}
257
258
259void
260SPDY_deinit ()
261{
262 SPDYF_ASSERT(SPDY_IO_SUBSYSTEM_NONE != spdyf_io_initialized,
263 "SPDY_init has not been called!");
264
265 if(SPDY_IO_SUBSYSTEM_OPENSSL & spdyf_io_initialized)
266 SPDYF_openssl_global_deinit();
267 else if(SPDY_IO_SUBSYSTEM_RAW & spdyf_io_initialized)
268 SPDYF_raw_global_deinit();
269
270 spdyf_io_initialized = SPDY_IO_SUBSYSTEM_NONE;
271}
272
273
274void
275SPDY_run (struct SPDY_Daemon *daemon)
276{
277 if(NULL == daemon)
278 {
279 SPDYF_DEBUG("daemon is NULL");
280 return;
281 }
282
283 SPDYF_run(daemon);
284}
285
286
287int
288SPDY_get_timeout (struct SPDY_Daemon *daemon,
289 unsigned long long *timeout)
290{
291 if(NULL == daemon)
292 {
293 SPDYF_DEBUG("daemon is NULL");
294 return SPDY_INPUT_ERROR;
295 }
296
297 return SPDYF_get_timeout(daemon,timeout);
298}
299
300
301int
302SPDY_get_fdset (struct SPDY_Daemon *daemon,
303 fd_set *read_fd_set,
304 fd_set *write_fd_set,
305 fd_set *except_fd_set)
306{
307 if(NULL == daemon
308 || NULL == read_fd_set
309 || NULL == write_fd_set
310 || NULL == except_fd_set)
311 {
312 SPDYF_DEBUG("a parameter is NULL");
313 return SPDY_INPUT_ERROR;
314 }
315
316 return SPDYF_get_fdset(daemon,
317 read_fd_set,
318 write_fd_set,
319 except_fd_set,
320 false);
321}
322
323
324struct SPDY_Daemon *
325SPDY_start_daemon (uint16_t port,
326 const char *certfile,
327 const char *keyfile,
328 SPDY_NewSessionCallback nscb,
329 SPDY_SessionClosedCallback sccb,
330 SPDY_NewRequestCallback nrcb,
331 SPDY_NewDataCallback npdcb,
332 void * cls,
333 ...)
334{
335 struct SPDY_Daemon *daemon;
336 va_list valist;
337
338 if(SPDY_IO_SUBSYSTEM_NONE == spdyf_io_initialized)
339 {
340 SPDYF_DEBUG("library not initialized");
341 return NULL;
342 }
343 /*
344 * for now make this checks in framing layer
345 if(NULL == certfile)
346 {
347 SPDYF_DEBUG("certfile is NULL");
348 return NULL;
349 }
350 if(NULL == keyfile)
351 {
352 SPDYF_DEBUG("keyfile is NULL");
353 return NULL;
354 }
355 */
356
357 va_start(valist, cls);
358 daemon = SPDYF_start_daemon_va ( port,
359 certfile,
360 keyfile,
361 nscb,
362 sccb,
363 nrcb,
364 npdcb,
365 &spdy_handler_new_stream,
366 &spdy_handler_new_data,
367 cls,
368 NULL,
369 valist
370 );
371 va_end(valist);
372
373 return daemon;
374}
375
376
377void
378SPDY_stop_daemon (struct SPDY_Daemon *daemon)
379{
380 if(NULL == daemon)
381 {
382 SPDYF_DEBUG("daemon is NULL");
383 return;
384 }
385
386 SPDYF_stop_daemon(daemon);
387}
388
389
390struct SPDY_Response *
391SPDY_build_response(int status,
392 const char * statustext,
393 const char * version,
394 struct SPDY_NameValue * headers,
395 const void * data,
396 size_t size)
397{
398 struct SPDY_Response *response = NULL;
399 struct SPDY_NameValue ** all_headers = NULL; //TODO maybe array in stack is enough
400 char *fullstatus = NULL;
401 int ret;
402 int num_hdr_containers = 1;
403
404 if(NULL == version)
405 {
406 SPDYF_DEBUG("version is NULL");
407 return NULL;
408 }
409
410 if(NULL == (response = malloc(sizeof(struct SPDY_Response))))
411 goto free_and_fail;
412 memset(response, 0, sizeof(struct SPDY_Response));
413
414 if(NULL != headers && !SPDYF_name_value_is_empty(headers))
415 num_hdr_containers = 2;
416
417 if(NULL == (all_headers = malloc(num_hdr_containers * sizeof(struct SPDY_NameValue *))))
418 goto free_and_fail;
419 memset(all_headers, 0, num_hdr_containers * sizeof(struct SPDY_NameValue *));
420
421 if(2 == num_hdr_containers)
422 all_headers[1] = headers;
423
424 if(NULL == (all_headers[0] = SPDY_name_value_create()))
425 goto free_and_fail;
426
427 if(NULL == statustext)
428 ret = asprintf(&fullstatus, "%i", status);
429 else
430 ret = asprintf(&fullstatus, "%i %s", status, statustext);
431 if(-1 == ret)
432 goto free_and_fail;
433
434 if(SPDY_YES != SPDY_name_value_add(all_headers[0], ":status", fullstatus))
435 goto free_and_fail;
436
437 free(fullstatus);
438 fullstatus = NULL;
439
440 if(SPDY_YES != SPDY_name_value_add(all_headers[0], ":version", version))
441 goto free_and_fail;
442
443 if(0 >= (response->headers_size = SPDYF_name_value_to_stream(all_headers,
444 num_hdr_containers,
445 &(response->headers))))
446 goto free_and_fail;
447
448 SPDY_name_value_destroy(all_headers[0]);
449 free(all_headers);
450 all_headers = NULL;
451
452 if(size > 0)
453 {
454 //copy the data to the response object
455 if(NULL == (response->data = malloc(size)))
456 {
457 free(response->headers);
458 goto free_and_fail;
459 }
460 memcpy(response->data, data, size);
461 response->data_size = size;
462 }
463
464 return response;
465
466 //for GOTO
467 free_and_fail:
468
469 free(fullstatus);
470 if(NULL != all_headers)
471 SPDY_name_value_destroy(all_headers[0]);
472 free(all_headers);
473 free(response);
474
475 return NULL;
476}
477
478
479struct SPDY_Response *
480SPDY_build_response_with_callback(int status,
481 const char * statustext,
482 const char * version,
483 struct SPDY_NameValue * headers,
484 SPDY_ResponseCallback rcb,
485 void *rcb_cls,
486 uint32_t block_size)
487{
488 struct SPDY_Response *response;
489
490 if(NULL == rcb)
491 {
492 SPDYF_DEBUG("rcb is NULL");
493 return NULL;
494 }
495 if(block_size > SPDY_MAX_SUPPORTED_FRAME_SIZE)
496 {
497 SPDYF_DEBUG("block_size is wrong");
498 return NULL;
499 }
500
501 if(0 == block_size)
502 block_size = SPDY_MAX_SUPPORTED_FRAME_SIZE;
503
504 response = SPDY_build_response(status,
505 statustext,
506 version,
507 headers,
508 NULL,
509 0);
510
511 if(NULL == response)
512 {
513 return NULL;
514 }
515
516 response->rcb = rcb;
517 response->rcb_cls = rcb_cls;
518 response->rcb_block_size = block_size;
519
520 return response;
521}
522
523
524int
525SPDY_queue_response (struct SPDY_Request * request,
526 struct SPDY_Response *response,
527 bool closestream,
528 bool consider_priority,
529 SPDY_ResponseResultCallback rrcb,
530 void * rrcb_cls)
531{
532 struct SPDYF_Response_Queue *headers_to_queue;
533 struct SPDYF_Response_Queue *body_to_queue;
534 SPDYF_ResponseQueueResultCallback frqcb = NULL;
535 void *frqcb_cls = NULL;
536 int int_consider_priority = consider_priority ? SPDY_YES : SPDY_NO;
537
538 if(NULL == request)
539 {
540 SPDYF_DEBUG("request is NULL");
541 return SPDY_INPUT_ERROR;
542 }
543 if(NULL == response)
544 {
545 SPDYF_DEBUG("request is NULL");
546 return SPDY_INPUT_ERROR;
547 }
548
549 if(request->stream->is_out_closed
550 || SPDY_SESSION_STATUS_CLOSING == request->stream->session->status)
551 return SPDY_NO;
552
553 if(NULL != rrcb)
554 {
555 frqcb_cls = request;
556 frqcb = &spdy_handler_response_queue_result;
557 }
558
559 if(response->data_size > 0)
560 {
561 //SYN_REPLY and DATA will be queued
562
563 if(NULL == (headers_to_queue = SPDYF_response_queue_create(false,
564 response->headers,
565 response->headers_size,
566 response,
567 request->stream,
568 false,
569 NULL,
570 NULL,
571 NULL,
572 NULL)))
573 {
574 return SPDY_NO;
575 }
576
577 if(NULL == (body_to_queue = SPDYF_response_queue_create(true,
578 response->data,
579 response->data_size,
580 response,
581 request->stream,
582 closestream,
583 frqcb,
584 frqcb_cls,
585 rrcb,
586 rrcb_cls)))
587 {
588 SPDYF_response_queue_destroy(headers_to_queue);
589 return SPDY_NO;
590 }
591
592 SPDYF_queue_response (headers_to_queue,
593 request->stream->session,
594 int_consider_priority);
595
596 SPDYF_queue_response (body_to_queue,
597 request->stream->session,
598 int_consider_priority);
599 }
600 else if(NULL == response->rcb)
601 {
602 //no "body" will be queued, e.g. HTTP 404 without body
603
604 if(NULL == (headers_to_queue = SPDYF_response_queue_create(false,
605 response->headers,
606 response->headers_size,
607 response,
608 request->stream,
609 closestream,
610 frqcb,
611 frqcb_cls,
612 rrcb,
613 rrcb_cls)))
614 {
615 return SPDY_NO;
616 }
617
618 SPDYF_queue_response (headers_to_queue,
619 request->stream->session,
620 int_consider_priority);
621 }
622 else
623 {
624 //response with callbacks
625
626 if(NULL == (headers_to_queue = SPDYF_response_queue_create(false,
627 response->headers,
628 response->headers_size,
629 response,
630 request->stream,
631 false,
632 NULL,
633 NULL,
634 NULL,
635 NULL)))
636 {
637 return SPDY_NO;
638 }
639
640 if(NULL == (body_to_queue = SPDYF_response_queue_create(true,
641 response->data,
642 response->data_size,
643 response,
644 request->stream,
645 closestream,
646 frqcb,
647 frqcb_cls,
648 rrcb,
649 rrcb_cls)))
650 {
651 SPDYF_response_queue_destroy(headers_to_queue);
652 return SPDY_NO;
653 }
654
655 SPDYF_queue_response (headers_to_queue,
656 request->stream->session,
657 int_consider_priority);
658
659 SPDYF_queue_response (body_to_queue,
660 request->stream->session,
661 int_consider_priority);
662 }
663
664 return SPDY_YES;
665}
666
667
668socklen_t
669SPDY_get_remote_addr(struct SPDY_Session * session,
670 struct sockaddr ** addr)
671{
672 if(NULL == session)
673 {
674 SPDYF_DEBUG("session is NULL");
675 return 0;
676 }
677
678 *addr = session->addr;
679
680 return session->addr_len;
681}
682
683
684struct SPDY_Session *
685SPDY_get_session_for_request(const struct SPDY_Request * request)
686{
687 if(NULL == request)
688 {
689 SPDYF_DEBUG("request is NULL");
690 return NULL;
691 }
692
693 return request->stream->session;
694}
695
696
697void *
698SPDY_get_cls_from_session(struct SPDY_Session * session)
699{
700 if(NULL == session)
701 {
702 SPDYF_DEBUG("session is NULL");
703 return NULL;
704 }
705
706 return session->user_cls;
707}
708
709
710void
711SPDY_set_cls_to_session(struct SPDY_Session * session,
712 void * cls)
713{
714 if(NULL == session)
715 {
716 SPDYF_DEBUG("session is NULL");
717 return;
718 }
719
720 session->user_cls = cls;
721}
722
723
724void *
725SPDY_get_cls_from_request(struct SPDY_Request * request)
726{
727 if(NULL == request)
728 {
729 SPDYF_DEBUG("request is NULL");
730 return NULL;
731 }
732
733 return request->user_cls;
734}
735
736
737void
738SPDY_set_cls_to_request(struct SPDY_Request * request,
739 void * cls)
740{
741 if(NULL == request)
742 {
743 SPDYF_DEBUG("request is NULL");
744 return;
745 }
746
747 request->user_cls = cls;
748}
diff --git a/src/microspdy/applicationlayer.h b/src/microspdy/applicationlayer.h
deleted file mode 100644
index a36760fe..00000000
--- a/src/microspdy/applicationlayer.h
+++ /dev/null
@@ -1,31 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2012 Andrey Uzunov
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file 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
deleted file mode 100644
index 532ab64a..00000000
--- a/src/microspdy/compression.c
+++ /dev/null
@@ -1,441 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2012 Andrey Uzunov
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file 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 unsigned int 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
deleted file mode 100644
index 40746e78..00000000
--- a/src/microspdy/compression.h
+++ /dev/null
@@ -1,117 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2012 Andrey Uzunov
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file 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
deleted file mode 100644
index ca6f0da7..00000000
--- a/src/microspdy/daemon.c
+++ /dev/null
@@ -1,544 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2012 Andrey Uzunov
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file microspdy/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 "io.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) * 1000;
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 case SPDY_DAEMON_OPTION_IO_SUBSYSTEM:
146 daemon->io_subsystem = va_arg (valist, enum SPDY_IO_SUBSYSTEM);
147 break;
148 case SPDY_DAEMON_OPTION_MAX_NUM_FRAMES:
149 daemon->max_num_frames = va_arg (valist, uint32_t);
150 break;
151 default:
152 SPDYF_DEBUG("Wrong option for the daemon %i",opt);
153 return SPDY_NO;
154 }
155 }
156 return SPDY_YES;
157}
158
159
160void
161SPDY_set_panic_func (SPDY_PanicCallback cb,
162 void *cls)
163{
164 spdyf_panic = cb;
165 spdyf_panic_cls = cls;
166}
167
168
169struct SPDY_Daemon *
170SPDYF_start_daemon_va (uint16_t port,
171 const char *certfile,
172 const char *keyfile,
173 SPDY_NewSessionCallback nscb,
174 SPDY_SessionClosedCallback sccb,
175 SPDY_NewRequestCallback nrcb,
176 SPDY_NewDataCallback npdcb,
177 SPDYF_NewStreamCallback fnscb,
178 SPDYF_NewDataCallback fndcb,
179 void * cls,
180 void * fcls,
181 va_list valist)
182{
183 struct SPDY_Daemon *daemon = NULL;
184 int afamily;
185 int option_on = 1;
186 int ret;
187 struct sockaddr_in* servaddr4 = NULL;
188#if HAVE_INET6
189 struct sockaddr_in6* servaddr6 = NULL;
190#endif
191 socklen_t addrlen;
192
193 if (NULL == (daemon = malloc (sizeof (struct SPDY_Daemon))))
194 {
195 SPDYF_DEBUG("malloc");
196 return NULL;
197 }
198 memset (daemon, 0, sizeof (struct SPDY_Daemon));
199 daemon->socket_fd = -1;
200 daemon->port = port;
201
202 if(SPDY_YES != spdyf_parse_options_va (daemon, valist))
203 {
204 SPDYF_DEBUG("parse");
205 goto free_and_fail;
206 }
207
208 if(0 == daemon->max_num_frames)
209 daemon->max_num_frames = SPDYF_NUM_SENT_FRAMES_AT_ONCE;
210
211 if(!port && NULL == daemon->address)
212 {
213 SPDYF_DEBUG("Port is 0");
214 goto free_and_fail;
215 }
216 if(0 == daemon->io_subsystem)
217 daemon->io_subsystem = SPDY_IO_SUBSYSTEM_OPENSSL;
218
219 if(SPDY_YES != SPDYF_io_set_daemon(daemon, daemon->io_subsystem))
220 goto free_and_fail;
221
222 if(SPDY_IO_SUBSYSTEM_RAW != daemon->io_subsystem)
223 {
224 if (NULL == certfile
225 || NULL == (daemon->certfile = strdup (certfile)))
226 {
227 SPDYF_DEBUG("strdup (certfile)");
228 goto free_and_fail;
229 }
230 if (NULL == keyfile
231 || NULL == (daemon->keyfile = strdup (keyfile)))
232 {
233 SPDYF_DEBUG("strdup (keyfile)");
234 goto free_and_fail;
235 }
236 }
237
238 daemon->new_session_cb = nscb;
239 daemon->session_closed_cb = sccb;
240 daemon->new_request_cb = nrcb;
241 daemon->received_data_cb = npdcb;
242 daemon->cls = cls;
243 daemon->fcls = fcls;
244 daemon->fnew_stream_cb = fnscb;
245 daemon->freceived_data_cb = fndcb;
246
247#if HAVE_INET6
248 //handling IPv6
249 if((daemon->flags & SPDY_DAEMON_FLAG_ONLY_IPV6)
250 && NULL != daemon->address && AF_INET6 != daemon->address->sa_family)
251 {
252 SPDYF_DEBUG("SPDY_DAEMON_FLAG_ONLY_IPV6 set but IPv4 address provided");
253 goto free_and_fail;
254 }
255
256 addrlen = sizeof (struct sockaddr_in6);
257
258 if(NULL == daemon->address)
259 {
260 if (NULL == (servaddr6 = malloc (addrlen)))
261 {
262 SPDYF_DEBUG("malloc");
263 goto free_and_fail;
264 }
265 memset (servaddr6, 0, addrlen);
266 servaddr6->sin6_family = AF_INET6;
267 servaddr6->sin6_addr = in6addr_any;
268 servaddr6->sin6_port = htons (port);
269 daemon->address = (struct sockaddr *) servaddr6;
270 }
271
272 if(AF_INET6 == daemon->address->sa_family)
273 {
274 afamily = PF_INET6;
275 }
276 else
277 {
278 afamily = PF_INET;
279 }
280#else
281 //handling IPv4
282 if(daemon->flags & SPDY_DAEMON_FLAG_ONLY_IPV6)
283 {
284 SPDYF_DEBUG("SPDY_DAEMON_FLAG_ONLY_IPV6 set but no support");
285 goto free_and_fail;
286 }
287
288 addrlen = sizeof (struct sockaddr_in);
289
290 if(NULL == daemon->address)
291 {
292 if (NULL == (servaddr4 = malloc (addrlen)))
293 {
294 SPDYF_DEBUG("malloc");
295 goto free_and_fail;
296 }
297 memset (servaddr4, 0, addrlen);
298 servaddr4->sin_family = AF_INET;
299 servaddr4->sin_addr = INADDR_ANY;
300 servaddr4->sin_port = htons (port);
301 daemon->address = (struct sockaddr *) servaddr4;
302 }
303
304 afamily = PF_INET;
305#endif
306
307 daemon->socket_fd = socket (afamily, SOCK_STREAM, 0);
308 if (-1 == daemon->socket_fd)
309 {
310 SPDYF_DEBUG("sock");
311 goto free_and_fail;
312 }
313
314 //setting option for the socket to reuse address
315 ret = setsockopt(daemon->socket_fd, SOL_SOCKET, SO_REUSEADDR, &option_on, sizeof(option_on));
316 if(ret)
317 {
318 SPDYF_DEBUG("WARNING: SO_REUSEADDR was not set for the server");
319 }
320
321#if HAVE_INET6
322 if(daemon->flags & SPDY_DAEMON_FLAG_ONLY_IPV6)
323 {
324 ret = setsockopt(daemon->socket_fd, IPPROTO_IPV6, IPV6_V6ONLY, &option_on, sizeof(option_on));
325 if(ret)
326 {
327 SPDYF_DEBUG("setsockopt with IPPROTO_IPV6 failed");
328 goto free_and_fail;
329 }
330 }
331#endif
332
333 if (-1 == bind (daemon->socket_fd, daemon->address, addrlen))
334 {
335 SPDYF_DEBUG("bind %i",errno);
336 goto free_and_fail;
337 }
338
339 if (listen (daemon->socket_fd, 20) < 0)
340 {
341 SPDYF_DEBUG("listen %i",errno);
342 goto free_and_fail;
343 }
344
345 if(SPDY_YES != daemon->fio_init(daemon))
346 {
347 SPDYF_DEBUG("tls");
348 goto free_and_fail;
349 }
350
351 return daemon;
352
353 //for GOTO
354 free_and_fail:
355 if(daemon->socket_fd > 0)
356 (void)MHD_socket_close_ (daemon->socket_fd);
357
358 free(servaddr4);
359#if HAVE_INET6
360 free(servaddr6);
361#endif
362 if(NULL != daemon->certfile)
363 free(daemon->certfile);
364 if(NULL != daemon->keyfile)
365 free(daemon->keyfile);
366 free (daemon);
367
368 return NULL;
369}
370
371
372void
373SPDYF_stop_daemon (struct SPDY_Daemon *daemon)
374{
375 daemon->fio_deinit(daemon);
376
377 shutdown (daemon->socket_fd, SHUT_RDWR);
378 spdyf_close_all_sessions (daemon);
379 (void)MHD_socket_close_ (daemon->socket_fd);
380
381 if(!(SPDY_DAEMON_OPTION_SOCK_ADDR & daemon->options))
382 free(daemon->address);
383
384 free(daemon->certfile);
385 free(daemon->keyfile);
386
387 free(daemon);
388}
389
390
391int
392SPDYF_get_timeout (struct SPDY_Daemon *daemon,
393 unsigned long long *timeout)
394{
395 unsigned long long earliest_deadline = 0;
396 unsigned long long now;
397 struct SPDY_Session *pos;
398 bool have_timeout;
399
400 if(0 == daemon->session_timeout)
401 return SPDY_NO;
402
403 now = SPDYF_monotonic_time();
404 have_timeout = false;
405 for (pos = daemon->sessions_head; NULL != pos; pos = pos->next)
406 {
407 if ( (! have_timeout) ||
408 (earliest_deadline > pos->last_activity + daemon->session_timeout) )
409 earliest_deadline = pos->last_activity + daemon->session_timeout;
410
411 have_timeout = true;
412
413 if (SPDY_YES == pos->fio_is_pending(pos))
414 {
415 earliest_deadline = 0;
416 break;
417 }
418 }
419
420 if (!have_timeout)
421 return SPDY_NO;
422 if (earliest_deadline <= now)
423 *timeout = 0;
424 else
425 *timeout = earliest_deadline - now;
426
427 return SPDY_YES;
428}
429
430
431int
432SPDYF_get_fdset (struct SPDY_Daemon *daemon,
433 fd_set *read_fd_set,
434 fd_set *write_fd_set,
435 fd_set *except_fd_set,
436 bool all)
437{
438 (void)except_fd_set;
439 struct SPDY_Session *pos;
440 int fd;
441 int max_fd = -1;
442
443 fd = daemon->socket_fd;
444 if (-1 != fd)
445 {
446 FD_SET (fd, read_fd_set);
447 /* update max file descriptor */
448 max_fd = fd;
449 }
450
451 for (pos = daemon->sessions_head; NULL != pos; pos = pos->next)
452 {
453 fd = pos->socket_fd;
454 FD_SET(fd, read_fd_set);
455 if (all
456 || (NULL != pos->response_queue_head) //frames pending
457 || (NULL != pos->write_buffer) //part of last frame pending
458 || (SPDY_SESSION_STATUS_CLOSING == pos->status) //the session is about to be closed
459 || (daemon->session_timeout //timeout passed for the session
460 && (pos->last_activity + daemon->session_timeout < SPDYF_monotonic_time()))
461 || (SPDY_YES == pos->fio_is_pending(pos)) //data in TLS' read buffer pending
462 || ((pos->read_buffer_offset - pos->read_buffer_beginning) > 0) // data in lib's read buffer pending
463 )
464 FD_SET(fd, write_fd_set);
465 if(fd > max_fd)
466 max_fd = fd;
467 }
468
469 return max_fd;
470}
471
472
473void
474SPDYF_run (struct SPDY_Daemon *daemon)
475{
476 struct SPDY_Session *pos;
477 struct SPDY_Session *next;
478 int num_ready;
479 fd_set rs;
480 fd_set ws;
481 fd_set es;
482 int max;
483 struct timeval timeout;
484 int ds;
485
486 timeout.tv_sec = 0;
487 timeout.tv_usec = 0;
488 FD_ZERO (&rs);
489 FD_ZERO (&ws);
490 FD_ZERO (&es);
491 //here we need really all descriptors to see later which are ready
492 max = SPDYF_get_fdset(daemon,&rs,&ws,&es, true);
493
494 num_ready = select (max + 1, &rs, &ws, &es, &timeout);
495
496 if(num_ready < 1)
497 return;
498
499 if ( (-1 != (ds = daemon->socket_fd)) &&
500 (FD_ISSET (ds, &rs)) ){
501 SPDYF_session_accept(daemon);
502 }
503
504 next = daemon->sessions_head;
505 while (NULL != (pos = next))
506 {
507 next = pos->next;
508 ds = pos->socket_fd;
509 if (ds != -1)
510 {
511 //fill the read buffer
512 if (FD_ISSET (ds, &rs) || pos->fio_is_pending(pos)){
513 SPDYF_session_read(pos);
514 }
515
516 //do something with the data in read buffer
517 if(SPDY_NO == SPDYF_session_idle(pos))
518 {
519 //the session was closed, cannot write anymore
520 //continue;
521 }
522
523 //write whatever has been put to the response queue
524 //during read or idle operation, something might be put
525 //on the response queue, thus call write operation
526 if (FD_ISSET (ds, &ws)){
527 if(SPDY_NO == SPDYF_session_write(pos, false))
528 {
529 //SPDYF_session_close(pos);
530 //continue;
531 }
532 }
533
534 /* the response queue has been flushed for half closed
535 * connections, so let close them */
536 /*if(pos->read_closed)
537 {
538 SPDYF_session_close(pos);
539 }*/
540 }
541 }
542
543 spdyf_cleanup_sessions(daemon);
544}
diff --git a/src/microspdy/daemon.h b/src/microspdy/daemon.h
deleted file mode 100644
index cb3ed5fa..00000000
--- a/src/microspdy/daemon.h
+++ /dev/null
@@ -1,130 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2012 Andrey Uzunov
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file 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 * Global flags containing the initialized IO subsystems.
33 */
34enum SPDY_IO_SUBSYSTEM spdyf_io_initialized;
35
36
37/**
38 * Start a SPDDY webserver on the given port.
39 *
40 * @param port port to bind to
41 * @param certfile path to the certificate that will be used by server
42 * @param keyfile path to the keyfile for the certificate
43 * @param nscb callback called when a new SPDY session is
44 * established by a client
45 * @param sccb callback called when a client closes the session
46 * @param nrcb callback called when a client sends request
47 * @param npdcb callback called when HTTP POST params are received
48 * after request
49 * @param fnscb callback called when new stream is opened by a client
50 * @param fndcb callback called when new data -- within a data frame --
51 * is received by the server
52 * @param cls extra argument to all of the callbacks without those
53 * specific only for the framing layer
54 * @param fcls extra argument to all of the callbacks, specific only for
55 * the framing layer (those vars starting with 'f').
56 * @param valist va_list of options (type-value pairs,
57 * terminated with SPDY_DAEMON_OPTION_END).
58 * @return NULL on error, handle to daemon on success
59 */
60struct SPDY_Daemon *
61SPDYF_start_daemon_va (uint16_t port,
62 const char *certfile,
63 const char *keyfile,
64 SPDY_NewSessionCallback nscb,
65 SPDY_SessionClosedCallback sccb,
66 SPDY_NewRequestCallback nrcb,
67 SPDY_NewDataCallback npdcb,
68 SPDYF_NewStreamCallback fnscb,
69 SPDYF_NewDataCallback fndcb,
70 void * cls,
71 void * fcls,
72 va_list valist);
73
74
75/**
76 * Run webserver operations (without blocking unless
77 * in client callbacks). This method must be called in the client event
78 * loop.
79 *
80 * @param daemon daemon to run
81 */
82void
83SPDYF_run (struct SPDY_Daemon *daemon);
84
85
86/**
87 * Obtain timeout value for select for this daemon. The returned value
88 * is how long select
89 * should at most block, not the timeout value set for connections.
90 *
91 * @param daemon daemon to query for timeout
92 * @param timeout set to the timeout (in milliseconds)
93 * @return SPDY_YES on success, SPDY_NO if no connections exist that
94 * would necessiate the use of a timeout right now
95 */
96int
97SPDYF_get_timeout (struct SPDY_Daemon *daemon,
98 unsigned long long *timeout);
99
100
101/**
102 * Obtain the select sets for this daemon. The idea of SPDYF_get_fdset
103 * is to return such descriptors that the select in the application can
104 * return and SPDY_run can be called only when this is really needed.
105 * That means not all sockets will be added to write_fd_set.
106 *
107 * @param daemon daemon to get sets from
108 * @param read_fd_set read set
109 * @param write_fd_set write set
110 * @param except_fd_set except set
111 * @param all add all session's descriptors to write_fd_set or not
112 * @return largest FD added
113 */
114int
115SPDYF_get_fdset (struct SPDY_Daemon *daemon,
116 fd_set *read_fd_set,
117 fd_set *write_fd_set,
118 fd_set *except_fd_set,
119 bool all);
120
121
122/**
123 * Shutdown the daemon.
124 *
125 * @param daemon daemon to stop
126 */
127void
128SPDYF_stop_daemon (struct SPDY_Daemon *daemon);
129
130#endif
diff --git a/src/microspdy/internal.c b/src/microspdy/internal.c
deleted file mode 100644
index f0d2fc11..00000000
--- a/src/microspdy/internal.c
+++ /dev/null
@@ -1,40 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2012 Andrey Uzunov
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file microspdy/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
29unsigned long long
30SPDYF_monotonic_time (void)
31{
32#ifdef HAVE_CLOCK_GETTIME
33#ifdef CLOCK_MONOTONIC
34 struct timespec ts;
35 if (0 == clock_gettime (CLOCK_MONOTONIC, &ts))
36 return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
37#endif
38#endif
39 return time (NULL) * 1000;
40}
diff --git a/src/microspdy/internal.h b/src/microspdy/internal.h
deleted file mode 100644
index b5382c01..00000000
--- a/src/microspdy/internal.h
+++ /dev/null
@@ -1,199 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2012 Andrey Uzunov
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file microspdy/internal.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 "platform_interface.h"
30#include "microspdy.h"
31
32/**
33 * size of read buffers for each connection
34 * must be at least the size of SPDY_MAX_SUPPORTED_FRAME_SIZE
35 */
36#define SPDYF_BUFFER_SIZE 8192
37
38/**
39 * initial size of window for each stream (this is for the data
40 * within data frames that can be handled)
41 */
42#define SPDYF_INITIAL_WINDOW_SIZE 65536
43
44/**
45 * number of frames written to the socket at once. After X frames
46 * everything should be run again. In this way the application can
47 * response to more important requests while a big file is still
48 * being transmitted to the client
49 */
50#define SPDYF_NUM_SENT_FRAMES_AT_ONCE 10
51
52
53/**
54 * Handler for fatal errors.
55 */
56extern SPDY_PanicCallback spdyf_panic;
57
58
59/**
60 * Closure argument for "mhd_panic".
61 */
62extern void *spdyf_panic_cls;
63
64
65/**
66 * Trigger 'panic' action based on fatal errors.
67 *
68 * @param msg error message (const char *)
69 */
70#define SPDYF_PANIC(msg) \
71 spdyf_panic (spdyf_panic_cls, __FILE__, __LINE__, msg)
72
73
74/**
75 * Asserts the validity of an expression.
76 *
77 * @param expr (bool)
78 * @param msg message to print on error (const char *)
79 */
80#define SPDYF_ASSERT(expr, msg) \
81 if(!(expr)){\
82 SPDYF_PANIC(msg);\
83 abort();\
84 }
85
86
87/**
88 * Convert 24 bit integer from host byte order to network byte order.
89 *
90 * @param n input value (int32_t)
91 * @return converted value (uint32_t)
92 */
93#if HAVE_BIG_ENDIAN
94#define HTON24(n) n
95#else
96#define HTON24(n) (((((uint32_t)(n) & 0xFF)) << 16)\
97 | (((uint32_t)(n) & 0xFF00))\
98 | ((((uint32_t)(n) & 0xFF0000)) >> 16))
99#endif
100
101
102/**
103 * Convert 24 bit integer from network byte order to host byte order.
104 *
105 * @param n input value (int32_t)
106 * @return converted value (uint32_t)
107 */
108#if HAVE_BIG_ENDIAN
109#define NTOH24(n) n
110#else
111#define NTOH24(n) (((((uint32_t)(n) & 0xFF)) << 16)\
112 | (((uint32_t)(n) & 0xFF00))\
113 | ((((uint32_t)(n) & 0xFF0000)) >> 16))
114#endif
115
116
117/**
118 * Convert 31 bit integer from network byte order to host byte order.
119 *
120 * @param n input value (int32_t)
121 * @return converted value (uint32_t)
122 */
123#if HAVE_BIG_ENDIAN
124#define NTOH31(n) n
125#else
126#define NTOH31(n) (((((uint32_t)(n) & 0x7F)) << 24) | \
127 ((((uint32_t)(n) & 0xFF00)) << 8) | \
128 ((((uint32_t)(n) & 0xFF0000)) >> 8) | \
129 ((((uint32_t)(n) & 0xFF000000)) >> 24))
130#endif
131
132
133/**
134 * Convert 31 bit integer from host byte order to network byte order.
135 *
136 * @param n input value (int32_t)
137 * @return converted value (uint32_t)
138 */
139#if HAVE_BIG_ENDIAN
140#define HTON31(n) n
141#else
142#define HTON31(n) (((((uint32_t)(n) & 0xFF)) << 24) | \
143 ((((uint32_t)(n) & 0xFF00)) << 8) | \
144 ((((uint32_t)(n) & 0xFF0000)) >> 8) | \
145 ((((uint32_t)(n) & 0x7F000000)) >> 24))
146#endif
147
148
149/**
150 * Print formatted debug value.
151 *
152 * @param fmt format (const char *)
153 * @param ... args for format
154 */
155#define SPDYF_DEBUG(fmt, ...) do { \
156 fprintf (stdout, "%s\n%u: ",__FILE__, __LINE__);\
157 fprintf(stdout,fmt,##__VA_ARGS__);\
158 fprintf(stdout,"\n");\
159 fflush(stdout); } while (0)
160
161
162/**
163 * Print stream for debuging.
164 *
165 * @param strm (void *)
166 * @param size (int)
167 */
168#define SPDYF_PRINT_STREAM(strm, size) do { \
169 int ___i;\
170 for(___i=0;___i<size;___i++){\
171 fprintf(stdout,"%x ",*((uint8_t *) strm + ___i));\
172 fflush(stdout);\
173 }\
174 fprintf(stdout,"\n");\
175 } while (0)
176
177
178/**
179 * Print message and raise SIGINT for debug purposes.
180 *
181 * @param msg message (const char *)
182 */
183#define SPDYF_SIGINT(msg) do { \
184 fprintf(stdout,"%i : %s\n", __LINE__,__FILE__);\
185 fprintf(stdout,msg);\
186 fprintf(stdout,"\n");\
187 fflush(stdout);\
188 raise(SIGINT); } while (0)
189
190
191/**
192 * Returns monotonic time, to be used for session timeouts.
193 *
194 * @return time in milliseconds
195 */
196unsigned long long
197SPDYF_monotonic_time(void);
198
199#endif
diff --git a/src/microspdy/io.c b/src/microspdy/io.c
deleted file mode 100644
index c333c89a..00000000
--- a/src/microspdy/io.c
+++ /dev/null
@@ -1,90 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2013 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 io.c
21 * @brief Generic functions for IO.
22 * @author Andrey Uzunov
23 */
24
25#include "platform.h"
26#include "structures.h"
27#include "internal.h"
28#include "io.h"
29
30
31int
32SPDYF_io_set_daemon(struct SPDY_Daemon *daemon,
33 enum SPDY_IO_SUBSYSTEM io_subsystem)
34{
35 switch(io_subsystem)
36 {
37 case SPDY_IO_SUBSYSTEM_OPENSSL:
38 daemon->fio_init = &SPDYF_openssl_init;
39 daemon->fio_deinit = &SPDYF_openssl_deinit;
40 break;
41
42 case SPDY_IO_SUBSYSTEM_RAW:
43 daemon->fio_init = &SPDYF_raw_init;
44 daemon->fio_deinit = &SPDYF_raw_deinit;
45 break;
46
47 case SPDY_IO_SUBSYSTEM_NONE:
48 default:
49 SPDYF_DEBUG("Unsupported subsystem");
50 return SPDY_NO;
51 }
52
53 return SPDY_YES;
54}
55
56
57int
58SPDYF_io_set_session(struct SPDY_Session *session,
59 enum SPDY_IO_SUBSYSTEM io_subsystem)
60{
61 switch(io_subsystem)
62 {
63 case SPDY_IO_SUBSYSTEM_OPENSSL:
64 session->fio_new_session = &SPDYF_openssl_new_session;
65 session->fio_close_session = &SPDYF_openssl_close_session;
66 session->fio_is_pending = &SPDYF_openssl_is_pending;
67 session->fio_recv = &SPDYF_openssl_recv;
68 session->fio_send = &SPDYF_openssl_send;
69 session->fio_before_write = &SPDYF_openssl_before_write;
70 session->fio_after_write = &SPDYF_openssl_after_write;
71 break;
72
73 case SPDY_IO_SUBSYSTEM_RAW:
74 session->fio_new_session = &SPDYF_raw_new_session;
75 session->fio_close_session = &SPDYF_raw_close_session;
76 session->fio_is_pending = &SPDYF_raw_is_pending;
77 session->fio_recv = &SPDYF_raw_recv;
78 session->fio_send = &SPDYF_raw_send;
79 session->fio_before_write = &SPDYF_raw_before_write;
80 session->fio_after_write = &SPDYF_raw_after_write;
81 break;
82
83 case SPDY_IO_SUBSYSTEM_NONE:
84 default:
85 SPDYF_DEBUG("Unsupported subsystem");
86 return SPDY_NO;
87 }
88
89 return SPDY_YES;
90}
diff --git a/src/microspdy/io.h b/src/microspdy/io.h
deleted file mode 100644
index c28ba21b..00000000
--- a/src/microspdy/io.h
+++ /dev/null
@@ -1,216 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2013 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 io.h
21 * @brief Signatures for IO functions.
22 * @author Andrey Uzunov
23 */
24
25#ifndef IO_H
26#define IO_H
27
28#include "platform.h"
29#include "io_openssl.h"
30#include "io_raw.h"
31
32
33/**
34 * Used for return code when reading and writing to the TLS socket.
35 */
36enum SPDY_IO_ERROR
37{
38 /**
39 * The connection was closed by the other party.
40 */
41 SPDY_IO_ERROR_CLOSED = 0,
42
43 /**
44 * Any kind of error ocurred. The session has to be closed.
45 */
46 SPDY_IO_ERROR_ERROR = -2,
47
48 /**
49 * The function had to return without processing any data. The whole
50 * cycle of events has to be called again (SPDY_run) as something
51 * either has to be written or read or the the syscall was
52 * interrupted by a signal.
53 */
54 SPDY_IO_ERROR_AGAIN = -3,
55};
56
57
58/**
59 * Global initializing. Must be called only once in the program.
60 *
61 */
62typedef void
63(*SPDYF_IOGlobalInit) ();
64
65
66/**
67 * Global deinitializing for the whole program. Should be called
68 * at the end of the program.
69 *
70 */
71typedef void
72(*SPDYF_IOGlobalDeinit) ();
73
74
75/**
76 * Initializing of io context for a specific daemon.
77 * Must be called when the daemon starts.
78 *
79 * @param daemon SPDY_Daemon for which io will be used. Daemon's
80 * certificate and key file are used for tls.
81 * @return SPDY_YES on success or SPDY_NO on error
82 */
83typedef int
84(*SPDYF_IOInit) (struct SPDY_Daemon *daemon);
85
86
87/**
88 * Deinitializing io context for a daemon. Should be called
89 * when the deamon is stopped.
90 *
91 * @param daemon SPDY_Daemon which is being stopped
92 */
93typedef void
94(*SPDYF_IODeinit) (struct SPDY_Daemon *daemon);
95
96
97/**
98 * Initializing io for a specific connection. Must be called
99 * after the connection has been accepted.
100 *
101 * @param session SPDY_Session whose socket will be used
102 * @return SPDY_NO if some funcs inside fail. SPDY_YES otherwise
103 */
104typedef int
105(*SPDYF_IONewSession) (struct SPDY_Session *session);
106
107
108/**
109 * Deinitializing io for a specific connection. Should be called
110 * closing session's socket.
111 *
112 * @param session SPDY_Session whose socket is used
113 */
114typedef void
115(*SPDYF_IOCloseSession) (struct SPDY_Session *session);
116
117
118/**
119 * Reading from session's socket. Reads available data and put it to the
120 * buffer.
121 *
122 * @param session for which data is received
123 * @param buffer where data from the socket will be written to
124 * @param size of the buffer
125 * @return number of bytes (at most size) read from the connection
126 * 0 if the other party has closed the connection
127 * SPDY_IO_ERROR code on error
128 */
129typedef int
130(*SPDYF_IORecv) (struct SPDY_Session *session,
131 void * buffer,
132 size_t size);
133
134
135/**
136 * Writing to session's socket. Writes the data given into the buffer to the
137 * socket.
138 *
139 * @param session whose context is used
140 * @param buffer from where data will be written to the socket
141 * @param size number of bytes to be taken from the buffer
142 * @return number of bytes (at most size) from the buffer that has been
143 * written to the connection
144 * 0 if the other party has closed the connection
145 * SPDY_IO_ERROR code on error
146 */
147typedef int
148(*SPDYF_IOSend) (struct SPDY_Session *session,
149 const void * buffer,
150 size_t size);
151
152
153/**
154 * Checks if there is data staying in the buffers of the underlying
155 * system that waits to be read. In case of TLS, this will call
156 * something like SSL_pending().
157 *
158 * @param session which is checked
159 * @return SPDY_YES if data is pending or SPDY_NO otherwise
160 */
161typedef int
162(*SPDYF_IOIsPending) (struct SPDY_Session *session);
163
164
165/**
166 * Called just before frames are about to be processed and written
167 * to the socket.
168 *
169 * @param session
170 * @return SPDY_NO if writing must not happen in the call;
171 * SPDY_YES otherwise
172 */
173typedef int
174(*SPDYF_IOBeforeWrite) (struct SPDY_Session *session);
175
176
177/**
178 * Called just after frames have been processed and written
179 * to the socket.
180 *
181 * @param session
182 * @param was_written has the same value as the write function for the
183 * session will return
184 * @return returned value will be used by the write function to return
185 */
186typedef int
187(*SPDYF_IOAfterWrite) (struct SPDY_Session *session,
188 int was_written);
189
190
191/**
192 * Sets callbacks for the daemon with regard to the IO subsystem.
193 *
194 * @param daemon
195 * @param io_subsystem the IO subsystem that will
196 * be initialized and used by daemon.
197 * @return SPDY_YES on success or SPDY_NO otherwise
198 */
199int
200SPDYF_io_set_daemon(struct SPDY_Daemon *daemon,
201 enum SPDY_IO_SUBSYSTEM io_subsystem);
202
203
204/**
205 * Sets callbacks for the session with regard to the IO subsystem.
206 *
207 * @param session
208 * @param io_subsystem the IO subsystem that will
209 * be initialized and used by session.
210 * @return SPDY_YES on success or SPDY_NO otherwise
211 */
212int
213SPDYF_io_set_session(struct SPDY_Session *session,
214 enum SPDY_IO_SUBSYSTEM io_subsystem);
215
216#endif
diff --git a/src/microspdy/io_openssl.c b/src/microspdy/io_openssl.c
deleted file mode 100644
index f71a9230..00000000
--- a/src/microspdy/io_openssl.c
+++ /dev/null
@@ -1,280 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2012 Andrey Uzunov
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file io_openssl.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 "io_openssl.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_openssl_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_openssl_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_openssl_init(struct SPDY_Daemon *daemon)
79{
80 int options;
81 //create ssl context. TLSv1 used
82 if(NULL == (daemon->io_context = SSL_CTX_new(TLSv1_server_method())))
83 {
84 SPDYF_DEBUG("Couldn't create ssl context");
85 return SPDY_NO;
86 }
87 //set options for tls
88 //TODO DH is not enabled for easier debugging
89 //SSL_CTX_set_options(daemon->io_context, SSL_OP_SINGLE_DH_USE);
90
91 //TODO here session tickets are disabled for easier debuging with
92 //wireshark when using Chrome
93 // SSL_OP_NO_COMPRESSION disables TLS compression to avoid CRIME attack
94 options = SSL_OP_NO_TICKET;
95#ifdef SSL_OP_NO_COMPRESSION
96 options |= SSL_OP_NO_COMPRESSION;
97#elif OPENSSL_VERSION_NUMBER >= 0x00908000L /* workaround for OpenSSL 0.9.8 */
98 sk_SSL_COMP_zero(SSL_COMP_get_compression_methods());
99#endif
100
101 SSL_CTX_set_options(daemon->io_context, options);
102 if(1 != SSL_CTX_use_certificate_file(daemon->io_context, daemon->certfile , SSL_FILETYPE_PEM))
103 {
104 SPDYF_DEBUG("Couldn't load the cert file");
105 SSL_CTX_free(daemon->io_context);
106 return SPDY_NO;
107 }
108 if(1 != SSL_CTX_use_PrivateKey_file(daemon->io_context, daemon->keyfile, SSL_FILETYPE_PEM))
109 {
110 SPDYF_DEBUG("Couldn't load the name file");
111 SSL_CTX_free(daemon->io_context);
112 return SPDY_NO;
113 }
114 SSL_CTX_set_next_protos_advertised_cb(daemon->io_context, &spdyf_next_protos_advertised_cb, NULL);
115 if (1 != SSL_CTX_set_cipher_list(daemon->io_context, "HIGH"))
116 {
117 SPDYF_DEBUG("Couldn't set the desired cipher list");
118 SSL_CTX_free(daemon->io_context);
119 return SPDY_NO;
120 }
121
122 return SPDY_YES;
123}
124
125
126void
127SPDYF_openssl_deinit(struct SPDY_Daemon *daemon)
128{
129 SSL_CTX_free(daemon->io_context);
130}
131
132
133int
134SPDYF_openssl_new_session(struct SPDY_Session *session)
135{
136 int ret;
137
138 if(NULL == (session->io_context = SSL_new(session->daemon->io_context)))
139 {
140 SPDYF_DEBUG("Couldn't create ssl structure");
141 return SPDY_NO;
142 }
143 if(1 != (ret = SSL_set_fd(session->io_context, session->socket_fd)))
144 {
145 SPDYF_DEBUG("SSL_set_fd %i",ret);
146 SSL_free(session->io_context);
147 session->io_context = NULL;
148 return SPDY_NO;
149 }
150
151 //for non-blocking I/O SSL_accept may return -1
152 //and this function won't work
153 if(1 != (ret = SSL_accept(session->io_context)))
154 {
155 SPDYF_DEBUG("SSL_accept %i",ret);
156 SSL_free(session->io_context);
157 session->io_context = NULL;
158 return SPDY_NO;
159 }
160 /* alternatively
161 SSL_set_accept_state(session->io_context);
162 * may be called and then the negotiation will be done on reading
163 */
164
165 return SPDY_YES;
166}
167
168
169void
170SPDYF_openssl_close_session(struct SPDY_Session *session)
171{
172 //SSL_shutdown sends TLS "close notify" as in TLS standard.
173 //The function may fail as it waits for the other party to also close
174 //the TLS session. The lib just sends it and will close the socket
175 //after that because the browsers don't seem to care much about
176 //"close notify"
177 SSL_shutdown(session->io_context);
178
179 SSL_free(session->io_context);
180}
181
182
183int
184SPDYF_openssl_recv(struct SPDY_Session *session,
185 void * buffer,
186 size_t size)
187{
188 int ret;
189 int n = SSL_read(session->io_context,
190 buffer,
191 size);
192 //if(n > 0) SPDYF_DEBUG("recvd: %i",n);
193 if (n <= 0)
194 {
195 ret = SSL_get_error(session->io_context, n);
196 switch(ret)
197 {
198 case SSL_ERROR_ZERO_RETURN:
199 return 0;
200
201 case SSL_ERROR_WANT_READ:
202 case SSL_ERROR_WANT_WRITE:
203 return SPDY_IO_ERROR_AGAIN;
204
205 case SSL_ERROR_SYSCALL:
206 if(EINTR == errno)
207 return SPDY_IO_ERROR_AGAIN;
208 return SPDY_IO_ERROR_ERROR;
209 default:
210 return SPDY_IO_ERROR_ERROR;
211 }
212 }
213
214 return n;
215}
216
217
218int
219SPDYF_openssl_send(struct SPDY_Session *session,
220 const void * buffer,
221 size_t size)
222{
223 int ret;
224
225 int n = SSL_write(session->io_context,
226 buffer,
227 size);
228 //if(n > 0) SPDYF_DEBUG("sent: %i",n);
229 if (n <= 0)
230 {
231 ret = SSL_get_error(session->io_context, n);
232 switch(ret)
233 {
234 case SSL_ERROR_ZERO_RETURN:
235 return 0;
236
237 case SSL_ERROR_WANT_READ:
238 case SSL_ERROR_WANT_WRITE:
239 return SPDY_IO_ERROR_AGAIN;
240
241 case SSL_ERROR_SYSCALL:
242 if(EINTR == errno)
243 return SPDY_IO_ERROR_AGAIN;
244 return SPDY_IO_ERROR_ERROR;
245 default:
246 return SPDY_IO_ERROR_ERROR;
247 }
248 }
249
250 return n;
251}
252
253
254int
255SPDYF_openssl_is_pending(struct SPDY_Session *session)
256{
257 /* From openssl docs:
258 * BUGS
259SSL_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().
260 */
261 return SSL_pending(session->io_context) > 0 ? SPDY_YES : SPDY_NO;
262}
263
264
265int
266SPDYF_openssl_before_write(struct SPDY_Session *session)
267{
268 (void)session;
269
270 return SPDY_YES;
271}
272
273
274int
275SPDYF_openssl_after_write(struct SPDY_Session *session, int was_written)
276{
277 (void)session;
278
279 return was_written;
280}
diff --git a/src/microspdy/io_openssl.h b/src/microspdy/io_openssl.h
deleted file mode 100644
index a4e94293..00000000
--- a/src/microspdy/io_openssl.h
+++ /dev/null
@@ -1,166 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2012 Andrey Uzunov
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file io_openssl.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 IO_OPENSSL_H
28#define IO_OPENSSL_H
29
30#include "platform.h"
31#include "io.h"
32#include <openssl/err.h>
33#include <openssl/ssl.h>
34#include <openssl/rand.h>
35
36
37/**
38 * Global initializing of openssl. Must be called only once in the program.
39 *
40 */
41void
42SPDYF_openssl_global_init();
43
44
45/**
46 * Global deinitializing of openssl for the whole program. Should be called
47 * at the end of the program.
48 *
49 */
50void
51SPDYF_openssl_global_deinit();
52
53
54/**
55 * Initializing of openssl for a specific daemon.
56 * Must be called when the daemon starts.
57 *
58 * @param daemon SPDY_Daemon for which openssl will be used. Daemon's
59 * certificate and key file are used.
60 * @return SPDY_YES on success or SPDY_NO on error
61 */
62int
63SPDYF_openssl_init(struct SPDY_Daemon *daemon);
64
65
66/**
67 * Deinitializing openssl for a daemon. Should be called
68 * when the deamon is stopped.
69 *
70 * @param daemon SPDY_Daemon which is being stopped
71 */
72void
73SPDYF_openssl_deinit(struct SPDY_Daemon *daemon);
74
75
76/**
77 * Initializing openssl for a specific connection. Must be called
78 * after the connection has been accepted.
79 *
80 * @param session SPDY_Session whose socket will be used by openssl
81 * @return SPDY_NO if some openssl funcs fail. SPDY_YES otherwise
82 */
83int
84SPDYF_openssl_new_session(struct SPDY_Session *session);
85
86
87/**
88 * Deinitializing openssl for a specific connection. Should be called
89 * closing session's socket.
90 *
91 * @param session SPDY_Session whose socket is used by openssl
92 */
93void
94SPDYF_openssl_close_session(struct SPDY_Session *session);
95
96
97/**
98 * Reading from a TLS socket. Reads available data and put it to the
99 * buffer.
100 *
101 * @param session for which data is received
102 * @param buffer where data from the socket will be written to
103 * @param size of the buffer
104 * @return number of bytes (at most size) read from the TLS connection
105 * 0 if the other party has closed the connection
106 * SPDY_IO_ERROR code on error
107 */
108int
109SPDYF_openssl_recv(struct SPDY_Session *session,
110 void * buffer,
111 size_t size);
112
113
114/**
115 * Writing to a TLS socket. Writes the data given into the buffer to the
116 * TLS socket.
117 *
118 * @param session whose context is used
119 * @param buffer from where data will be written to the socket
120 * @param size number of bytes to be taken from the buffer
121 * @return number of bytes (at most size) from the buffer that has been
122 * written to the TLS connection
123 * 0 if the other party has closed the connection
124 * SPDY_IO_ERROR code on error
125 */
126int
127SPDYF_openssl_send(struct SPDY_Session *session,
128 const void * buffer,
129 size_t size);
130
131
132/**
133 * Checks if there is data staying in the buffers of the underlying
134 * system that waits to be read.
135 *
136 * @param session which is checked
137 * @return SPDY_YES if data is pending or SPDY_NO otherwise
138 */
139int
140SPDYF_openssl_is_pending(struct SPDY_Session *session);
141
142
143/**
144 * Nothing.
145 *
146 * @param session
147 * @return SPDY_NO if writing must not happen in the call;
148 * SPDY_YES otherwise
149 */
150int
151SPDYF_openssl_before_write(struct SPDY_Session *session);
152
153
154/**
155 * Nothing.
156 *
157 * @param session
158 * @param was_written has the same value as the write function for the
159 * session will return
160 * @return returned value will be used by the write function to return
161 */
162int
163SPDYF_openssl_after_write(struct SPDY_Session *session, int was_written);
164
165
166#endif
diff --git a/src/microspdy/io_raw.c b/src/microspdy/io_raw.c
deleted file mode 100644
index 722f347e..00000000
--- a/src/microspdy/io_raw.c
+++ /dev/null
@@ -1,194 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2013 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 io_raw.c
21 * @brief IO for SPDY without TLS.
22 * @author Andrey Uzunov
23 */
24
25#include "platform.h"
26#include "internal.h"
27#include "session.h"
28#include "io_raw.h"
29//TODO put in in the right place
30#include <netinet/tcp.h>
31
32
33void
34SPDYF_raw_global_init()
35{
36}
37
38
39void
40SPDYF_raw_global_deinit()
41{
42}
43
44
45int
46SPDYF_raw_init(struct SPDY_Daemon *daemon)
47{
48 (void)daemon;
49
50 return SPDY_YES;
51}
52
53
54void
55SPDYF_raw_deinit(struct SPDY_Daemon *daemon)
56{
57 (void)daemon;
58}
59
60
61int
62SPDYF_raw_new_session(struct SPDY_Session *session)
63{
64 int fd_flags;
65 int val = 1;
66 int ret;
67
68 //setting the socket to be non-blocking
69 fd_flags = fcntl (session->socket_fd, F_GETFL);
70 if ( -1 == fd_flags
71 || 0 != fcntl (session->socket_fd, F_SETFL, fd_flags | O_NONBLOCK))
72 SPDYF_DEBUG("WARNING: Couldn't set the new connection to be non-blocking");
73
74 if(SPDY_DAEMON_FLAG_NO_DELAY & session->daemon->flags)
75 {
76 ret = setsockopt(session->socket_fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val));
77 if(-1 == ret)
78 SPDYF_DEBUG("WARNING: Couldn't set the new connection to TCP_NODELAY");
79 }
80
81 return SPDY_YES;
82}
83
84
85void
86SPDYF_raw_close_session(struct SPDY_Session *session)
87{
88 (void)session;
89}
90
91
92int
93SPDYF_raw_recv(struct SPDY_Session *session,
94 void * buffer,
95 size_t size)
96{
97 int n = read(session->socket_fd,
98 buffer,
99 size);
100 //if(n > 0) SPDYF_DEBUG("recvd: %i",n);
101 if (n < 0)
102 {
103 switch(errno)
104 {
105 case EAGAIN:
106#if EAGAIN != EWOULDBLOCK
107 case EWOULDBLOCK:
108#endif
109 case EINTR:
110 return SPDY_IO_ERROR_AGAIN;
111
112 default:
113 return SPDY_IO_ERROR_ERROR;
114 }
115 }
116
117 return n;
118}
119
120
121int
122SPDYF_raw_send(struct SPDY_Session *session,
123 const void * buffer,
124 size_t size)
125{
126 int n = write(session->socket_fd,
127 buffer,
128 size);
129 //if(n > 0) SPDYF_DEBUG("sent: %i",n);
130 if (n < 0)
131 {
132 switch(errno)
133 {
134 case EAGAIN:
135#if EAGAIN != EWOULDBLOCK
136 case EWOULDBLOCK:
137#endif
138 case EINTR:
139 return SPDY_IO_ERROR_AGAIN;
140
141 default:
142 return SPDY_IO_ERROR_ERROR;
143 }
144 }
145
146 return n;
147}
148
149
150int
151SPDYF_raw_is_pending(struct SPDY_Session *session)
152{
153 (void)session;
154
155 return SPDY_NO;
156}
157
158
159int
160SPDYF_raw_before_write(struct SPDY_Session *session)
161{
162#if HAVE_DECL_TCP_CORK
163 if(0 == (SPDY_DAEMON_FLAG_NO_DELAY & session->daemon->flags))
164 {
165 int val = 1;
166 int ret;
167
168 ret = setsockopt(session->socket_fd, IPPROTO_TCP, TCP_CORK, &val, (socklen_t)sizeof(val));
169 if(-1 == ret)
170 SPDYF_DEBUG("WARNING: Couldn't set the new connection to TCP_CORK");
171 }
172#endif
173
174 return SPDY_YES;
175}
176
177
178int
179SPDYF_raw_after_write(struct SPDY_Session *session, int was_written)
180{
181#if HAVE_DECL_TCP_CORK
182 if(SPDY_YES == was_written && 0 == (SPDY_DAEMON_FLAG_NO_DELAY & session->daemon->flags))
183 {
184 int val = 0;
185 int ret;
186
187 ret = setsockopt(session->socket_fd, IPPROTO_TCP, TCP_CORK, &val, (socklen_t)sizeof(val));
188 if(-1 == ret)
189 SPDYF_DEBUG("WARNING: Couldn't unset the new connection to TCP_CORK");
190 }
191
192#endif
193 return was_written;
194}
diff --git a/src/microspdy/io_raw.h b/src/microspdy/io_raw.h
deleted file mode 100644
index 8ca830bf..00000000
--- a/src/microspdy/io_raw.h
+++ /dev/null
@@ -1,158 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2013 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 io_raw.h
21 * @brief IO for SPDY without TLS.
22 * @author Andrey Uzunov
23 */
24
25#ifndef IO_RAW_H
26#define IO_RAW_H
27
28#include "platform.h"
29
30
31/**
32 * Must be called only once in the program.
33 *
34 */
35void
36SPDYF_raw_global_init();
37
38
39/**
40 * Should be called
41 * at the end of the program.
42 *
43 */
44void
45SPDYF_raw_global_deinit();
46
47
48/**
49 * Must be called when the daemon starts.
50 *
51 * @param daemon SPDY_Daemon
52 * @return SPDY_YES on success or SPDY_NO on error
53 */
54int
55SPDYF_raw_init(struct SPDY_Daemon *daemon);
56
57
58/**
59 * Should be called
60 * when the deamon is stopped.
61 *
62 * @param daemon SPDY_Daemon which is being stopped
63 */
64void
65SPDYF_raw_deinit(struct SPDY_Daemon *daemon);
66
67
68/**
69 * Must be called
70 * after the connection has been accepted.
71 *
72 * @param session SPDY_Session whose socket will be used
73 * @return SPDY_NO if some funcs fail. SPDY_YES otherwise
74 */
75int
76SPDYF_raw_new_session(struct SPDY_Session *session);
77
78
79/**
80 * Should be called
81 * closing session's socket.
82 *
83 * @param session SPDY_Session whose socket is used
84 */
85void
86SPDYF_raw_close_session(struct SPDY_Session *session);
87
88
89/**
90 * Reading from socket. Reads available data and put it to the
91 * buffer.
92 *
93 * @param session for which data is received
94 * @param buffer where data from the socket will be written to
95 * @param size of the buffer
96 * @return number of bytes (at most size) read from the connection
97 * 0 if the other party has closed the connection
98 * SPDY_IO_ERROR code on error
99 */
100int
101SPDYF_raw_recv(struct SPDY_Session *session,
102 void * buffer,
103 size_t size);
104
105
106/**
107 * Writing to socket. Writes the data given into the buffer to the
108 * socket.
109 *
110 * @param session whose context is used
111 * @param buffer from where data will be written to the socket
112 * @param size number of bytes to be taken from the buffer
113 * @return number of bytes (at most size) from the buffer that has been
114 * written to the connection
115 * 0 if the other party has closed the connection
116 * SPDY_IO_ERROR code on error
117 */
118int
119SPDYF_raw_send(struct SPDY_Session *session,
120 const void * buffer,
121 size_t size);
122
123
124/**
125 * Checks if there is data staying in the buffers of the underlying
126 * system that waits to be read. Always returns SPDY_NO, as we do not
127 * use a subsystem here.
128 *
129 * @param session which is checked
130 * @return SPDY_YES if data is pending or SPDY_NO otherwise
131 */
132int
133SPDYF_raw_is_pending(struct SPDY_Session *session);
134
135
136/**
137 * Sets TCP_CORK.
138 *
139 * @param session
140 * @return SPDY_NO if writing must not happen in the call;
141 * SPDY_YES otherwise
142 */
143int
144SPDYF_raw_before_write(struct SPDY_Session *session);
145
146
147/**
148 * Unsets TCP_CORK.
149 *
150 * @param session
151 * @param was_written has the same value as the write function for the
152 * session will return
153 * @return returned value will be used by the write function to return
154 */
155int
156SPDYF_raw_after_write(struct SPDY_Session *session, int was_written);
157
158#endif
diff --git a/src/microspdy/session.c b/src/microspdy/session.c
deleted file mode 100644
index 7cb8c3d1..00000000
--- a/src/microspdy/session.c
+++ /dev/null
@@ -1,1769 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2012 Andrey Uzunov
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file 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 "stream.h"
32#include "io.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 unsigned int 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 !=
107 SPDYF_zlib_inflate(&session->zlib_recv_stream,
108 session->read_buffer + session->read_buffer_beginning,
109 compressed_data_size,
110 &name_value_strm,
111 &name_value_strm_size)) )
112 {
113 /* something went wrong on inflating,
114 * the state of the stream for decompression is unknown
115 * and we may not be able to read anything more received on
116 * this session,
117 * so it is better to close the session */
118 free(name_value_strm);
119 free(frame);
120
121 /* mark the session for closing and close it, when
122 * everything on the output queue is already written */
123 session->status = SPDY_SESSION_STATUS_FLUSHING;
124
125 SPDYF_prepare_goaway(session, SPDY_GOAWAY_STATUS_INTERNAL_ERROR, false);
126
127 return;
128 }
129
130 if(0 == name_value_strm_size || 0 == compressed_data_size)
131 {
132 //Protocol error: send RST_STREAM
133 if(SPDY_YES != SPDYF_prepare_rst_stream(session, session->streams_head,
134 SPDY_RST_STREAM_STATUS_PROTOCOL_ERROR))
135 {
136 //no memory, try later to send RST
137 free(name_value_strm);
138 return;
139 }
140 }
141 else
142 {
143 ret = SPDYF_name_value_from_stream(name_value_strm, name_value_strm_size, &headers);
144 if(SPDY_NO == ret)
145 {
146 //memory error, try later
147 free(name_value_strm);
148 return;
149 }
150
151 session->streams_head->headers = headers;
152 //inform the application layer for the new stream received
153 if(SPDY_YES != session->daemon->fnew_stream_cb(session->daemon->fcls, session->streams_head))
154 {
155 //memory error, try later
156 free(name_value_strm);
157 return;
158 }
159
160 session->read_buffer_beginning += compressed_data_size;
161 }
162
163 //SPDYF_DEBUG("syn_stream received: id %i", session->current_stream_id);
164
165 //change state to wait for new frame
166 free(name_value_strm);
167 session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER;
168 free(frame);
169}
170
171
172/**
173 * Handler for reading the GOAWAY frame after we know that
174 * the frame is such.
175 * The function waits for the full frame and then changes status
176 * of the session.
177 *
178 * @param session SPDY_Session whose read buffer is used.
179 */
180static void
181spdyf_handler_read_goaway (struct SPDY_Session *session)
182{
183 struct SPDYF_Control_Frame *frame;
184 uint32_t last_good_stream_id;
185 uint32_t status_int;
186 enum SPDY_GOAWAY_STATUS status;
187
188 SPDYF_ASSERT(SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER == session->status,
189 "the function is called wrong");
190
191 frame = (struct SPDYF_Control_Frame *)session->frame_handler_cls;
192
193 if(frame->length > SPDY_MAX_SUPPORTED_FRAME_SIZE)
194 {
195 //this is a protocol error/attack
196 session->status = SPDY_SESSION_STATUS_IGNORE_BYTES;
197 return;
198 }
199
200 if(0 != frame->flags || 8 != frame->length)
201 {
202 //this is a protocol error
203 SPDYF_DEBUG("wrong GOAWAY received");
204 //anyway, it will be handled
205 }
206
207 if((session->read_buffer_offset - session->read_buffer_beginning) < frame->length)
208 {
209 //not all fields are received
210 //try later
211 return;
212 }
213
214 //mark that the session is almost closed
215 session->is_goaway_received = true;
216
217 if(8 == frame->length)
218 {
219 memcpy(&last_good_stream_id, session->read_buffer + session->read_buffer_beginning, 4);
220 last_good_stream_id = NTOH31(last_good_stream_id);
221 session->read_buffer_beginning += 4;
222
223 memcpy(&status_int, session->read_buffer + session->read_buffer_beginning, 4);
224 status = ntohl(status_int);
225 session->read_buffer_beginning += 4;
226
227 //TODO do something with last_good
228
229 //SPDYF_DEBUG("Received GOAWAY; status=%i; lastgood=%i",status,last_good_stream_id);
230
231 //do something according to the status
232 //TODO
233 switch(status)
234 {
235 case SPDY_GOAWAY_STATUS_OK:
236 break;
237 case SPDY_GOAWAY_STATUS_PROTOCOL_ERROR:
238 break;
239 case SPDY_GOAWAY_STATUS_INTERNAL_ERROR:
240 break;
241 }
242
243 //SPDYF_DEBUG("goaway received: status %i", status);
244 }
245
246 session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER;
247 free(frame);
248}
249
250
251/**
252 * Handler for reading RST_STREAM frames. After receiving the frame
253 * the stream moves into closed state and status
254 * of the session is changed. Frames, belonging to this stream, which
255 * are still at the output queue, will be ignored later.
256 *
257 * @param session SPDY_Session whose read buffer is used.
258 */
259static void
260spdyf_handler_read_rst_stream (struct SPDY_Session *session)
261{
262 struct SPDYF_Control_Frame *frame;
263 uint32_t stream_id;
264 int32_t status_int;
265 //enum SPDY_RST_STREAM_STATUS status; //for debug
266 struct SPDYF_Stream *stream;
267
268 SPDYF_ASSERT(SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER == session->status,
269 "the function is called wrong");
270
271 frame = (struct SPDYF_Control_Frame *)session->frame_handler_cls;
272
273 if(0 != frame->flags || 8 != frame->length)
274 {
275 //this is a protocol error
276 SPDYF_DEBUG("wrong RST_STREAM received");
277 //ignore as a large frame
278 session->status = SPDY_SESSION_STATUS_IGNORE_BYTES;
279 return;
280 }
281
282 if((session->read_buffer_offset - session->read_buffer_beginning) < frame->length)
283 {
284 //not all fields are received
285 //try later
286 return;
287 }
288
289 memcpy(&stream_id, session->read_buffer + session->read_buffer_beginning, 4);
290 stream_id = NTOH31(stream_id);
291 session->read_buffer_beginning += 4;
292
293 memcpy(&status_int, session->read_buffer + session->read_buffer_beginning, 4);
294 //status = ntohl(status_int); //for debug
295 session->read_buffer_beginning += 4;
296
297 session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER;
298 free(frame);
299
300 //mark the stream as closed
301 stream = session->streams_head;
302 while(NULL != stream)
303 {
304 if(stream_id == stream->stream_id)
305 {
306 stream->is_in_closed = true;
307 stream->is_out_closed = true;
308 break;
309 }
310 stream = stream->next;
311 }
312
313 //SPDYF_DEBUG("Received RST_STREAM; status=%i; id=%i",status,stream_id);
314
315 //do something according to the status
316 //TODO
317 /*switch(status)
318 {
319 case SPDY_RST_STREAM_STATUS_PROTOCOL_ERROR:
320 break;
321 }*/
322}
323
324
325/**
326 * Handler for reading DATA frames. In requests they are used for POST
327 * arguments.
328 *
329 * @param session SPDY_Session whose read buffer is used.
330 */
331static void
332spdyf_handler_read_data (struct SPDY_Session *session)
333{
334 int ret;
335 struct SPDYF_Data_Frame * frame;
336 struct SPDYF_Stream * stream;
337
338 SPDYF_ASSERT(SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER == session->status
339 || SPDY_SESSION_STATUS_WAIT_FOR_BODY == session->status,
340 "the function is called wrong");
341
342 //SPDYF_DEBUG("DATA frame received (POST?). Ignoring");
343
344 //SPDYF_SIGINT("");
345
346 frame = (struct SPDYF_Data_Frame *)session->frame_handler_cls;
347
348 //handle subheaders
349 if(SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER == session->status)
350 {
351 if(frame->length > SPDY_MAX_SUPPORTED_FRAME_SIZE)
352 {
353 session->status = SPDY_SESSION_STATUS_IGNORE_BYTES;
354 return;
355 }
356 else
357 session->status = SPDY_SESSION_STATUS_WAIT_FOR_BODY;
358 }
359
360 //handle body
361
362 if(session->read_buffer_offset - session->read_buffer_beginning
363 >= frame->length)
364 {
365 stream = SPDYF_stream_find(frame->stream_id, session);
366
367 if(NULL == stream || stream->is_in_closed || NULL == session->daemon->received_data_cb)
368 {
369 if(NULL == session->daemon->received_data_cb)
370 SPDYF_DEBUG("No callback for DATA frame set; Ignoring DATA frame!");
371
372 //TODO send error?
373
374 //TODO for now ignore frame
375 session->read_buffer_beginning += frame->length;
376 session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER;
377 free(frame);
378 return;
379 }
380
381 ret = session->daemon->freceived_data_cb(session->daemon->cls,
382 stream,
383 session->read_buffer + session->read_buffer_beginning,
384 frame->length,
385 0 == (SPDY_DATA_FLAG_FIN & frame->flags));
386
387 session->read_buffer_beginning += frame->length;
388
389 stream->window_size -= frame->length;
390
391 //TODO close in and send rst maybe
392 SPDYF_ASSERT(SPDY_YES == ret, "Cancel POST data is not yet implemented");
393
394 if(SPDY_DATA_FLAG_FIN & frame->flags)
395 {
396 stream->is_in_closed = true;
397 }
398 else if(stream->window_size < SPDYF_INITIAL_WINDOW_SIZE / 2)
399 {
400 //very simple implementation of flow control
401 //when the window's size is under the half of the initial value,
402 //increase it again up to the initial value
403
404 //prepare WINDOW_UPDATE
405 if(SPDY_YES == SPDYF_prepare_window_update(session, stream,
406 SPDYF_INITIAL_WINDOW_SIZE - stream->window_size))
407 {
408 stream->window_size = SPDYF_INITIAL_WINDOW_SIZE;
409 }
410 //else: do it later
411 }
412
413 //SPDYF_DEBUG("data received: id %i", frame->stream_id);
414
415 session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER;
416 free(frame);
417 }
418}
419
420
421int
422SPDYF_handler_write_syn_reply (struct SPDY_Session *session)
423{
424 struct SPDYF_Response_Queue *response_queue = session->response_queue_head;
425 struct SPDYF_Stream *stream = response_queue->stream;
426 struct SPDYF_Control_Frame control_frame;
427 void *compressed_headers = NULL;
428 size_t compressed_headers_size=0;
429 size_t used_data=0;
430 size_t total_size;
431 uint32_t stream_id_nbo;
432
433 SPDYF_ASSERT(NULL == session->write_buffer, "the function is called not in the correct moment");
434
435 memcpy(&control_frame, response_queue->control_frame, sizeof(control_frame));
436
437 if(SPDY_YES != SPDYF_zlib_deflate(&session->zlib_send_stream,
438 response_queue->data,
439 response_queue->data_size,
440 &used_data,
441 &compressed_headers,
442 &compressed_headers_size))
443 {
444 /* something went wrong on compressing,
445 * the state of the stream for compression is unknown
446 * and we may not be able to send anything more on
447 * this session,
448 * so it is better to close the session right now */
449 session->status = SPDY_SESSION_STATUS_CLOSING;
450
451 free(compressed_headers);
452
453 return SPDY_NO;
454 }
455
456 //TODO do we need this used_Data
457 SPDYF_ASSERT(used_data == response_queue->data_size, "not everything was used by zlib");
458
459 total_size = sizeof(struct SPDYF_Control_Frame) //SPDY header
460 + 4 // stream id as "subheader"
461 + compressed_headers_size;
462
463 if(NULL == (session->write_buffer = malloc(total_size)))
464 {
465 /* no memory
466 * since we do not save the compressed data anywhere and
467 * the sending zlib stream is already in new state, we must
468 * close the session */
469 session->status = SPDY_SESSION_STATUS_CLOSING;
470
471 free(compressed_headers);
472
473 return SPDY_NO;
474 }
475 session->write_buffer_beginning = 0;
476 session->write_buffer_offset = 0;
477 session->write_buffer_size = total_size;
478
479 control_frame.length = compressed_headers_size + 4; // compressed data + stream_id
480 SPDYF_CONTROL_FRAME_HTON(&control_frame);
481
482 //put frame headers to write buffer
483 memcpy(session->write_buffer + session->write_buffer_offset,&control_frame,sizeof(struct SPDYF_Control_Frame));
484 session->write_buffer_offset += sizeof(struct SPDYF_Control_Frame);
485
486 //put stream id to write buffer
487 stream_id_nbo = HTON31(stream->stream_id);
488 memcpy(session->write_buffer + session->write_buffer_offset, &stream_id_nbo, 4);
489 session->write_buffer_offset += 4;
490
491 //put compressed name/value pairs to write buffer
492 memcpy(session->write_buffer + session->write_buffer_offset, compressed_headers, compressed_headers_size);
493 session->write_buffer_offset += compressed_headers_size;
494
495 SPDYF_ASSERT(0 == session->write_buffer_beginning, "bug1");
496 SPDYF_ASSERT(session->write_buffer_offset == session->write_buffer_size, "bug2");
497
498 //DEBUG CODE, break compression state to see what happens
499/* SPDYF_zlib_deflate(&session->zlib_send_stream,
500 "1234567890",
501 10,
502 &used_data,
503 &compressed_headers,
504 &compressed_headers_size);
505*/
506 free(compressed_headers);
507
508 session->last_replied_to_stream_id = stream->stream_id;
509
510 //SPDYF_DEBUG("syn_reply sent: id %i", stream->stream_id);
511
512 return SPDY_YES;
513}
514
515
516int
517SPDYF_handler_write_goaway (struct SPDY_Session *session)
518{
519 struct SPDYF_Response_Queue *response_queue = session->response_queue_head;
520 struct SPDYF_Control_Frame control_frame;
521 size_t total_size;
522 int last_good_stream_id;
523
524 SPDYF_ASSERT(NULL == session->write_buffer, "the function is called not in the correct moment");
525
526 memcpy(&control_frame, response_queue->control_frame, sizeof(control_frame));
527
528 session->is_goaway_sent = true;
529
530 total_size = sizeof(struct SPDYF_Control_Frame) //SPDY header
531 + 4 // last good stream id as "subheader"
532 + 4; // status code as "subheader"
533
534 if(NULL == (session->write_buffer = malloc(total_size)))
535 {
536 return SPDY_NO;
537 }
538 session->write_buffer_beginning = 0;
539 session->write_buffer_offset = 0;
540 session->write_buffer_size = total_size;
541
542 control_frame.length = 8; // always for GOAWAY
543 SPDYF_CONTROL_FRAME_HTON(&control_frame);
544
545 //put frame headers to write buffer
546 memcpy(session->write_buffer + session->write_buffer_offset,&control_frame,sizeof(struct SPDYF_Control_Frame));
547 session->write_buffer_offset += sizeof(struct SPDYF_Control_Frame);
548
549 //put last good stream id to write buffer
550 last_good_stream_id = HTON31(session->last_replied_to_stream_id);
551 memcpy(session->write_buffer + session->write_buffer_offset, &last_good_stream_id, 4);
552 session->write_buffer_offset += 4;
553
554 //put "data" to write buffer. This is the status
555 memcpy(session->write_buffer + session->write_buffer_offset, response_queue->data, 4);
556 session->write_buffer_offset += 4;
557 //data is not freed by the destroy function so:
558 //free(response_queue->data);
559
560 //SPDYF_DEBUG("goaway sent: status %i", NTOH31(*(uint32_t*)(response_queue->data)));
561
562 SPDYF_ASSERT(0 == session->write_buffer_beginning, "bug1");
563 SPDYF_ASSERT(session->write_buffer_offset == session->write_buffer_size, "bug2");
564
565 return SPDY_YES;
566}
567
568
569int
570SPDYF_handler_write_data (struct SPDY_Session *session)
571{
572 struct SPDYF_Response_Queue *response_queue = session->response_queue_head;
573 struct SPDYF_Response_Queue *new_response_queue;
574 size_t total_size;
575 struct SPDYF_Data_Frame data_frame;
576 ssize_t ret;
577 bool more;
578
579 SPDYF_ASSERT(NULL == session->write_buffer, "the function is called not in the correct moment");
580
581 memcpy(&data_frame, response_queue->data_frame, sizeof(data_frame));
582
583 if(NULL == response_queue->response->rcb)
584 {
585 //standard response with data into the struct
586 SPDYF_ASSERT(NULL != response_queue->data, "no data for the response");
587
588 total_size = sizeof(struct SPDYF_Data_Frame) //SPDY header
589 + response_queue->data_size;
590
591 if(NULL == (session->write_buffer = malloc(total_size)))
592 {
593 return SPDY_NO;
594 }
595 session->write_buffer_beginning = 0;
596 session->write_buffer_offset = 0;
597 session->write_buffer_size = total_size;
598
599 data_frame.length = response_queue->data_size;
600 SPDYF_DATA_FRAME_HTON(&data_frame);
601
602 //put SPDY headers to the writing buffer
603 memcpy(session->write_buffer + session->write_buffer_offset,&data_frame,sizeof(struct SPDYF_Data_Frame));
604 session->write_buffer_offset += sizeof(struct SPDYF_Data_Frame);
605
606 //put data to the writing buffer
607 memcpy(session->write_buffer + session->write_buffer_offset, response_queue->data, response_queue->data_size);
608 session->write_buffer_offset += response_queue->data_size;
609 }
610 else
611 {
612 /* response with callbacks. The lib will produce more than 1
613 * data frames
614 */
615
616 total_size = sizeof(struct SPDYF_Data_Frame) //SPDY header
617 + SPDY_MAX_SUPPORTED_FRAME_SIZE; //max possible size
618
619 if(NULL == (session->write_buffer = malloc(total_size)))
620 {
621 return SPDY_NO;
622 }
623 session->write_buffer_beginning = 0;
624 session->write_buffer_offset = 0;
625 session->write_buffer_size = total_size;
626
627 ret = response_queue->response->rcb(response_queue->response->rcb_cls,
628 session->write_buffer + sizeof(struct SPDYF_Data_Frame),
629 response_queue->response->rcb_block_size,
630 &more);
631
632 if(ret < 0 || ret > response_queue->response->rcb_block_size)
633 {
634 free(session->write_buffer);
635 session->write_buffer = NULL;
636
637 //send RST_STREAM
638 if(SPDY_YES == (ret = SPDYF_prepare_rst_stream(session,
639 response_queue->stream,
640 SPDY_RST_STREAM_STATUS_INTERNAL_ERROR)))
641 {
642 return SPDY_NO;
643 }
644
645 //else no memory
646 //for now close session
647 //TODO what?
648 session->status = SPDY_SESSION_STATUS_CLOSING;
649
650 return SPDY_NO;
651 }
652 if(0 == ret && more)
653 {
654 //the app couldn't write anything to buf but later will
655 free(session->write_buffer);
656 session->write_buffer = NULL;
657 session->write_buffer_size = 0;
658
659 if(NULL != response_queue->next)
660 {
661 //put the frame at the end of the queue
662 //otherwise - head of line blocking
663 session->response_queue_head = response_queue->next;
664 session->response_queue_head->prev = NULL;
665 session->response_queue_tail->next = response_queue;
666 response_queue->prev = session->response_queue_tail;
667 response_queue->next = NULL;
668 session->response_queue_tail = response_queue;
669 }
670
671 return SPDY_YES;
672 }
673
674 if(more)
675 {
676 //create another response queue object to call the user cb again
677 if(NULL == (new_response_queue = SPDYF_response_queue_create(true,
678 NULL,
679 0,
680 response_queue->response,
681 response_queue->stream,
682 false,
683 response_queue->frqcb,
684 response_queue->frqcb_cls,
685 response_queue->rrcb,
686 response_queue->rrcb_cls)))
687 {
688 //TODO send RST_STREAM
689 //for now close session
690 session->status = SPDY_SESSION_STATUS_CLOSING;
691
692 free(session->write_buffer);
693 session->write_buffer = NULL;
694 return SPDY_NO;
695 }
696
697 //put it at second position on the queue
698 new_response_queue->prev = response_queue;
699 new_response_queue->next = response_queue->next;
700 if(NULL == response_queue->next)
701 {
702 session->response_queue_tail = new_response_queue;
703 }
704 else
705 {
706 response_queue->next->prev = new_response_queue;
707 }
708 response_queue->next = new_response_queue;
709
710 response_queue->frqcb = NULL;
711 response_queue->frqcb_cls = NULL;
712 response_queue->rrcb = NULL;
713 response_queue->rrcb_cls = NULL;
714 }
715 else
716 {
717 data_frame.flags |= SPDY_DATA_FLAG_FIN;
718 }
719
720 data_frame.length = ret;
721 SPDYF_DATA_FRAME_HTON(&data_frame);
722
723 //put SPDY headers to the writing buffer
724 memcpy(session->write_buffer + session->write_buffer_offset,
725 &data_frame,
726 sizeof(struct SPDYF_Data_Frame));
727 session->write_buffer_offset += sizeof(struct SPDYF_Data_Frame);
728 session->write_buffer_offset += ret;
729 session->write_buffer_size = session->write_buffer_offset;
730 }
731
732 //SPDYF_DEBUG("data sent: id %i", NTOH31(data_frame.stream_id));
733
734 SPDYF_ASSERT(0 == session->write_buffer_beginning, "bug1");
735 SPDYF_ASSERT(session->write_buffer_offset == session->write_buffer_size, "bug2");
736
737 return SPDY_YES;
738}
739
740
741int
742SPDYF_handler_write_rst_stream (struct SPDY_Session *session)
743{
744 struct SPDYF_Response_Queue *response_queue = session->response_queue_head;
745 struct SPDYF_Control_Frame control_frame;
746 size_t total_size;
747
748 SPDYF_ASSERT(NULL == session->write_buffer, "the function is called not in the correct moment");
749
750 memcpy(&control_frame, response_queue->control_frame, sizeof(control_frame));
751
752 total_size = sizeof(struct SPDYF_Control_Frame) //SPDY header
753 + 4 // stream id as "subheader"
754 + 4; // status code as "subheader"
755
756 if(NULL == (session->write_buffer = malloc(total_size)))
757 {
758 return SPDY_NO;
759 }
760 session->write_buffer_beginning = 0;
761 session->write_buffer_offset = 0;
762 session->write_buffer_size = total_size;
763
764 control_frame.length = 8; // always for RST_STREAM
765 SPDYF_CONTROL_FRAME_HTON(&control_frame);
766
767 //put frame headers to write buffer
768 memcpy(session->write_buffer + session->write_buffer_offset,&control_frame,sizeof(struct SPDYF_Control_Frame));
769 session->write_buffer_offset += sizeof(struct SPDYF_Control_Frame);
770
771 //put stream id to write buffer. This is the status
772 memcpy(session->write_buffer + session->write_buffer_offset, response_queue->data, 8);
773 session->write_buffer_offset += 8;
774 //data is not freed by the destroy function so:
775 //free(response_queue->data);
776
777 //SPDYF_DEBUG("rst_stream sent: id %i", NTOH31((((uint64_t)response_queue->data) & 0xFFFF0000) >> 32));
778
779 SPDYF_ASSERT(0 == session->write_buffer_beginning, "bug1");
780 SPDYF_ASSERT(session->write_buffer_offset == session->write_buffer_size, "bug2");
781
782 return SPDY_YES;
783}
784
785
786int
787SPDYF_handler_write_window_update (struct SPDY_Session *session)
788{
789 struct SPDYF_Response_Queue *response_queue = session->response_queue_head;
790 struct SPDYF_Control_Frame control_frame;
791 size_t total_size;
792
793 SPDYF_ASSERT(NULL == session->write_buffer, "the function is called not in the correct moment");
794
795 memcpy(&control_frame, response_queue->control_frame, sizeof(control_frame));
796
797 total_size = sizeof(struct SPDYF_Control_Frame) //SPDY header
798 + 4 // stream id as "subheader"
799 + 4; // delta-window-size as "subheader"
800
801 if(NULL == (session->write_buffer = malloc(total_size)))
802 {
803 return SPDY_NO;
804 }
805 session->write_buffer_beginning = 0;
806 session->write_buffer_offset = 0;
807 session->write_buffer_size = total_size;
808
809 control_frame.length = 8; // always for WINDOW_UPDATE
810 SPDYF_CONTROL_FRAME_HTON(&control_frame);
811
812 //put frame headers to write buffer
813 memcpy(session->write_buffer + session->write_buffer_offset,&control_frame,sizeof(struct SPDYF_Control_Frame));
814 session->write_buffer_offset += sizeof(struct SPDYF_Control_Frame);
815
816 //put stream id and delta-window-size to write buffer
817 memcpy(session->write_buffer + session->write_buffer_offset, response_queue->data, 8);
818 session->write_buffer_offset += 8;
819
820 //SPDYF_DEBUG("window_update sent: id %i", NTOH31((((uint64_t)response_queue->data) & 0xFFFF0000) >> 32));
821
822 SPDYF_ASSERT(0 == session->write_buffer_beginning, "bug1");
823 SPDYF_ASSERT(session->write_buffer_offset == session->write_buffer_size, "bug2");
824
825 return SPDY_YES;
826}
827
828
829void
830SPDYF_handler_ignore_frame (struct SPDY_Session *session)
831{
832 struct SPDYF_Control_Frame *frame;
833
834 SPDYF_ASSERT(SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER == session->status
835 || SPDY_SESSION_STATUS_WAIT_FOR_BODY == session->status,
836 "the function is called wrong");
837
838
839 frame = (struct SPDYF_Control_Frame *)session->frame_handler_cls;
840
841 //handle subheaders
842 if(SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER == session->status)
843 {
844 if(frame->length > SPDY_MAX_SUPPORTED_FRAME_SIZE)
845 {
846 session->status = SPDY_SESSION_STATUS_IGNORE_BYTES;
847 return;
848 }
849 else
850 session->status = SPDY_SESSION_STATUS_WAIT_FOR_BODY;
851 }
852
853 //handle body
854
855 if(session->read_buffer_offset - session->read_buffer_beginning
856 >= frame->length)
857 {
858 session->read_buffer_beginning += frame->length;
859 session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER;
860 free(frame);
861 }
862}
863
864
865int
866SPDYF_session_read (struct SPDY_Session *session)
867{
868 int bytes_read;
869 bool reallocate;
870 size_t actual_buf_size;
871
872 if(SPDY_SESSION_STATUS_CLOSING == session->status
873 || SPDY_SESSION_STATUS_FLUSHING == session->status)
874 return SPDY_NO;
875
876 //if the read buffer is full to the end, we need to reallocate space
877 if (session->read_buffer_size == session->read_buffer_offset)
878 {
879 //but only if the state of the session requires it
880 //i.e. no further proceeding is possible without reallocation
881 reallocate = false;
882 actual_buf_size = session->read_buffer_offset
883 - session->read_buffer_beginning;
884 switch(session->status)
885 {
886 case SPDY_SESSION_STATUS_WAIT_FOR_HEADER:
887
888 case SPDY_SESSION_STATUS_IGNORE_BYTES:
889 //we need space for a whole control frame header
890 if(actual_buf_size < sizeof(struct SPDYF_Control_Frame))
891 reallocate = true;
892 break;
893
894 case SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER:
895
896 case SPDY_SESSION_STATUS_WAIT_FOR_BODY:
897 //we need as many bytes as set in length field of the
898 //header
899 SPDYF_ASSERT(NULL != session->frame_handler_cls,
900 "no frame for session");
901 if(session->frame_handler != &spdyf_handler_read_data)
902 {
903 if(actual_buf_size
904 < ((struct SPDYF_Control_Frame *)session->frame_handler_cls)->length)
905 reallocate = true;
906 }
907 else
908 {
909 if(actual_buf_size
910 < ((struct SPDYF_Data_Frame *)session->frame_handler_cls)->length)
911 reallocate = true;
912 }
913 break;
914
915 case SPDY_SESSION_STATUS_CLOSING:
916 case SPDY_SESSION_STATUS_FLUSHING:
917 //nothing needed
918 break;
919 }
920
921 if(reallocate)
922 {
923 //reuse the space in the buffer that was already read by the lib
924 memmove(session->read_buffer,
925 session->read_buffer + session->read_buffer_beginning,
926 session->read_buffer_offset - session->read_buffer_beginning);
927
928 session->read_buffer_offset -= session->read_buffer_beginning;
929 session->read_buffer_beginning = 0;
930 }
931 else
932 {
933 //will read next time
934 //TODO optimize it, memmove more often?
935 return SPDY_NO;
936 }
937 }
938
939 session->last_activity = SPDYF_monotonic_time();
940
941 //actual read from the TLS socket
942 bytes_read = session->fio_recv(session,
943 session->read_buffer + session->read_buffer_offset,
944 session->read_buffer_size - session->read_buffer_offset);
945
946 switch(bytes_read)
947 {
948 case SPDY_IO_ERROR_CLOSED:
949 //The TLS connection was closed by the other party, clean
950 //or not
951 shutdown (session->socket_fd, SHUT_RD);
952 session->read_closed = true;
953 session->status = SPDY_SESSION_STATUS_CLOSING;
954 return SPDY_YES;
955
956 case SPDY_IO_ERROR_ERROR:
957 //any kind of error in the TLS subsystem
958 //try to prepare GOAWAY frame
959 SPDYF_prepare_goaway(session, SPDY_GOAWAY_STATUS_INTERNAL_ERROR, false);
960 //try to flush the queue when write is called
961 session->status = SPDY_SESSION_STATUS_FLUSHING;
962 return SPDY_YES;
963
964 case SPDY_IO_ERROR_AGAIN:
965 //read or write should be called again; leave it for the
966 //next time
967 return SPDY_NO;
968
969 //default:
970 //something was really read from the TLS subsystem
971 //just continue
972 }
973
974 session->read_buffer_offset += bytes_read;
975
976 return SPDY_YES;
977}
978
979
980int
981SPDYF_session_write (struct SPDY_Session *session,
982 bool only_one_frame)
983{
984 unsigned int i;
985 int bytes_written;
986 struct SPDYF_Response_Queue *queue_head;
987 struct SPDYF_Response_Queue *response_queue;
988
989 if(SPDY_SESSION_STATUS_CLOSING == session->status)
990 return SPDY_NO;
991
992 if(SPDY_NO == session->fio_before_write(session))
993 return SPDY_NO;
994
995 for(i=0;
996 only_one_frame
997 ? i < 1
998 : i < session->max_num_frames;
999 ++i)
1000 {
1001 //if the buffer is not null, part of the last frame is still
1002 //pending to be sent
1003 if(NULL == session->write_buffer)
1004 {
1005 //discard frames on closed streams
1006 response_queue = session->response_queue_head;
1007
1008 while(NULL != response_queue)
1009 {
1010 //if stream is closed, remove not yet sent frames
1011 //associated with it
1012 //GOAWAY frames are not associated to streams
1013 //and still need to be sent
1014 if(NULL == response_queue->stream
1015 || !response_queue->stream->is_out_closed)
1016 break;
1017
1018 DLL_remove(session->response_queue_head,session->response_queue_tail,response_queue);
1019
1020 if(NULL != response_queue->frqcb)
1021 {
1022 response_queue->frqcb(response_queue->frqcb_cls, response_queue, SPDY_RESPONSE_RESULT_STREAM_CLOSED);
1023 }
1024
1025 SPDYF_response_queue_destroy(response_queue);
1026 response_queue = session->response_queue_head;
1027 }
1028
1029 if(NULL == session->response_queue_head)
1030 break;//nothing on the queue
1031
1032 //get next data from queue and put it to the write buffer
1033 // to send it
1034 if(SPDY_NO == session->response_queue_head->process_response_handler(session))
1035 {
1036 //error occured and the handler changed or not the
1037 //session's status appropriately
1038 if(SPDY_SESSION_STATUS_CLOSING == session->status)
1039 {
1040 //try to send GOAWAY first if the current frame is different
1041 if(session->response_queue_head->is_data
1042 || SPDY_CONTROL_FRAME_TYPES_GOAWAY
1043 != session->response_queue_head->control_frame->type)
1044 {
1045 session->status = SPDY_SESSION_STATUS_FLUSHING;
1046 SPDYF_prepare_goaway(session, SPDY_GOAWAY_STATUS_INTERNAL_ERROR, true);
1047 SPDYF_session_write(session,true);
1048 session->status = SPDY_SESSION_STATUS_CLOSING;
1049 }
1050 return SPDY_YES;
1051 }
1052
1053 //just return from the loop to return from this function
1054 ++i;
1055 break;
1056 }
1057
1058 //check if something was prepared for writing
1059 //on respones with callbacks it is possible that their is no
1060 //data available
1061 if(0 == session->write_buffer_size)//nothing to write
1062 {
1063 if(response_queue != session->response_queue_head)
1064 {
1065 //the handler modified the queue
1066 continue;
1067 }
1068 else
1069 {
1070 //no need to try the same frame again
1071 ++i;
1072 break;
1073 }
1074 }
1075 }
1076
1077 session->last_activity = SPDYF_monotonic_time();
1078
1079 //actual write to the IO
1080 bytes_written = session->fio_send(session,
1081 session->write_buffer + session->write_buffer_beginning,
1082 session->write_buffer_offset - session->write_buffer_beginning);
1083
1084 switch(bytes_written)
1085 {
1086 case SPDY_IO_ERROR_CLOSED:
1087 //The TLS connection was closed by the other party, clean
1088 //or not
1089 shutdown (session->socket_fd, SHUT_RD);
1090 session->read_closed = true;
1091 session->status = SPDY_SESSION_STATUS_CLOSING;
1092 return SPDY_YES;
1093
1094 case SPDY_IO_ERROR_ERROR:
1095 //any kind of error in the TLS subsystem
1096 //forbid more writing
1097 session->status = SPDY_SESSION_STATUS_CLOSING;
1098 return SPDY_YES;
1099
1100 case SPDY_IO_ERROR_AGAIN:
1101 //read or write should be called again; leave it for the
1102 //next time; return from the function as we do not now
1103 //whether reading or writing is needed
1104 return i>0 ? SPDY_YES : SPDY_NO;
1105
1106 //default:
1107 //something was really read from the TLS subsystem
1108 //just continue
1109 }
1110
1111 session->write_buffer_beginning += bytes_written;
1112
1113 //check if the full buffer was written
1114 if(session->write_buffer_beginning == session->write_buffer_size)
1115 {
1116 //that response is handled, remove it from queue
1117 free(session->write_buffer);
1118 session->write_buffer = NULL;
1119 session->write_buffer_size = 0;
1120 queue_head = session->response_queue_head;
1121 if(NULL == queue_head->next)
1122 {
1123 session->response_queue_head = NULL;
1124 session->response_queue_tail = NULL;
1125 }
1126 else
1127 {
1128 session->response_queue_head = queue_head->next;
1129 session->response_queue_head->prev = NULL;
1130 }
1131
1132 //set stream to closed if the frame's fin flag is set
1133 SPDYF_stream_set_flags_on_write(queue_head);
1134
1135 if(NULL != queue_head->frqcb)
1136 {
1137 //application layer callback to notify sending of the response
1138 queue_head->frqcb(queue_head->frqcb_cls, queue_head, SPDY_RESPONSE_RESULT_SUCCESS);
1139 }
1140
1141 SPDYF_response_queue_destroy(queue_head);
1142 }
1143 }
1144
1145 if(SPDY_SESSION_STATUS_FLUSHING == session->status
1146 && NULL == session->response_queue_head)
1147 session->status = SPDY_SESSION_STATUS_CLOSING;
1148
1149 //return i>0 ? SPDY_YES : SPDY_NO;
1150 return session->fio_after_write(session, i>0 ? SPDY_YES : SPDY_NO);
1151}
1152
1153
1154int
1155SPDYF_session_idle (struct SPDY_Session *session)
1156{
1157 size_t read_buffer_beginning;
1158 size_t frame_length;
1159 struct SPDYF_Control_Frame* control_frame;
1160 struct SPDYF_Data_Frame *data_frame;
1161
1162 //prepare session for closing if timeout is used and already passed
1163 if(SPDY_SESSION_STATUS_CLOSING != session->status
1164 && session->daemon->session_timeout
1165 && (session->last_activity + session->daemon->session_timeout < SPDYF_monotonic_time()))
1166 {
1167 session->status = SPDY_SESSION_STATUS_CLOSING;
1168 //best effort for sending GOAWAY
1169 SPDYF_prepare_goaway(session, SPDY_GOAWAY_STATUS_OK, true);
1170 SPDYF_session_write(session,true);
1171 }
1172
1173 switch(session->status)
1174 {
1175 //expect new frame to arrive
1176 case SPDY_SESSION_STATUS_WAIT_FOR_HEADER:
1177 session->current_stream_id = 0;
1178 //check if the whole frame header is already here
1179 //both frame types have the same length
1180 if(session->read_buffer_offset - session->read_buffer_beginning
1181 < sizeof(struct SPDYF_Control_Frame))
1182 return SPDY_NO;
1183
1184 /* check the first bit to see if it is data or control frame
1185 * and also if the version is supported */
1186 if(0x80 == *(uint8_t *)(session->read_buffer + session->read_buffer_beginning)
1187 && SPDY_VERSION == *((uint8_t *)session->read_buffer + session->read_buffer_beginning + 1))
1188 {
1189 //control frame
1190 if(NULL == (control_frame = malloc(sizeof(struct SPDYF_Control_Frame))))
1191 {
1192 SPDYF_DEBUG("No memory");
1193 return SPDY_NO;
1194 }
1195
1196 //get frame headers
1197 memcpy(control_frame,
1198 session->read_buffer + session->read_buffer_beginning,
1199 sizeof(struct SPDYF_Control_Frame));
1200 session->read_buffer_beginning += sizeof(struct SPDYF_Control_Frame);
1201 SPDYF_CONTROL_FRAME_NTOH(control_frame);
1202
1203 session->status = SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER;
1204 //assign different frame handler according to frame type
1205 switch(control_frame->type){
1206 case SPDY_CONTROL_FRAME_TYPES_SYN_STREAM:
1207 session->frame_handler = &spdyf_handler_read_syn_stream;
1208 break;
1209 case SPDY_CONTROL_FRAME_TYPES_GOAWAY:
1210 session->frame_handler = &spdyf_handler_read_goaway;
1211 break;
1212 case SPDY_CONTROL_FRAME_TYPES_RST_STREAM:
1213 session->frame_handler = &spdyf_handler_read_rst_stream;
1214 break;
1215 default:
1216 session->frame_handler = &SPDYF_handler_ignore_frame;
1217 }
1218 session->frame_handler_cls = control_frame;
1219 //DO NOT break the outer case
1220 }
1221 else if(0 == *(uint8_t *)(session->read_buffer + session->read_buffer_beginning))
1222 {
1223 //needed for POST
1224 //data frame
1225 if(NULL == (data_frame = malloc(sizeof(struct SPDYF_Data_Frame))))
1226 {
1227 SPDYF_DEBUG("No memory");
1228 return SPDY_NO;
1229 }
1230
1231 //get frame headers
1232 memcpy(data_frame,
1233 session->read_buffer + session->read_buffer_beginning,
1234 sizeof(struct SPDYF_Data_Frame));
1235 session->read_buffer_beginning += sizeof(struct SPDYF_Data_Frame);
1236 SPDYF_DATA_FRAME_NTOH(data_frame);
1237
1238 session->status = SPDY_SESSION_STATUS_WAIT_FOR_BODY;
1239 session->frame_handler = &spdyf_handler_read_data;
1240 session->frame_handler_cls = data_frame;
1241 //DO NOT brake the outer case
1242 }
1243 else
1244 {
1245 SPDYF_DEBUG("another protocol or version received!");
1246
1247 /* According to the draft the lib should send here
1248 * RST_STREAM with status UNSUPPORTED_VERSION. I don't
1249 * see any sense of keeping the session open since
1250 * we don't know how many bytes is the bogus "frame".
1251 * And the latter normally will be HTTP request.
1252 *
1253 */
1254
1255 //shutdown(session->socket_fd, SHUT_RD);
1256 session->status = SPDY_SESSION_STATUS_FLUSHING;
1257 SPDYF_prepare_goaway(session, SPDY_GOAWAY_STATUS_PROTOCOL_ERROR,false);
1258 //SPDYF_session_write(session,false);
1259 /* close connection since the client expects another
1260 protocol from us */
1261 //SPDYF_session_close(session);
1262 return SPDY_YES;
1263 }
1264
1265 //expect specific header fields after the standard header
1266 case SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER:
1267 if(NULL!=session->frame_handler)
1268 {
1269 read_buffer_beginning = session->read_buffer_beginning;
1270 //if everything is ok, the "body" will also be processed
1271 //by the handler
1272 session->frame_handler(session);
1273
1274 if(SPDY_SESSION_STATUS_IGNORE_BYTES == session->status)
1275 {
1276 //check for larger than max supported frame
1277 if(session->frame_handler != &spdyf_handler_read_data)
1278 {
1279 frame_length = ((struct SPDYF_Control_Frame *)session->frame_handler_cls)->length;
1280 }
1281 else
1282 {
1283 frame_length = ((struct SPDYF_Data_Frame *)session->frame_handler_cls)->length;
1284 }
1285
1286 //if(SPDY_MAX_SUPPORTED_FRAME_SIZE < frame_length)
1287 {
1288 SPDYF_DEBUG("received frame with unsupported size: %zu", frame_length);
1289 //the data being received must be ignored and
1290 //RST_STREAM sent
1291
1292 //ignore bytes that will arive later
1293 session->read_ignore_bytes = frame_length
1294 + read_buffer_beginning
1295 - session->read_buffer_offset;
1296 //ignore what is already in read buffer
1297 session->read_buffer_beginning = session->read_buffer_offset;
1298
1299 SPDYF_prepare_rst_stream(session,
1300 session->current_stream_id > 0 ? session->streams_head : NULL, //may be 0 here which is not good
1301 SPDY_RST_STREAM_STATUS_FRAME_TOO_LARGE);
1302
1303 //actually the read buffer can be bigger than the
1304 //max supported size
1305 session->status = session->read_ignore_bytes
1306 ? SPDY_SESSION_STATUS_IGNORE_BYTES
1307 : SPDY_SESSION_STATUS_WAIT_FOR_HEADER;
1308
1309 free(session->frame_handler_cls);
1310 }
1311 }
1312 }
1313
1314 if(SPDY_SESSION_STATUS_IGNORE_BYTES != session->status)
1315 {
1316 break;
1317 }
1318
1319 //ignoring data in read buffer
1320 case SPDY_SESSION_STATUS_IGNORE_BYTES:
1321 SPDYF_ASSERT(session->read_ignore_bytes > 0,
1322 "Session is in wrong state");
1323 if(session->read_ignore_bytes
1324 > session->read_buffer_offset - session->read_buffer_beginning)
1325 {
1326 session->read_ignore_bytes -=
1327 session->read_buffer_offset - session->read_buffer_beginning;
1328 session->read_buffer_beginning = session->read_buffer_offset;
1329 }
1330 else
1331 {
1332 session->read_buffer_beginning += session->read_ignore_bytes;
1333 session->read_ignore_bytes = 0;
1334 session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER;
1335 }
1336 break;
1337
1338 //expect frame body (name/value pairs)
1339 case SPDY_SESSION_STATUS_WAIT_FOR_BODY:
1340 if(NULL!=session->frame_handler)
1341 session->frame_handler(session);
1342 break;
1343
1344 case SPDY_SESSION_STATUS_FLUSHING:
1345
1346 return SPDY_NO;
1347
1348 //because of error the session needs to be closed
1349 case SPDY_SESSION_STATUS_CLOSING:
1350 //error should be already sent to the client
1351 SPDYF_session_close(session);
1352 return SPDY_YES;
1353 }
1354
1355 return SPDY_YES;
1356}
1357
1358
1359void
1360SPDYF_session_close (struct SPDY_Session *session)
1361{
1362 struct SPDY_Daemon *daemon = session->daemon;
1363 int by_client = session->read_closed ? SPDY_YES : SPDY_NO;
1364
1365 //shutdown the tls and deinit the tls context
1366 session->fio_close_session(session);
1367 shutdown (session->socket_fd,
1368 session->read_closed ? SHUT_WR : SHUT_RDWR);
1369 session->read_closed = true;
1370
1371 //remove session from the list
1372 DLL_remove (daemon->sessions_head,
1373 daemon->sessions_tail,
1374 session);
1375 //add the session for the list for cleaning up
1376 DLL_insert (daemon->cleanup_head,
1377 daemon->cleanup_tail,
1378 session);
1379
1380 //call callback for closed session
1381 if(NULL != daemon->session_closed_cb)
1382 {
1383 daemon->session_closed_cb(daemon->cls, session, by_client);
1384 }
1385}
1386
1387
1388int
1389SPDYF_session_accept(struct SPDY_Daemon *daemon)
1390{
1391 int new_socket_fd;
1392 int ret;
1393 struct SPDY_Session *session = NULL;
1394 socklen_t addr_len;
1395 struct sockaddr *addr;
1396
1397#if HAVE_INET6
1398 struct sockaddr_in6 addr6;
1399
1400 addr = (struct sockaddr *)&addr6;
1401 addr_len = sizeof(addr6);
1402#else
1403 struct sockaddr_in addr4;
1404
1405 addr = (struct sockaddr *)&addr4;
1406 addr_len = sizeof(addr6);
1407#endif
1408
1409 new_socket_fd = accept (daemon->socket_fd, addr, &addr_len);
1410
1411 if(new_socket_fd < 1)
1412 return SPDY_NO;
1413
1414 if (NULL == (session = malloc (sizeof (struct SPDY_Session))))
1415 {
1416 goto free_and_fail;
1417 }
1418 memset (session, 0, sizeof (struct SPDY_Session));
1419
1420 session->daemon = daemon;
1421 session->socket_fd = new_socket_fd;
1422 session->max_num_frames = daemon->max_num_frames;
1423
1424 ret = SPDYF_io_set_session(session, daemon->io_subsystem);
1425 SPDYF_ASSERT(SPDY_YES == ret, "Somehow daemon->io_subsystem iswrong here");
1426
1427 //init TLS context, handshake will be done
1428 if(SPDY_YES != session->fio_new_session(session))
1429 {
1430 goto free_and_fail;
1431 }
1432
1433 //read buffer
1434 session->read_buffer_size = SPDYF_BUFFER_SIZE;
1435 if (NULL == (session->read_buffer = malloc (session->read_buffer_size)))
1436 {
1437 session->fio_close_session(session);
1438 goto free_and_fail;
1439 }
1440
1441 //address of the client
1442 if (NULL == (session->addr = malloc (addr_len)))
1443 {
1444 session->fio_close_session(session);
1445 goto free_and_fail;
1446 }
1447 memcpy (session->addr, addr, addr_len);
1448
1449 session->addr_len = addr_len;
1450 session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER;
1451
1452 //init zlib context for the whole session
1453 if(SPDY_YES != SPDYF_zlib_deflate_init(&session->zlib_send_stream))
1454 {
1455 session->fio_close_session(session);
1456 goto free_and_fail;
1457 }
1458 if(SPDY_YES != SPDYF_zlib_inflate_init(&session->zlib_recv_stream))
1459 {
1460 session->fio_close_session(session);
1461 SPDYF_zlib_deflate_end(&session->zlib_send_stream);
1462 goto free_and_fail;
1463 }
1464
1465 //add it to daemon's list
1466 DLL_insert(daemon->sessions_head,daemon->sessions_tail,session);
1467
1468 session->last_activity = SPDYF_monotonic_time();
1469
1470 if(NULL != daemon->new_session_cb)
1471 daemon->new_session_cb(daemon->cls, session);
1472
1473 return SPDY_YES;
1474
1475 //for GOTO
1476 free_and_fail:
1477 /* something failed, so shutdown, close and free memory */
1478 shutdown (new_socket_fd, SHUT_RDWR);
1479 (void)MHD_socket_close_ (new_socket_fd);
1480
1481 if(NULL != session)
1482 {
1483 if(NULL != session->addr)
1484 free (session->addr);
1485 if(NULL != session->read_buffer)
1486 free (session->read_buffer);
1487 free (session);
1488 }
1489 return SPDY_NO;
1490}
1491
1492
1493void
1494SPDYF_queue_response (struct SPDYF_Response_Queue *response_to_queue,
1495 struct SPDY_Session *session,
1496 int consider_priority)
1497{
1498 struct SPDYF_Response_Queue *pos;
1499 struct SPDYF_Response_Queue *last;
1500 uint8_t priority;
1501
1502 SPDYF_ASSERT(SPDY_YES != consider_priority || NULL != response_to_queue->stream,
1503 "called with consider_priority but no stream provided");
1504
1505 last = response_to_queue;
1506 while(NULL != last->next)
1507 {
1508 last = last->next;
1509 }
1510
1511 if(SPDY_NO == consider_priority)
1512 {
1513 //put it at the end of the queue
1514 response_to_queue->prev = session->response_queue_tail;
1515 if (NULL == session->response_queue_head)
1516 session->response_queue_head = response_to_queue;
1517 else
1518 session->response_queue_tail->next = response_to_queue;
1519 session->response_queue_tail = last;
1520 return;
1521 }
1522 else if(-1 == consider_priority)
1523 {
1524 //put it at the head of the queue
1525 last->next = session->response_queue_head;
1526 if (NULL == session->response_queue_tail)
1527 session->response_queue_tail = last;
1528 else
1529 session->response_queue_head->prev = response_to_queue;
1530 session->response_queue_head = response_to_queue;
1531 return;
1532 }
1533
1534 if(NULL == session->response_queue_tail)
1535 {
1536 session->response_queue_head = response_to_queue;
1537 session->response_queue_tail = last;
1538 return;
1539 }
1540
1541 //search for the right position to put it
1542 pos = session->response_queue_tail;
1543 priority = response_to_queue->stream->priority;
1544 while(NULL != pos
1545 && pos->stream->priority > priority)
1546 {
1547 pos = pos->prev;
1548 }
1549
1550 if(NULL == pos)
1551 {
1552 //put it on the head
1553 session->response_queue_head->prev = last;
1554 last->next = session->response_queue_head;
1555 session->response_queue_head = response_to_queue;
1556 }
1557 else if(NULL == pos->next)
1558 {
1559 //put it at the end
1560 response_to_queue->prev = pos;
1561 pos->next = response_to_queue;
1562 session->response_queue_tail = last;
1563 }
1564 else
1565 {
1566 response_to_queue->prev = pos;
1567 last->next = pos->next;
1568 pos->next = response_to_queue;
1569 last->next->prev = last;
1570 }
1571}
1572
1573
1574void
1575SPDYF_session_destroy(struct SPDY_Session *session)
1576{
1577 struct SPDYF_Stream *stream;
1578 struct SPDYF_Response_Queue *response_queue;
1579
1580 (void)MHD_socket_close_ (session->socket_fd);
1581 SPDYF_zlib_deflate_end(&session->zlib_send_stream);
1582 SPDYF_zlib_inflate_end(&session->zlib_recv_stream);
1583
1584 //clean up unsent data in the output queue
1585 while (NULL != (response_queue = session->response_queue_head))
1586 {
1587 DLL_remove (session->response_queue_head,
1588 session->response_queue_tail,
1589 response_queue);
1590
1591 if(NULL != response_queue->frqcb)
1592 {
1593 response_queue->frqcb(response_queue->frqcb_cls, response_queue, SPDY_RESPONSE_RESULT_SESSION_CLOSED);
1594 }
1595
1596 SPDYF_response_queue_destroy(response_queue);
1597 }
1598
1599 //clean up the streams belonging to this session
1600 while (NULL != (stream = session->streams_head))
1601 {
1602 DLL_remove (session->streams_head,
1603 session->streams_tail,
1604 stream);
1605
1606 SPDYF_stream_destroy(stream);
1607 }
1608
1609 free(session->addr);
1610 free(session->read_buffer);
1611 free(session->write_buffer);
1612 free(session);
1613}
1614
1615
1616int
1617SPDYF_prepare_goaway (struct SPDY_Session *session,
1618 enum SPDY_GOAWAY_STATUS status,
1619 bool in_front)
1620{
1621 struct SPDYF_Response_Queue *response_to_queue;
1622 struct SPDYF_Control_Frame *control_frame;
1623 uint32_t *data;
1624
1625 if(NULL == (response_to_queue = malloc(sizeof(struct SPDYF_Response_Queue))))
1626 {
1627 return SPDY_NO;
1628 }
1629 memset(response_to_queue, 0, sizeof(struct SPDYF_Response_Queue));
1630
1631 if(NULL == (control_frame = malloc(sizeof(struct SPDYF_Control_Frame))))
1632 {
1633 free(response_to_queue);
1634 return SPDY_NO;
1635 }
1636 memset(control_frame, 0, sizeof(struct SPDYF_Control_Frame));
1637
1638 if(NULL == (data = malloc(4)))
1639 {
1640 free(control_frame);
1641 free(response_to_queue);
1642 return SPDY_NO;
1643 }
1644 *(data) = htonl(status);
1645
1646 control_frame->control_bit = 1;
1647 control_frame->version = SPDY_VERSION;
1648 control_frame->type = SPDY_CONTROL_FRAME_TYPES_GOAWAY;
1649 control_frame->flags = 0;
1650
1651 response_to_queue->control_frame = control_frame;
1652 response_to_queue->process_response_handler = &SPDYF_handler_write_goaway;
1653 response_to_queue->data = data;
1654 response_to_queue->data_size = 4;
1655
1656 SPDYF_queue_response (response_to_queue,
1657 session,
1658 in_front ? -1 : SPDY_NO);
1659
1660 return SPDY_YES;
1661}
1662
1663
1664int
1665SPDYF_prepare_rst_stream (struct SPDY_Session *session,
1666 struct SPDYF_Stream * stream,
1667 enum SPDY_RST_STREAM_STATUS status)
1668{
1669 struct SPDYF_Response_Queue *response_to_queue;
1670 struct SPDYF_Control_Frame *control_frame;
1671 uint32_t *data;
1672 uint32_t stream_id;
1673
1674 if(NULL == stream)
1675 stream_id = 0;
1676 else
1677 stream_id = stream->stream_id;
1678
1679 if(NULL == (response_to_queue = malloc(sizeof(struct SPDYF_Response_Queue))))
1680 {
1681 return SPDY_NO;
1682 }
1683 memset(response_to_queue, 0, sizeof(struct SPDYF_Response_Queue));
1684
1685 if(NULL == (control_frame = malloc(sizeof(struct SPDYF_Control_Frame))))
1686 {
1687 free(response_to_queue);
1688 return SPDY_NO;
1689 }
1690 memset(control_frame, 0, sizeof(struct SPDYF_Control_Frame));
1691
1692 if(NULL == (data = malloc(8)))
1693 {
1694 free(control_frame);
1695 free(response_to_queue);
1696 return SPDY_NO;
1697 }
1698 *(data) = HTON31(stream_id);
1699 *(data + 1) = htonl(status);
1700
1701 control_frame->control_bit = 1;
1702 control_frame->version = SPDY_VERSION;
1703 control_frame->type = SPDY_CONTROL_FRAME_TYPES_RST_STREAM;
1704 control_frame->flags = 0;
1705
1706 response_to_queue->control_frame = control_frame;
1707 response_to_queue->process_response_handler = &SPDYF_handler_write_rst_stream;
1708 response_to_queue->data = data;
1709 response_to_queue->data_size = 8;
1710 response_to_queue->stream = stream;
1711
1712 SPDYF_queue_response (response_to_queue,
1713 session,
1714 -1);
1715
1716 return SPDY_YES;
1717}
1718
1719
1720int
1721SPDYF_prepare_window_update (struct SPDY_Session *session,
1722 struct SPDYF_Stream * stream,
1723 int32_t delta_window_size)
1724{
1725 struct SPDYF_Response_Queue *response_to_queue;
1726 struct SPDYF_Control_Frame *control_frame;
1727 uint32_t *data;
1728
1729 SPDYF_ASSERT(NULL != stream, "stream cannot be NULL");
1730
1731 if(NULL == (response_to_queue = malloc(sizeof(struct SPDYF_Response_Queue))))
1732 {
1733 return SPDY_NO;
1734 }
1735 memset(response_to_queue, 0, sizeof(struct SPDYF_Response_Queue));
1736
1737 if(NULL == (control_frame = malloc(sizeof(struct SPDYF_Control_Frame))))
1738 {
1739 free(response_to_queue);
1740 return SPDY_NO;
1741 }
1742 memset(control_frame, 0, sizeof(struct SPDYF_Control_Frame));
1743
1744 if(NULL == (data = malloc(8)))
1745 {
1746 free(control_frame);
1747 free(response_to_queue);
1748 return SPDY_NO;
1749 }
1750 *(data) = HTON31(stream->stream_id);
1751 *(data + 1) = HTON31(delta_window_size);
1752
1753 control_frame->control_bit = 1;
1754 control_frame->version = SPDY_VERSION;
1755 control_frame->type = SPDY_CONTROL_FRAME_TYPES_WINDOW_UPDATE;
1756 control_frame->flags = 0;
1757
1758 response_to_queue->control_frame = control_frame;
1759 response_to_queue->process_response_handler = &SPDYF_handler_write_window_update;
1760 response_to_queue->data = data;
1761 response_to_queue->data_size = 8;
1762 response_to_queue->stream = stream;
1763
1764 SPDYF_queue_response (response_to_queue,
1765 session,
1766 -1);
1767
1768 return SPDY_YES;
1769}
diff --git a/src/microspdy/session.h b/src/microspdy/session.h
deleted file mode 100644
index 29ab550f..00000000
--- a/src/microspdy/session.h
+++ /dev/null
@@ -1,281 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2012 Andrey Uzunov
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file 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 * @param only_one_frame when true, the function will write at most one
55 * SPDY frame to the underlying IO subsystem;
56 * when false, the function will write up to
57 * session->max_num_frames SPDY frames
58 * @return SPDY_YES if the session's internal writing state has changed:
59 * something was written and/or session's status was
60 * changed and/or response callback was called but did not provide
61 * data. It is possible that error occurred but was handled
62 * and the status was therefore changed.
63 * SPDY_NO if nothing happened. However, it is possible that some
64 * frames were discarded within the call, e.g. frames belonging
65 * to a closed stream.
66 */
67int
68SPDYF_session_write (struct SPDY_Session *session,
69 bool only_one_frame);
70
71
72/**
73 * Called by the daemon on SPDY_run to handle the data in the read and write
74 * buffer of a session. Based on the state and the content of the read
75 * buffer new frames are received and interpreted, appropriate user
76 * callbacks are called and maybe something is put on the response queue
77 * ready to be handled by session_write.
78 *
79 * @param session SPDY_Session which will be handled.
80 * @return SPDY_YES if something from the read buffers was processed,
81 * session's status was changed and/or the session was closed.
82 * SPDY_NO if nothing happened, e.g. the session is in a state,
83 * not allowing processing read buffers.
84 */
85int
86SPDYF_session_idle (struct SPDY_Session *session);
87
88
89/**
90 * This function shutdowns the socket, moves the session structure to
91 * daemon's queue for sessions to be cleaned up.
92 *
93 * @param session SPDY_Session which will be handled.
94 */
95void
96SPDYF_session_close (struct SPDY_Session *session);
97
98
99/**
100 * Called to accept new TCP connection and create SPDY session.
101 *
102 * @param daemon SPDY_Daemon whose listening socket is used.
103 * @return SPDY_NO on any kind of error while accepting new TCP connection
104 * and initializing new SPDY_Session.
105 * SPDY_YES otherwise.
106 */
107int
108SPDYF_session_accept(struct SPDY_Daemon *daemon);
109
110
111/**
112 * Puts SPDYF_Response_Queue object on the queue to be sent to the
113 * client later.
114 *
115 * @param response_to_queue linked list of objects containing SPDY
116 * frame and data to be added to the queue
117 * @param session SPDY session for which the response is sent
118 * @param consider_priority if SPDY_NO, the list will be added to the
119 * end of the queue.
120 * If SPDY_YES, the response will be added after
121 * the last previously added response with priority of the
122 * request grater or equal to that of the current one.
123 * If -1, the object will be put at the head of the queue.
124 */
125void
126SPDYF_queue_response (struct SPDYF_Response_Queue *response_to_queue,
127 struct SPDY_Session *session,
128 int consider_priority);
129
130
131/**
132 * Cleans up the TSL context for the session, closes the TCP connection,
133 * cleans up any data pointed by members of the session structure
134 * (buffers, queue of responses, etc.) and frees the memory allocated by
135 * the session itself.
136 */
137void
138SPDYF_session_destroy(struct SPDY_Session *session);
139
140
141/**
142 * Prepares GOAWAY frame to tell the client to stop creating new streams.
143 * The session should be closed soon after this call.
144 *
145 * @param session SPDY session
146 * @param status code for the GOAWAY frame
147 * @param in_front whether or not to put the frame in front of everything
148 * on the response queue
149 * @return SPDY_NO on error (not enough memory) or
150 * SPDY_YES on success
151 */
152int
153SPDYF_prepare_goaway (struct SPDY_Session *session,
154 enum SPDY_GOAWAY_STATUS status,
155 bool in_front);
156
157
158/**
159 * Prepares RST_STREAM frame to terminate a stream. This frame may or
160 * not indicate an error. The frame will be put at the head of the queue.
161 * This means that frames for this stream which are still in the queue
162 * will be discarded soon.
163 *
164 * @param session SPDY session
165 * @param stream stream to terminate
166 * @param status code for the RST_STREAM frame
167 * @return SPDY_NO on memory error or
168 * SPDY_YES on success
169 */
170int
171SPDYF_prepare_rst_stream (struct SPDY_Session *session,
172 struct SPDYF_Stream * stream,
173 enum SPDY_RST_STREAM_STATUS status);
174
175
176/**
177 * Prepares WINDOW_UPDATE frame to tell the other party that more
178 * data can be sent on the stream. The frame will be put at the head of
179 * the queue.
180 *
181 * @param session SPDY session
182 * @param stream stream to which the changed window will apply
183 * @param delta_window_size how much the window grows
184 * @return SPDY_NO on memory error or
185 * SPDY_YES on success
186 */
187int
188SPDYF_prepare_window_update (struct SPDY_Session *session,
189 struct SPDYF_Stream * stream,
190 int32_t delta_window_size);
191
192
193/**
194 * Handler called by session_write to fill the write buffer according to
195 * the data frame waiting in the response queue.
196 * When response data is given by user callback, the lib does not know
197 * how many frames are needed. In such case this call produces
198 * another ResponseQueue object and puts it on the queue while the the
199 * user callback says that there will be more data.
200 *
201 * @return SPDY_NO on error (not enough memory or the user calback for
202 * providing response data did something wrong). If
203 * the error is unrecoverable the handler changes session's
204 * status.
205 * SPDY_YES on success
206 */
207int
208SPDYF_handler_write_data (struct SPDY_Session *session);
209
210
211/**
212 * Handler called by session_write to fill the write buffer based on the
213 * control frame (SYN_REPLY) waiting in the response queue.
214 *
215 * @param session SPDY session
216 * @return SPDY_NO on error (zlib state is broken; the session MUST be
217 * closed). If
218 * the error is unrecoverable the handler changes session's
219 * status.
220 * SPDY_YES on success
221 */
222int
223SPDYF_handler_write_syn_reply (struct SPDY_Session *session);
224
225
226/**
227 * Handler called by session_write to fill the write buffer based on the
228 * control frame (GOAWAY) waiting in the response queue.
229 *
230 * @param session SPDY session
231 * @return SPDY_NO on error (not enough memory; by specification the
232 * session must be closed
233 * soon, thus there is no need to handle the error) or
234 * SPDY_YES on success
235 */
236int
237SPDYF_handler_write_goaway (struct SPDY_Session *session);
238
239
240/**
241 * Handler called by session_write to fill the write buffer based on the
242 * control frame (RST_STREAM) waiting in the response queue.
243 *
244 * @param session SPDY session
245 * @return SPDY_NO on error (not enough memory). If
246 * the error is unrecoverable the handler changes session's
247 * status.
248 * SPDY_YES on success
249 */
250int
251SPDYF_handler_write_rst_stream (struct SPDY_Session *session);
252
253
254/**
255 * Handler called by session_write to fill the write buffer based on the
256 * control frame (WINDOW_UPDATE) waiting in the response queue.
257 *
258 * @param session SPDY session
259 * @return SPDY_NO on error (not enough memory). If
260 * the error is unrecoverable the handler changes session's
261 * status.
262 * SPDY_YES on success
263 */
264int
265SPDYF_handler_write_window_update (struct SPDY_Session *session);
266
267
268/**
269 * Carefully ignore the full size of frames which are not yet supported
270 * by the lib.
271 * TODO Ignoring frames containing compressed bodies means that the
272 * compress state will be corrupted on next received frame. According to
273 * the draft the lib SHOULD try to decompress data also in corrupted
274 * frames just to keep right compression state.
275 *
276 * @param session SPDY_Session whose read buffer is used.
277 */
278void
279SPDYF_handler_ignore_frame (struct SPDY_Session *session);
280
281#endif
diff --git a/src/microspdy/stream.c b/src/microspdy/stream.c
deleted file mode 100644
index 9b6dc08d..00000000
--- a/src/microspdy/stream.c
+++ /dev/null
@@ -1,169 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2012 Andrey Uzunov
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file 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 stream->window_size = SPDYF_INITIAL_WINDOW_SIZE;
108
109 //put the stream to the list of streams for the session
110 DLL_insert(session->streams_head, session->streams_tail, stream);
111
112 return SPDY_YES;
113}
114
115
116void
117SPDYF_stream_destroy(struct SPDYF_Stream *stream)
118{
119 SPDY_name_value_destroy(stream->headers);
120 free(stream);
121 stream = NULL;
122}
123
124
125void
126SPDYF_stream_set_flags_on_write(struct SPDYF_Response_Queue *response_queue)
127{
128 struct SPDYF_Stream * stream = response_queue->stream;
129
130 if(NULL != response_queue->data_frame)
131 {
132 stream->is_out_closed = (bool)(response_queue->data_frame->flags & SPDY_DATA_FLAG_FIN);
133 }
134 else if(NULL != response_queue->control_frame)
135 {
136 switch(response_queue->control_frame->type)
137 {
138 case SPDY_CONTROL_FRAME_TYPES_SYN_REPLY:
139 stream->is_out_closed = (bool)(response_queue->control_frame->flags & SPDY_SYN_REPLY_FLAG_FIN);
140 break;
141
142 case SPDY_CONTROL_FRAME_TYPES_RST_STREAM:
143 if(NULL != stream)
144 {
145 stream->is_out_closed = true;
146 stream->is_in_closed = true;
147 }
148 break;
149
150 }
151 }
152}
153
154
155//TODO add function *on_read
156
157
158struct SPDYF_Stream *
159SPDYF_stream_find(uint32_t stream_id, struct SPDY_Session * session)
160{
161 struct SPDYF_Stream * stream = session->streams_head;
162
163 while(NULL != stream && stream_id != stream->stream_id)
164 {
165 stream = stream->next;
166 }
167
168 return stream;
169}
diff --git a/src/microspdy/stream.h b/src/microspdy/stream.h
deleted file mode 100644
index 220231f4..00000000
--- a/src/microspdy/stream.h
+++ /dev/null
@@ -1,76 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2012 Andrey Uzunov
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file 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_on_write(struct SPDYF_Response_Queue *response_queue);
64
65
66/**
67 * Find and return a session's stream, based on stream's ID.
68 *
69 * @param stream_id to search for
70 * @param session whose streams are considered
71 * @return SPDY_Stream with the desired ID. Can be NULL.
72 */
73struct SPDYF_Stream *
74SPDYF_stream_find(uint32_t stream_id, struct SPDY_Session * session);
75
76#endif
diff --git a/src/microspdy/structures.c b/src/microspdy/structures.c
deleted file mode 100644
index f00806bc..00000000
--- a/src/microspdy/structures.c
+++ /dev/null
@@ -1,638 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2012 Andrey Uzunov
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file 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//TODO not for here?
31#include <ctype.h>
32
33
34int
35SPDYF_name_value_is_empty(struct SPDY_NameValue *container)
36{
37 SPDYF_ASSERT(NULL != container, "NULL is not an empty container!");
38 return (NULL == container->name && NULL == container->value) ? SPDY_YES : SPDY_NO;
39}
40
41struct SPDY_NameValue *
42SPDY_name_value_create ()
43{
44 struct SPDY_NameValue *pair;
45
46 if(NULL == (pair = malloc(sizeof(struct SPDY_NameValue))))
47 return NULL;
48
49 memset (pair, 0, sizeof (struct SPDY_NameValue));
50
51 return pair;
52}
53
54
55int
56SPDY_name_value_add (struct SPDY_NameValue *container,
57 const char *name,
58 const char *value)
59{
60 unsigned int i;
61 unsigned int len;
62 struct SPDY_NameValue *pair;
63 struct SPDY_NameValue *temp;
64 char **temp_value;
65 char *temp_string;
66
67 if(NULL == container || NULL == name || NULL == value || 0 == (len = strlen(name)))
68 return SPDY_INPUT_ERROR;
69 //TODO there is old code handling value==NULL
70 //update it to handle strlen(value)==0
71
72 for(i=0; i<len; ++i)
73 {
74 if(isupper((int) name[i]))
75 return SPDY_INPUT_ERROR;
76 }
77
78 if(SPDYF_name_value_is_empty(container))
79 {
80 //container is empty/just created
81 if (NULL == (container->name = strdup (name)))
82 {
83 return SPDY_NO;
84 }
85 if (NULL == (container->value = malloc(sizeof(char *))))
86 {
87 free(container->name);
88 return SPDY_NO;
89 }
90 /*if(NULL == value)
91 container->value[0] = NULL;
92 else */if (NULL == (container->value[0] = strdup (value)))
93 {
94 free(container->value);
95 free(container->name);
96 return SPDY_NO;
97 }
98 container->num_values = 1;
99 return SPDY_YES;
100 }
101
102 pair = container;
103 while(NULL != pair)
104 {
105 if(0 == strcmp(pair->name, name))
106 {
107 //the value will be added to this pair
108 break;
109 }
110 pair = pair->next;
111 }
112
113 if(NULL == pair)
114 {
115 //the name doesn't exist in container, add new pair
116 if(NULL == (pair = malloc(sizeof(struct SPDY_NameValue))))
117 return SPDY_NO;
118
119 memset(pair, 0, sizeof(struct SPDY_NameValue));
120
121 if (NULL == (pair->name = strdup (name)))
122 {
123 free(pair);
124 return SPDY_NO;
125 }
126 if (NULL == (pair->value = malloc(sizeof(char *))))
127 {
128 free(pair->name);
129 free(pair);
130 return SPDY_NO;
131 }
132 /*if(NULL == value)
133 pair->value[0] = NULL;
134 else */if (NULL == (pair->value[0] = strdup (value)))
135 {
136 free(pair->value);
137 free(pair->name);
138 free(pair);
139 return SPDY_NO;
140 }
141 pair->num_values = 1;
142
143 temp = container;
144 while(NULL != temp->next)
145 temp = temp->next;
146 temp->next = pair;
147 pair->prev = temp;
148
149 return SPDY_YES;
150 }
151
152 //check for duplication (case sensitive)
153 for(i=0; i<pair->num_values; ++i)
154 if(0 == strcmp(pair->value[i], value))
155 return SPDY_NO;
156
157 if(strlen(pair->value[0]) > 0)
158 {
159 //the value will be appended to the others for this name
160 if (NULL == (temp_value = malloc((pair->num_values + 1) * sizeof(char *))))
161 {
162 return SPDY_NO;
163 }
164 memcpy(temp_value, pair->value, pair->num_values * sizeof(char *));
165 if (NULL == (temp_value[pair->num_values] = strdup (value)))
166 {
167 free(temp_value);
168 return SPDY_NO;
169 }
170 free(pair->value);
171 pair->value = temp_value;
172 ++pair->num_values;
173 return SPDY_YES;
174 }
175
176 //just replace the empty value
177
178 if (NULL == (temp_string = strdup (value)))
179 {
180 return SPDY_NO;
181 }
182 free(pair->value[0]);
183 pair->value[0] = temp_string;
184
185 return SPDY_YES;
186}
187
188
189const char * const *
190SPDY_name_value_lookup (struct SPDY_NameValue *container,
191 const char *name,
192 int *num_values)
193{
194 struct SPDY_NameValue *temp = container;
195
196 if(NULL == container || NULL == name || NULL == num_values)
197 return NULL;
198 if(SPDYF_name_value_is_empty(container))
199 return NULL;
200
201 do
202 {
203 if(strcmp(name, temp->name) == 0)
204 {
205 *num_values = temp->num_values;
206 return (const char * const *)temp->value;
207 }
208
209 temp = temp->next;
210 }
211 while(NULL != temp);
212
213 return NULL;
214}
215
216
217void
218SPDY_name_value_destroy (struct SPDY_NameValue *container)
219{
220 unsigned int i;
221 struct SPDY_NameValue *temp = container;
222
223 while(NULL != temp)
224 {
225 container = container->next;
226 free(temp->name);
227 for(i=0; i<temp->num_values; ++i)
228 free(temp->value[i]);
229 free(temp->value);
230 free(temp);
231 temp=container;
232 }
233}
234
235
236int
237SPDY_name_value_iterate (struct SPDY_NameValue *container,
238 SPDY_NameValueIterator iterator,
239 void *iterator_cls)
240{
241 int count;
242 int ret;
243 struct SPDY_NameValue *temp = container;
244
245 if(NULL == container)
246 return SPDY_INPUT_ERROR;
247
248 //check if container is an empty struct
249 if(SPDYF_name_value_is_empty(container))
250 return 0;
251
252 count = 0;
253
254 if(NULL == iterator)
255 {
256 do
257 {
258 ++count;
259 temp=temp->next;
260 }
261 while(NULL != temp);
262
263 return count;
264 }
265
266 //code duplication for avoiding if here
267 do
268 {
269 ++count;
270 ret = iterator(iterator_cls, temp->name, (const char * const *)temp->value, temp->num_values);
271 temp=temp->next;
272 }
273 while(NULL != temp && SPDY_YES == ret);
274
275 return count;
276}
277
278void
279SPDY_destroy_response(struct SPDY_Response *response)
280{
281 if(NULL == response)
282 return;
283 free(response->data);
284 free(response->headers);
285 free(response);
286}
287
288
289struct SPDYF_Response_Queue *
290SPDYF_response_queue_create(bool is_data,
291 void *data,
292 size_t data_size,
293 struct SPDY_Response *response,
294 struct SPDYF_Stream *stream,
295 bool closestream,
296 SPDYF_ResponseQueueResultCallback frqcb,
297 void *frqcb_cls,
298 SPDY_ResponseResultCallback rrcb,
299 void *rrcb_cls)
300{
301 struct SPDYF_Response_Queue *head = NULL;
302 struct SPDYF_Response_Queue *prev;
303 struct SPDYF_Response_Queue *response_to_queue;
304 struct SPDYF_Control_Frame *control_frame;
305 struct SPDYF_Data_Frame *data_frame;
306 unsigned int i;
307 bool is_last;
308
309 SPDYF_ASSERT((! is_data)
310 || ((0 == data_size) && (NULL != response->rcb))
311 || ((0 < data_size) && (NULL == response->rcb)),
312 "either data or request->rcb must not be null");
313
314 if (is_data && (data_size > SPDY_MAX_SUPPORTED_FRAME_SIZE))
315 {
316 //separate the data in more frames and add them to the queue
317
318 prev=NULL;
319 for(i = 0; i < data_size; i += SPDY_MAX_SUPPORTED_FRAME_SIZE)
320 {
321 is_last = (i + SPDY_MAX_SUPPORTED_FRAME_SIZE) >= data_size;
322
323 if(NULL == (response_to_queue = malloc(sizeof(struct SPDYF_Response_Queue))))
324 goto free_and_fail;
325
326 memset(response_to_queue, 0, sizeof(struct SPDYF_Response_Queue));
327 if(0 == i)
328 head = response_to_queue;
329
330 if(NULL == (data_frame = malloc(sizeof(struct SPDYF_Data_Frame))))
331 {
332 free(response_to_queue);
333 goto free_and_fail;
334 }
335 memset(data_frame, 0, sizeof(struct SPDYF_Data_Frame));
336 data_frame->control_bit = 0;
337 data_frame->stream_id = stream->stream_id;
338 if(is_last && closestream)
339 data_frame->flags |= SPDY_DATA_FLAG_FIN;
340
341 response_to_queue->data_frame = data_frame;
342 response_to_queue->process_response_handler = &SPDYF_handler_write_data;
343 response_to_queue->is_data = is_data;
344 response_to_queue->stream = stream;
345 if(is_last)
346 {
347 response_to_queue->frqcb = frqcb;
348 response_to_queue->frqcb_cls = frqcb_cls;
349 response_to_queue->rrcb = rrcb;
350 response_to_queue->rrcb_cls = rrcb_cls;
351 }
352 response_to_queue->data = data + i;
353 response_to_queue->data_size = is_last
354 ? (data_size - 1) % SPDY_MAX_SUPPORTED_FRAME_SIZE + 1
355 : SPDY_MAX_SUPPORTED_FRAME_SIZE;
356 response_to_queue->response = response;
357
358 response_to_queue->prev = prev;
359 if(NULL != prev)
360 prev->next = response_to_queue;
361 prev = response_to_queue;
362 }
363
364 return head;
365
366 //for GOTO
367 free_and_fail:
368 while(NULL != head)
369 {
370 response_to_queue = head;
371 head = head->next;
372 free(response_to_queue->data_frame);
373 free(response_to_queue);
374 }
375 return NULL;
376 }
377
378 //create only one frame for data, data with callback or control frame
379
380 if(NULL == (response_to_queue = malloc(sizeof(struct SPDYF_Response_Queue))))
381 {
382 return NULL;
383 }
384 memset(response_to_queue, 0, sizeof(struct SPDYF_Response_Queue));
385
386 if(is_data)
387 {
388 if(NULL == (data_frame = malloc(sizeof(struct SPDYF_Data_Frame))))
389 {
390 free(response_to_queue);
391 return NULL;
392 }
393 memset(data_frame, 0, sizeof(struct SPDYF_Data_Frame));
394 data_frame->control_bit = 0;
395 data_frame->stream_id = stream->stream_id;
396 if(closestream && NULL == response->rcb)
397 data_frame->flags |= SPDY_DATA_FLAG_FIN;
398
399 response_to_queue->data_frame = data_frame;
400 response_to_queue->process_response_handler = &SPDYF_handler_write_data;
401 }
402 else
403 {
404 if(NULL == (control_frame = malloc(sizeof(struct SPDYF_Control_Frame))))
405 {
406 free(response_to_queue);
407 return NULL;
408 }
409 memset(control_frame, 0, sizeof(struct SPDYF_Control_Frame));
410 control_frame->control_bit = 1;
411 control_frame->version = SPDY_VERSION;
412 control_frame->type = SPDY_CONTROL_FRAME_TYPES_SYN_REPLY;
413 if(closestream)
414 control_frame->flags |= SPDY_SYN_REPLY_FLAG_FIN;
415
416 response_to_queue->control_frame = control_frame;
417 response_to_queue->process_response_handler = &SPDYF_handler_write_syn_reply;
418 }
419
420 response_to_queue->is_data = is_data;
421 response_to_queue->stream = stream;
422 response_to_queue->frqcb = frqcb;
423 response_to_queue->frqcb_cls = frqcb_cls;
424 response_to_queue->rrcb = rrcb;
425 response_to_queue->rrcb_cls = rrcb_cls;
426 response_to_queue->data = data;
427 response_to_queue->data_size = data_size;
428 response_to_queue->response = response;
429
430 return response_to_queue;
431}
432
433
434void
435SPDYF_response_queue_destroy(struct SPDYF_Response_Queue *response_queue)
436{
437 //data is not copied to the struct but only linked
438 //but this is not valid for GOAWAY and RST_STREAM
439 if(!response_queue->is_data
440 && (SPDY_CONTROL_FRAME_TYPES_RST_STREAM == response_queue->control_frame->type
441 || SPDY_CONTROL_FRAME_TYPES_GOAWAY == response_queue->control_frame->type))
442 {
443 free(response_queue->data);
444 }
445 if(response_queue->is_data)
446 free(response_queue->data_frame);
447 else
448 free(response_queue->control_frame);
449
450 free(response_queue);
451}
452
453
454/* Needed by testcase to be extern -- should this be
455 in the header? */
456_MHD_EXTERN ssize_t
457SPDYF_name_value_to_stream(struct SPDY_NameValue * container[],
458 int num_containers,
459 void **stream)
460{
461 size_t size;
462 int32_t num_pairs = 0;
463 int32_t value_size;
464 int32_t name_size;
465 int32_t temp;
466 unsigned int i;
467 unsigned int offset;
468 unsigned int value_offset;
469 struct SPDY_NameValue * iterator;
470 int j;
471
472 size = 4; //for num pairs
473
474 for(j=0; j<num_containers; ++j)
475 {
476 iterator = container[j];
477 while(iterator != NULL)
478 {
479 ++num_pairs;
480 size += 4 + strlen(iterator->name); //length + string
481
482 SPDYF_ASSERT(iterator->num_values>0, "num_values is 0");
483
484 size += 4; //value length
485
486 for(i=0; i<iterator->num_values; ++i)
487 {
488 //if(NULL == iterator->value[i])
489 // continue;
490 size += strlen(iterator->value[i]); // string
491 if(i/* || !strlen(iterator->value[i])*/) ++size; //NULL separator
492 }
493
494 iterator = iterator->next;
495 }
496 }
497
498 if(NULL == (*stream = malloc(size)))
499 {
500 return -1;
501 }
502
503 //put num_pairs to the stream
504 num_pairs = htonl(num_pairs);
505 memcpy(*stream, &num_pairs, 4);
506 offset = 4;
507
508 //put all other headers to the stream
509 for(j=0; j<num_containers; ++j)
510 {
511 iterator = container[j];
512 while(iterator != NULL)
513 {
514 name_size = strlen(iterator->name);
515 temp = htonl(name_size);
516 memcpy(*stream + offset, &temp, 4);
517 offset += 4;
518 strncpy(*stream + offset, iterator->name, name_size);
519 offset += name_size;
520
521 value_offset = offset;
522 offset += 4;
523 for(i=0; i<iterator->num_values; ++i)
524 {
525 if(i /*|| !strlen(iterator->value[0])*/)
526 {
527 memset(*stream + offset, 0, 1);
528 ++offset;
529 //if(!i) continue;
530 }
531 //else if(NULL != iterator->value[i])
532 //{
533 strncpy(*stream + offset, iterator->value[i], strlen(iterator->value[i]));
534 offset += strlen(iterator->value[i]);
535 //}
536 }
537 value_size = offset - value_offset - 4;
538 value_size = htonl(value_size);
539 memcpy(*stream + value_offset, &value_size, 4);
540
541 iterator = iterator->next;
542 }
543 }
544
545 SPDYF_ASSERT(offset == size,"offset is wrong");
546
547 return size;
548}
549
550
551/* Needed by testcase to be extern -- should this be
552 in the header? */
553_MHD_EXTERN int
554SPDYF_name_value_from_stream(void *stream,
555 size_t size,
556 struct SPDY_NameValue ** container)
557{
558 int32_t num_pairs;
559 int32_t value_size;
560 int32_t name_size;
561 int i;
562 unsigned int offset = 0;
563 unsigned int value_end_offset;
564 char *name;
565 char *value;
566
567 if(NULL == (*container = SPDY_name_value_create ()))
568 {
569 return SPDY_NO;
570 }
571
572 //get number of pairs
573 memcpy(&num_pairs, stream, 4);
574 offset = 4;
575 num_pairs = ntohl(num_pairs);
576
577 if(num_pairs > 0)
578 {
579 for(i = 0; i < num_pairs; ++i)
580 {
581 //get name size
582 memcpy(&name_size, stream + offset, 4);
583 offset += 4;
584 name_size = ntohl(name_size);
585 //get name
586 if(NULL == (name = strndup(stream + offset, name_size)))
587 {
588 SPDY_name_value_destroy(*container);
589 return SPDY_NO;
590 }
591 offset+=name_size;
592
593 //get value size
594 memcpy(&value_size, stream + offset, 4);
595 offset += 4;
596 value_size = ntohl(value_size);
597 value_end_offset = offset + value_size;
598 //get value
599 do
600 {
601 if(NULL == (value = strndup(stream + offset, value_size)))
602 {
603 free(name);
604 SPDY_name_value_destroy(*container);
605 return SPDY_NO;
606 }
607 offset += strlen(value);
608 if(offset < value_end_offset)
609 ++offset; //NULL separator
610
611 //add name/value to the struct
612 if(SPDY_YES != SPDY_name_value_add(*container, name, value))
613 {
614 free(name);
615 free(value);
616 SPDY_name_value_destroy(*container);
617 return SPDY_NO;
618 }
619 free(value);
620 }
621 while(offset < value_end_offset);
622
623 free(name);
624
625 if(offset != value_end_offset)
626 {
627 SPDY_name_value_destroy(*container);
628 return SPDY_INPUT_ERROR;
629 }
630 }
631 }
632
633 if(offset == size)
634 return SPDY_YES;
635
636 SPDY_name_value_destroy(*container);
637 return SPDY_INPUT_ERROR;
638}
diff --git a/src/microspdy/structures.h b/src/microspdy/structures.h
deleted file mode 100644
index e1f8797a..00000000
--- a/src/microspdy/structures.h
+++ /dev/null
@@ -1,1246 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2012 Andrey Uzunov
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file 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 "io.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/**
322 * Callback for received new data chunk.
323 *
324 * @param cls client-defined closure
325 * @param stream handler
326 * @param buf data chunk from the data
327 * @param size the size of the data chunk 'buf' in bytes
328 * @param more false if this is the last frame received on this stream. Note:
329 * true does not mean that more data will come, exceptional
330 * situation is possible
331 * @return SPDY_YES to continue calling the function,
332 * SPDY_NO to stop calling the function for this stream
333 */
334typedef int
335(*SPDYF_NewDataCallback) (void * cls,
336 struct SPDYF_Stream *stream,
337 const void * buf,
338 size_t size,
339 bool more);
340
341
342/**
343 * Callback for new stream. To be used in the application layer of the
344 * lib.
345 *
346 * @param cls
347 * @param stream the new stream
348 * @return SPDY_YES on success,
349 * SPDY_NO if error occurs
350 */
351typedef int
352(*SPDYF_NewStreamCallback) (void *cls,
353 struct SPDYF_Stream * stream);
354
355
356/**
357 * Callback to be called when the response queue object was handled and
358 * the data was already sent.
359 *
360 * @param cls
361 * @param response_queue the SPDYF_Response_Queue structure which will
362 * be cleaned very soon
363 * @param status shows if actually the response was sent or it was
364 * discarded by the lib for any reason (e.g., closing session,
365 * closing stream, stopping daemon, etc.). It is possible that
366 * status indicates an error but part of the response (in one
367 * or several frames) was sent to the client.
368 */
369typedef void
370(*SPDYF_ResponseQueueResultCallback) (void * cls,
371 struct SPDYF_Response_Queue *response_queue,
372 enum SPDY_RESPONSE_RESULT status);
373
374
375/**
376 * Representation of the control frame's headers, which are common for
377 * all types.
378 */
379struct __attribute__((__packed__)) SPDYF_Control_Frame
380{
381 uint16_t version : 15;
382 uint16_t control_bit : 1; /* always 1 for control frames */
383 uint16_t type;
384 uint32_t flags : 8;
385 uint32_t length : 24;
386};
387
388
389/**
390 * Representation of the data frame's headers.
391 */
392struct __attribute__((__packed__)) SPDYF_Data_Frame
393{
394 uint32_t stream_id : 31;
395 uint32_t control_bit : 1; /* always 0 for data frames */
396 uint32_t flags : 8;
397 uint32_t length : 24;
398};
399
400
401/**
402 * Queue of the responses, to be handled (e.g. compressed) and sent later.
403 */
404struct SPDYF_Response_Queue
405{
406 /**
407 * This is a doubly-linked list.
408 */
409 struct SPDYF_Response_Queue *next;
410
411 /**
412 * This is a doubly-linked list.
413 */
414 struct SPDYF_Response_Queue *prev;
415
416 /**
417 * Stream (Request) for which is the response.
418 */
419 struct SPDYF_Stream *stream;
420
421 /**
422 * Response structure with all the data (uncompressed headers) to be sent.
423 */
424 struct SPDY_Response *response;
425
426 /**
427 * Control frame. The length field should be set after compressing
428 * the headers!
429 */
430 struct SPDYF_Control_Frame *control_frame;
431
432 /**
433 * Data frame. The length field should be set after compressing
434 * the body!
435 */
436 struct SPDYF_Data_Frame *data_frame;
437
438 /**
439 * Data to be sent: name/value pairs in control frames or body in data frames.
440 */
441 void *data;
442
443 /**
444 * Specific handler for different frame types.
445 */
446 int (* process_response_handler)(struct SPDY_Session *session);
447
448 /**
449 * Callback to be called when the last bytes from the response was sent
450 * to the client.
451 */
452 SPDYF_ResponseQueueResultCallback frqcb;
453
454 /**
455 * Closure for frqcb.
456 */
457 void *frqcb_cls;
458
459 /**
460 * Callback to be used by the application layer.
461 */
462 SPDY_ResponseResultCallback rrcb;
463
464 /**
465 * Closure for rcb.
466 */
467 void *rrcb_cls;
468
469 /**
470 * Data size.
471 */
472 size_t data_size;
473
474 /**
475 * True if data frame should be sent. False if control frame should
476 * be sent.
477 */
478 bool is_data;
479};
480
481
482
483/**
484 * Collection of HTTP headers used in requests and responses.
485 */
486struct SPDY_NameValue
487{
488 /**
489 * This is a doubly-linked list.
490 */
491 struct SPDY_NameValue *next;
492
493 /**
494 * This is a doubly-linked list.
495 */
496 struct SPDY_NameValue *prev;
497
498 /**
499 * Null terminated string for name.
500 */
501 char *name;
502
503 /**
504 * Array of Null terminated strings for value. num_values is the
505 * length of the array.
506 */
507 char **value;
508
509 /**
510 * Number of values, this is >= 0.
511 */
512 unsigned int num_values;
513};
514
515
516/**
517 * Represents a SPDY stream
518 */
519struct SPDYF_Stream
520{
521 /**
522 * This is a doubly-linked list.
523 */
524 struct SPDYF_Stream *next;
525
526 /**
527 * This is a doubly-linked list.
528 */
529 struct SPDYF_Stream *prev;
530
531 /**
532 * Reference to the SPDY_Session struct.
533 */
534 struct SPDY_Session *session;
535
536 /**
537 * Name value pairs, sent within the frame which created the stream.
538 */
539 struct SPDY_NameValue *headers;
540
541 /**
542 * Any object to be used by the application layer.
543 */
544 void *cls;
545
546 /**
547 * This stream's ID.
548 */
549 uint32_t stream_id;
550
551 /**
552 * Stream to which this one is associated.
553 */
554 uint32_t assoc_stream_id;
555
556 /**
557 * The window of the data within data frames.
558 */
559 uint32_t window_size;
560
561 /**
562 * Stream priority. 0 is the highest, 7 is the lowest.
563 */
564 uint8_t priority;
565
566 /**
567 * Integer specifying the index in the server's CREDENTIAL vector of
568 * the client certificate to be used for this request The value 0
569 * means no client certificate should be associated with this stream.
570 */
571 uint8_t slot;
572
573 /**
574 * If initially the stream was created as unidirectional.
575 */
576 bool flag_unidirectional;
577
578 /**
579 * If the stream won't be used for receiving frames anymore. The
580 * client has sent FLAG_FIN or the stream was terminated with
581 * RST_STREAM.
582 */
583 bool is_in_closed;
584
585 /**
586 * If the stream won't be used for sending out frames anymore. The
587 * server has sent FLAG_FIN or the stream was terminated with
588 * RST_STREAM.
589 */
590 bool is_out_closed;
591
592 /**
593 * Which entity (server/client) has created the stream.
594 */
595 bool is_server_initiator;
596};
597
598
599/**
600 * Represents a SPDY session which is just a TCP connection
601 */
602struct SPDY_Session
603{
604 /**
605 * zlib stream for decompressing all the name/pair values from the
606 * received frames. All the received compressed data must be
607 * decompressed within one context: this stream. Thus, it should be
608 * unique for the session and initialized at its creation.
609 */
610 z_stream zlib_recv_stream;
611
612 /**
613 * zlib stream for compressing all the name/pair values from the
614 * frames to be sent. All the sent compressed data must be
615 * compressed within one context: this stream. Thus, it should be
616 * unique for the session and initialized at its creation.
617 */
618 z_stream zlib_send_stream;
619
620 /**
621 * This is a doubly-linked list.
622 */
623 struct SPDY_Session *next;
624
625 /**
626 * This is a doubly-linked list.
627 */
628 struct SPDY_Session *prev;
629
630 /**
631 * Reference to the SPDY_Daemon struct.
632 */
633 struct SPDY_Daemon *daemon;
634
635 /**
636 * Foreign address (of length addr_len).
637 */
638 struct sockaddr *addr;
639
640 /**
641 * Head of doubly-linked list of the SPDY streams belonging to the
642 * session.
643 */
644 struct SPDYF_Stream *streams_head;
645
646 /**
647 * Tail of doubly-linked list of the streams.
648 */
649 struct SPDYF_Stream *streams_tail;
650
651 /**
652 * Unique IO context for the session. Initialized on each creation
653 * (actually when the TCP connection is established).
654 */
655 void *io_context;
656
657 /**
658 * Head of doubly-linked list of the responses.
659 */
660 struct SPDYF_Response_Queue *response_queue_head;
661
662 /**
663 * Tail of doubly-linked list of the responses.
664 */
665 struct SPDYF_Response_Queue *response_queue_tail;
666
667 /**
668 * Buffer for reading requests.
669 */
670 void *read_buffer;
671
672 /**
673 * Buffer for writing responses.
674 */
675 void *write_buffer;
676
677 /**
678 * Specific handler for the frame that is currently being received.
679 */
680 void (*frame_handler) (struct SPDY_Session * session);
681
682 /**
683 * Closure for frame_handler.
684 */
685 void *frame_handler_cls;
686
687 /**
688 * Extra field to be used by the user with set/get func for whatever
689 * purpose he wants.
690 */
691 void *user_cls;
692
693 /**
694 * Function to initialize the IO context for a new session.
695 */
696 SPDYF_IONewSession fio_new_session;
697
698 /**
699 * Function to deinitialize the IO context for a session.
700 */
701 SPDYF_IOCloseSession fio_close_session;
702
703 /**
704 * Function to read data from socket.
705 */
706 SPDYF_IORecv fio_recv;
707
708 /**
709 * Function to write data to socket.
710 */
711 SPDYF_IOSend fio_send;
712
713 /**
714 * Function to check for pending data in IO buffers.
715 */
716 SPDYF_IOIsPending fio_is_pending;
717
718 /**
719 * Function to call before writing set of frames.
720 */
721 SPDYF_IOBeforeWrite fio_before_write;
722
723 /**
724 * Function to call after writing set of frames.
725 */
726 SPDYF_IOAfterWrite fio_after_write;
727
728 /**
729 * Number of bytes that the lib must ignore immediately after they
730 * are read from the TLS socket without adding them to the read buf.
731 * This is needed, for instance, when receiving frame bigger than
732 * the buffer to avoid deadlock situations.
733 */
734 size_t read_ignore_bytes;
735
736 /**
737 * Size of read_buffer (in bytes). This value indicates
738 * how many bytes we're willing to read into the buffer;
739 * the real buffer is one byte longer to allow for
740 * adding zero-termination (when needed).
741 */
742 size_t read_buffer_size;
743
744 /**
745 * Position where we currently append data in
746 * read_buffer (last valid position).
747 */
748 size_t read_buffer_offset;
749
750 /**
751 * Position until where everything was already read
752 */
753 size_t read_buffer_beginning;
754
755 /**
756 * Size of write_buffer (in bytes). This value indicates
757 * how many bytes we're willing to prepare for writing.
758 */
759 size_t write_buffer_size;
760
761 /**
762 * Position where we currently append data in
763 * write_buffer (last valid position).
764 */
765 size_t write_buffer_offset;
766
767 /**
768 * Position until where everything was already written to the socket
769 */
770 size_t write_buffer_beginning;
771
772 /**
773 * Last time this connection had any activity
774 * (reading or writing). In milliseconds.
775 */
776 unsigned long long last_activity;
777
778 /**
779 * Socket for this connection. Set to -1 if
780 * this connection has died (daemon should clean
781 * up in that case).
782 */
783 int socket_fd;
784
785 /**
786 * Length of the foreign address.
787 */
788 socklen_t addr_len;
789
790 /**
791 * The biggest stream ID for this session for streams initiated
792 * by the client.
793 */
794 uint32_t last_in_stream_id;
795
796 /**
797 * The biggest stream ID for this session for streams initiated
798 * by the server.
799 */
800 uint32_t last_out_stream_id;
801
802 /**
803 * This value is updated whenever SYN_REPLY or RST_STREAM are sent
804 * and is used later in GOAWAY frame.
805 * TODO it is not clear in the draft what happens when streams are
806 * not answered in the order of their IDs. Moreover, why should we
807 * send GOAWAY with the ID of received bogus SYN_STREAM with huge ID?
808 */
809 uint32_t last_replied_to_stream_id;
810
811 /**
812 * Shows the stream id of the currently handled frame. This value is
813 * to be used when sending RST_STREAM in answer to a problematic
814 * frame, e.g. larger than supported.
815 */
816 uint32_t current_stream_id;
817
818 /**
819 * Maximum number of frames to be written to the socket at once. The
820 * library tries to send max_num_frames in a single call to SPDY_run
821 * for a single session. This means no requests can be received nor
822 * other sessions can send data as long the current one has enough
823 * frames to send and there is no error on writing.
824 */
825 uint32_t max_num_frames;
826
827 /**
828 * Shows the current receiving state the session, i.e. what is
829 * expected to come now, and how it shold be handled.
830 */
831 enum SPDY_SESSION_STATUS status;
832
833 /**
834 * Has this socket been closed for reading (i.e.
835 * other side closed the connection)? If so,
836 * we must completely close the connection once
837 * we are done sending our response (and stop
838 * trying to read from this socket).
839 */
840 bool read_closed;
841
842 /**
843 * If the server sends GOAWAY, it must ignore all SYN_STREAMS for
844 * this session. Normally the server will soon close the TCP session.
845 */
846 bool is_goaway_sent;
847
848 /**
849 * If the server receives GOAWAY, it must not send new SYN_STREAMS
850 * on this session. Normally the client will soon close the TCP
851 * session.
852 */
853 bool is_goaway_received;
854};
855
856
857/**
858 * State and settings kept for each SPDY daemon.
859 */
860struct SPDY_Daemon
861{
862
863 /**
864 * Tail of doubly-linked list of our current, active sessions.
865 */
866 struct SPDY_Session *sessions_head;
867
868 /**
869 * Tail of doubly-linked list of our current, active sessions.
870 */
871 struct SPDY_Session *sessions_tail;
872
873 /**
874 * Tail of doubly-linked list of connections to clean up.
875 */
876 struct SPDY_Session *cleanup_head;
877
878 /**
879 * Tail of doubly-linked list of connections to clean up.
880 */
881 struct SPDY_Session *cleanup_tail;
882
883 /**
884 * Unique IO context for the daemon. Initialized on daemon start.
885 */
886 void *io_context;
887
888 /**
889 * Certificate file of the server. File path is kept here.
890 */
891 char *certfile;
892
893 /**
894 * Key file for the certificate of the server. File path is
895 * kept here.
896 */
897 char *keyfile;
898
899
900 /**
901 * The address to which the listening socket is bound.
902 */
903 struct sockaddr *address;
904
905 /**
906 * Callback called when a new SPDY session is
907 * established by a client
908 */
909 SPDY_NewSessionCallback new_session_cb;
910
911 /**
912 * Callback called when a client closes the session
913 */
914 SPDY_SessionClosedCallback session_closed_cb;
915
916 /**
917 * Callback called when a client sends request
918 */
919 SPDY_NewRequestCallback new_request_cb;
920
921 /**
922 * Callback called when HTTP POST params are received
923 * after request. To be used by the application layer
924 */
925 SPDY_NewDataCallback received_data_cb;
926
927 /**
928 * Callback called when DATA frame is received.
929 */
930 SPDYF_NewDataCallback freceived_data_cb;
931
932 /**
933 * Closure argument for all the callbacks that can be used by the client.
934 */
935 void *cls;
936
937 /**
938 * Callback called when new stream is created.
939 */
940 SPDYF_NewStreamCallback fnew_stream_cb;
941
942 /**
943 * Closure argument for all the callbacks defined in the framing layer.
944 */
945 void *fcls;
946
947 /**
948 * Function to initialize the IO context for the daemon.
949 */
950 SPDYF_IOInit fio_init;
951
952 /**
953 * Function to deinitialize the IO context for the daemon.
954 */
955 SPDYF_IODeinit fio_deinit;
956
957 /**
958 * After how many milliseconds of inactivity should
959 * connections time out? Zero for no timeout.
960 */
961 unsigned long long session_timeout;
962
963 /**
964 * Listen socket.
965 */
966 int socket_fd;
967
968 /**
969 * This value is inherited by all sessions of the daemon.
970 * Maximum number of frames to be written to the socket at once. The
971 * library tries to send max_num_frames in a single call to SPDY_run
972 * for a single session. This means no requests can be received nor
973 * other sessions can send data as long the current one has enough
974 * frames to send and there is no error on writing.
975 */
976 uint32_t max_num_frames;
977
978 /**
979 * Daemon's options.
980 */
981 enum SPDY_DAEMON_OPTION options;
982
983 /**
984 * Daemon's flags.
985 */
986 enum SPDY_DAEMON_FLAG flags;
987
988 /**
989 * IO subsystem type used by daemon and all its sessions.
990 */
991 enum SPDY_IO_SUBSYSTEM io_subsystem;
992
993 /**
994 * Listen port.
995 */
996 uint16_t port;
997};
998
999
1000/**
1001 * Represents a SPDY response.
1002 */
1003struct SPDY_Response
1004{
1005 /**
1006 * Raw uncompressed stream of the name/value pairs in SPDY frame
1007 * used for the HTTP headers.
1008 */
1009 void *headers;
1010
1011 /**
1012 * Raw stream of the data to be sent. Equivalent to the body in HTTP
1013 * response.
1014 */
1015 void *data;
1016
1017 /**
1018 * Callback function to be used when the response data is provided
1019 * with callbacks. In this case data must be NULL and data_size must
1020 * be 0.
1021 */
1022 SPDY_ResponseCallback rcb;
1023
1024 /**
1025 * Extra argument to rcb.
1026 */
1027 void *rcb_cls;
1028
1029 /**
1030 * Length of headers.
1031 */
1032 size_t headers_size;
1033
1034 /**
1035 * Length of data.
1036 */
1037 size_t data_size;
1038
1039 /**
1040 * The callback func will be called to get that amount of bytes to
1041 * put them into a DATA frame. It is either user preffered or
1042 * the maximum supported by the lib value.
1043 */
1044 uint32_t rcb_block_size;
1045};
1046
1047
1048/* Macros for handling data and structures */
1049
1050
1051/**
1052 * Insert an element at the head of a DLL. Assumes that head, tail and
1053 * element are structs with prev and next fields.
1054 *
1055 * @param head pointer to the head of the DLL (struct ? *)
1056 * @param tail pointer to the tail of the DLL (struct ? *)
1057 * @param element element to insert (struct ? *)
1058 */
1059#define DLL_insert(head,tail,element) do { \
1060 (element)->next = (head); \
1061 (element)->prev = NULL; \
1062 if ((tail) == NULL) \
1063 (tail) = element; \
1064 else \
1065 (head)->prev = element; \
1066 (head) = (element); } while (0)
1067
1068
1069/**
1070 * Remove an element from a DLL. Assumes
1071 * that head, tail and element are structs
1072 * with prev and next fields.
1073 *
1074 * @param head pointer to the head of the DLL (struct ? *)
1075 * @param tail pointer to the tail of the DLL (struct ? *)
1076 * @param element element to remove (struct ? *)
1077 */
1078#define DLL_remove(head,tail,element) do { \
1079 if ((element)->prev == NULL) \
1080 (head) = (element)->next; \
1081 else \
1082 (element)->prev->next = (element)->next; \
1083 if ((element)->next == NULL) \
1084 (tail) = (element)->prev; \
1085 else \
1086 (element)->next->prev = (element)->prev; \
1087 (element)->next = NULL; \
1088 (element)->prev = NULL; } while (0)
1089
1090
1091/**
1092 * Convert all integers in a SPDY control frame headers structure from
1093 * host byte order to network byte order.
1094 *
1095 * @param frame input and output structure (struct SPDY_Control_Frame *)
1096 */
1097#if HAVE_BIG_ENDIAN
1098#define SPDYF_CONTROL_FRAME_HTON(frame)
1099#else
1100#define SPDYF_CONTROL_FRAME_HTON(frame) do { \
1101 (*((uint16_t *) frame )) = (*((uint8_t *) (frame) +1 )) | ((*((uint8_t *) frame ))<<8);\
1102 (frame)->type = htons((frame)->type); \
1103 (frame)->length = HTON24((frame)->length); \
1104 } while (0)
1105#endif
1106
1107
1108/**
1109 * Convert all integers in a SPDY control frame headers structure from
1110 * network byte order to host byte order.
1111 *
1112 * @param frame input and output structure (struct SPDY_Control_Frame *)
1113 */
1114#if HAVE_BIG_ENDIAN
1115#define SPDYF_CONTROL_FRAME_NTOH(frame)
1116#else
1117#define SPDYF_CONTROL_FRAME_NTOH(frame) do { \
1118 (*((uint16_t *) frame )) = (*((uint8_t *) (frame) +1 )) | ((*((uint8_t *) frame ))<<8);\
1119 (frame)->type = ntohs((frame)->type); \
1120 (frame)->length = NTOH24((frame)->length); \
1121 } while (0)
1122#endif
1123
1124
1125/**
1126 * Convert all integers in a SPDY data frame headers structure from
1127 * host byte order to network byte order.
1128 *
1129 * @param frame input and output structure (struct SPDY_Data_Frame *)
1130 */
1131#if HAVE_BIG_ENDIAN
1132#define SPDYF_DATA_FRAME_HTON(frame)
1133#else
1134#define SPDYF_DATA_FRAME_HTON(frame) do { \
1135 *((uint32_t *) frame ) = htonl(*((uint32_t *) frame ));\
1136 (frame)->length = HTON24((frame)->length); \
1137 } while (0)
1138#endif
1139
1140
1141/**
1142 * Convert all integers in a SPDY data frame headers structure from
1143 * network byte order to host byte order.
1144 *
1145 * @param frame input and output structure (struct SPDY_Data_Frame *)
1146 */
1147#if HAVE_BIG_ENDIAN
1148#define SPDYF_DATA_FRAME_NTOH(frame)
1149#else
1150#define SPDYF_DATA_FRAME_NTOH(frame) do { \
1151 *((uint32_t *) frame ) = ntohl(*((uint32_t *) frame ));\
1152 (frame)->length = NTOH24((frame)->length); \
1153 } while (0)
1154#endif
1155
1156
1157/**
1158 * Creates one or more new SPDYF_Response_Queue object to be put on the
1159 * response queue.
1160 *
1161 * @param is_data whether new data frame or new control frame will be
1162 * crerated
1163 * @param data the row stream which will be used as the body of the frame
1164 * @param data_size length of data
1165 * @param response object, part of which is the frame
1166 * @param stream on which data is to be sent
1167 * @param closestream TRUE if the frame must close the stream (with flag)
1168 * @param frqcb callback to notify application layer when the frame
1169 * has been sent or discarded
1170 * @param frqcb_cls closure for frqcb
1171 * @param rrcb callback used by the application layer to notify the
1172 * application when the frame has been sent or discarded.
1173 * frqcb will call it
1174 * @param rrcb_cls closure for rrcb
1175 * @return double linked list of SPDYF_Response_Queue structures: one or
1176 * more frames are returned based on the size of the data
1177 */
1178struct SPDYF_Response_Queue *
1179SPDYF_response_queue_create(bool is_data,
1180 void *data,
1181 size_t data_size,
1182 struct SPDY_Response *response,
1183 struct SPDYF_Stream *stream,
1184 bool closestream,
1185 SPDYF_ResponseQueueResultCallback frqcb,
1186 void *frqcb_cls,
1187 SPDY_ResponseResultCallback rrcb,
1188 void *rrcb_cls);
1189
1190
1191/**
1192 * Destroys SPDYF_Response_Queue structure and whatever is in it.
1193 *
1194 * @param response_queue to destroy
1195 */
1196void
1197SPDYF_response_queue_destroy(struct SPDYF_Response_Queue *response_queue);
1198
1199
1200/**
1201 * Checks if the container is empty, i.e. created but no values were
1202 * added to it.
1203 *
1204 * @param container
1205 * @return SPDY_YES if empty
1206 * SPDY_NO if not
1207 */
1208int
1209SPDYF_name_value_is_empty(struct SPDY_NameValue *container);
1210
1211
1212/**
1213 * Transforms raw binary decomressed stream of headers
1214 * into SPDY_NameValue, containing all of the headers and values.
1215 *
1216 * @param stream that is to be transformed
1217 * @param size length of the stream
1218 * @param container will contain the newly created SPDY_NameValue
1219 * container. Should point to NULL.
1220 * @return SPDY_YES on success
1221 * SPDY_NO on memory error
1222 * SPDY_INPUT_ERROR if the provided stream is not valid
1223 */
1224int
1225SPDYF_name_value_from_stream(void *stream,
1226 size_t size,
1227 struct SPDY_NameValue ** container);
1228
1229
1230/**
1231 * Transforms array of objects of name/values tuples, containing HTTP
1232 * headers, into raw binary stream. The resulting stream is ready to
1233 * be compressed and sent.
1234 *
1235 * @param container one or more SPDY_NameValue objects. Each object
1236 * contains multiple number of name/value tuples.
1237 * @param num_containers length of the array
1238 * @param stream will contain the resulting stream. Should point to NULL.
1239 * @return length of stream or value less than 0 indicating error
1240 */
1241ssize_t
1242SPDYF_name_value_to_stream(struct SPDY_NameValue * container[],
1243 int num_containers,
1244 void **stream);
1245
1246#endif
diff --git a/src/spdy2http/Makefile.am b/src/spdy2http/Makefile.am
deleted file mode 100644
index f7432a9b..00000000
--- a/src/spdy2http/Makefile.am
+++ /dev/null
@@ -1,29 +0,0 @@
1# This Makefile.am is in the public domain
2SUBDIRS = .
3
4AM_CFLAGS =
5
6if USE_COVERAGE
7 AM_CFLAGS += -fprofile-arcs -ftest-coverage
8endif
9
10AM_CPPFLAGS = \
11 -I$(top_srcdir) \
12 -I$(top_srcdir)/src/include \
13 -I$(top_srcdir)/src/applicationlayer \
14 -DDATA_DIR=\"$(top_srcdir)/src/datadir/\" \
15 $(LIBCURL_CPPFLAGS)
16
17if !HAVE_W32
18PERF_GET_CONCURRENT=perf_get_concurrent
19endif
20
21bin_PROGRAMS = \
22 microspdy2http
23
24microspdy2http_SOURCES = \
25 proxy.c
26microspdy2http_LDADD = \
27 $(top_builddir)/src/microspdy/libmicrospdy.la \
28 -lz \
29 $(LIBCURL)
diff --git a/src/spdy2http/proxy.c b/src/spdy2http/proxy.c
deleted file mode 100644
index 02144ad9..00000000
--- a/src/spdy2http/proxy.c
+++ /dev/null
@@ -1,1411 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2013 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 proxy.c
21 * @brief Translates incoming SPDY requests to http server on localhost.
22 * Uses libcurl.
23 * No error handling for curl requests.
24 * TODO:
25 * - test all options!
26 * - don't abort on lack of memory
27 * - Correct recapitalizetion of header names before giving the headers
28 * to curl.
29 * - curl does not close sockets when connection is closed and no
30 * new sockets are opened (they stay in CLOSE_WAIT)
31 * - add '/' when a user requests http://example.com . Now this is a bad
32 * request
33 * - curl returns 0 or 1 ms for timeout even when nothing will be done;
34 * thus the loop uses CPU for nothing
35 * @author Andrey Uzunov
36 */
37
38#include "platform.h"
39#include <unistd.h>
40#include <stdlib.h>
41#include <stdint.h>
42#include <stdbool.h>
43#include <string.h>
44#include <stdio.h>
45#include <ctype.h>
46#include <errno.h>
47#include "microspdy.h"
48#include <curl/curl.h>
49#include <assert.h>
50#include <getopt.h>
51#include <regex.h>
52
53#define ERROR_RESPONSE "502 Bad Gateway"
54
55
56struct global_options
57{
58 char *http_backend;
59 char *cert;
60 char *cert_key;
61 char *listen_host;
62 unsigned int timeout;
63 uint16_t listen_port;
64 bool verbose;
65 bool curl_verbose;
66 bool transparent;
67 bool http10;
68 bool notls;
69 bool nodelay;
70 bool ipv4;
71 bool ipv6;
72} glob_opt;
73
74
75struct URI
76{
77 char * full_uri;
78 char * scheme;
79 char * host_and_port;
80 //char * host_and_port_for_connecting;
81 char * host;
82 char * path;
83 char * path_and_more;
84 char * query;
85 char * fragment;
86 uint16_t port;
87};
88
89
90#define PRINT_INFO(msg) do{\
91 fprintf(stdout, "%i:%s\n", __LINE__, msg);\
92 fflush(stdout);\
93 }\
94 while(0)
95
96
97#define PRINT_INFO2(fmt, ...) do{\
98 fprintf(stdout, "%i\n", __LINE__);\
99 fprintf(stdout, fmt,##__VA_ARGS__);\
100 fprintf(stdout, "\n");\
101 fflush(stdout);\
102 }\
103 while(0)
104
105
106#define PRINT_VERBOSE(msg) do{\
107 if(glob_opt.verbose){\
108 fprintf(stdout, "%i:%s\n", __LINE__, msg);\
109 fflush(stdout);\
110 }\
111 }\
112 while(0)
113
114
115#define PRINT_VERBOSE2(fmt, ...) do{\
116 if(glob_opt.verbose){\
117 fprintf(stdout, "%i\n", __LINE__);\
118 fprintf(stdout, fmt,##__VA_ARGS__);\
119 fprintf(stdout, "\n");\
120 fflush(stdout);\
121 }\
122 }\
123 while(0)
124
125
126#define CURL_SETOPT(handle, opt, val) do{\
127 int ret; \
128 if(CURLE_OK != (ret = curl_easy_setopt(handle, opt, val))) \
129 { \
130 PRINT_INFO2("curl_easy_setopt failed (%i = %i)", opt, ret); \
131 abort(); \
132 } \
133 }\
134 while(0)
135
136
137#define DIE(msg) do{\
138 printf("FATAL ERROR (line %i): %s\n", __LINE__, msg);\
139 fflush(stdout);\
140 exit(EXIT_FAILURE);\
141 }\
142 while(0)
143
144
145static int loop = 1;
146
147static CURLM *multi_handle;
148
149static int still_running = 0; /* keep number of running handles */
150
151static regex_t uri_preg;
152
153static bool call_spdy_run;
154static bool call_curl_run;
155
156int debug_num_curls;
157
158
159struct Proxy
160{
161 char *url;
162 struct SPDY_Request *request;
163 struct SPDY_Response *response;
164 CURL *curl_handle;
165 struct curl_slist *curl_headers;
166 struct SPDY_NameValue *headers;
167 char *version;
168 char *status_msg;
169 void *http_body;
170 void *received_body;
171 bool *session_alive;
172 size_t http_body_size;
173 size_t received_body_size;
174 //ssize_t length;
175 int status;
176 //bool done;
177 bool receiving_done;
178 bool is_curl_read_paused;
179 bool is_with_body_data;
180 //bool error;
181 bool curl_done;
182 bool curl_error;
183 bool spdy_done;
184 bool spdy_error;
185};
186
187
188static void
189free_uri(struct URI * uri)
190{
191 if(NULL != uri)
192 {
193 free(uri->full_uri);
194 free(uri->scheme);
195 free(uri->host_and_port);
196 //free(uri->host_and_port_for_connecting);
197 free(uri->host);
198 free(uri->path);
199 free(uri->path_and_more);
200 free(uri->query);
201 free(uri->fragment);
202 uri->port = 0;
203 free(uri);
204 }
205}
206
207
208static int
209init_parse_uri(regex_t * preg)
210{
211 // RFC 2396
212 // ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
213 /*
214 scheme = $2
215 authority = $4
216 path = $5
217 query = $7
218 fragment = $9
219 */
220
221 return regcomp(preg, "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?", REG_EXTENDED);
222}
223
224
225static void
226deinit_parse_uri(regex_t * preg)
227{
228 regfree(preg);
229}
230
231
232static int
233parse_uri(regex_t * preg, const char * full_uri, struct URI ** uri)
234{
235 //TODO memeory checks
236 int ret;
237 char *colon;
238 long long port;
239 size_t nmatch = 10;
240 regmatch_t pmatch[10];
241
242 if (0 != (ret = regexec(preg, full_uri, nmatch, pmatch, 0)))
243 return ret;
244
245 *uri = malloc(sizeof(struct URI));
246 if(NULL == *uri)
247 return -200;
248
249 (*uri)->full_uri = strdup(full_uri);
250
251 asprintf(&((*uri)->scheme),
252 "%.*s",
253 (int) (pmatch[2].rm_eo - pmatch[2].rm_so),
254 &full_uri[pmatch[2].rm_so]);
255 asprintf(&((*uri)->host_and_port), "%.*s",
256 (int) (pmatch[4].rm_eo - pmatch[4].rm_so),
257 &full_uri[pmatch[4].rm_so]);
258 asprintf(&((*uri)->path),
259 "%.*s",
260 (int) (pmatch[5].rm_eo - pmatch[5].rm_so),
261 &full_uri[pmatch[5].rm_so]);
262 asprintf(&((*uri)->path_and_more),
263 "%.*s",
264 (int) (pmatch[9].rm_eo - pmatch[5].rm_so),
265 &full_uri[pmatch[5].rm_so]);
266 asprintf(&((*uri)->query),
267 "%.*s",
268 (int) (pmatch[7].rm_eo - pmatch[7].rm_so),
269 &full_uri[pmatch[7].rm_so]);
270 asprintf(&((*uri)->fragment),
271 "%.*s",
272 (int) (pmatch[9].rm_eo - pmatch[9].rm_so),
273 &full_uri[pmatch[9].rm_so]);
274
275 colon = strrchr((*uri)->host_and_port, ':');
276 if(NULL == colon)
277 {
278 (*uri)->host = strdup((*uri)->host_and_port);
279 /*if(0 == strcasecmp("http", uri->scheme))
280 {
281 uri->port = 80;
282 asprintf(&(uri->host_and_port_for_connecting), "%s:80", uri->host_and_port);
283 }
284 else if(0 == strcasecmp("https", uri->scheme))
285 {
286 uri->port = 443;
287 asprintf(&(uri->host_and_port_for_connecting), "%s:443", uri->host_and_port);
288 }
289 else
290 {
291 PRINT_INFO("no standard scheme!");
292 */(*uri)->port = 0;
293 /*uri->host_and_port_for_connecting = strdup(uri->host_and_port);
294 }*/
295 return 0;
296 }
297
298 port = atoi(colon + 1);
299 if(port<1 || port >= 256 * 256)
300 {
301 free_uri(*uri);
302 return -100;
303 }
304 (*uri)->port = port;
305 asprintf(&((*uri)->host), "%.*s", (int)(colon - (*uri)->host_and_port), (*uri)->host_and_port);
306
307 return 0;
308}
309
310
311static bool
312store_in_buffer(const void *src, size_t src_size, void **dst, size_t *dst_size)
313{
314 if(0 == src_size)
315 return true;
316
317 if(NULL == *dst)
318 *dst = malloc(src_size);
319 else
320 *dst = realloc(*dst, src_size + *dst_size);
321 if(NULL == *dst)
322 return false;
323
324 memcpy(*dst + *dst_size, src, src_size);
325 *dst_size += src_size;
326
327 return true;
328}
329
330
331static ssize_t
332get_from_buffer(void **src, size_t *src_size, void *dst, size_t max_size)
333{
334 size_t ret;
335 void *newbody;
336
337 if(max_size >= *src_size)
338 {
339 ret = *src_size;
340 newbody = NULL;
341 }
342 else
343 {
344 ret = max_size;
345 if(NULL == (newbody = malloc(*src_size - max_size)))
346 return -1;
347 memcpy(newbody, *src + ret, *src_size - ret);
348 }
349 memcpy(dst, *src, ret);
350 free(*src);
351 *src = newbody;
352 *src_size -= ret;
353
354 return ret;
355}
356
357
358static void
359catch_signal(int signal)
360{
361 (void)signal;
362
363 loop = 0;
364}
365
366static void
367new_session_cb (void * cls,
368 struct SPDY_Session * session)
369{
370 (void)cls;
371
372 bool *session_alive;
373
374 PRINT_VERBOSE("new session");
375 //TODO clean this memory
376 if(NULL == (session_alive = malloc(sizeof(bool))))
377 {
378 DIE("no memory");
379 }
380 *session_alive = true;
381 SPDY_set_cls_to_session(session,
382 session_alive);
383}
384
385static void
386session_closed_cb (void * cls,
387 struct SPDY_Session * session,
388 int by_client)
389{
390 (void)cls;
391
392 bool *session_alive;
393
394 PRINT_VERBOSE2("session closed; by client: %i", by_client);
395
396 session_alive = SPDY_get_cls_from_session(session);
397 assert(NULL != session_alive);
398
399 *session_alive = false;
400}
401
402
403static int
404spdy_post_data_cb (void * cls,
405 struct SPDY_Request *request,
406 const void * buf,
407 size_t size,
408 bool more)
409{
410 (void)cls;
411 int ret;
412 struct Proxy *proxy = (struct Proxy *)SPDY_get_cls_from_request(request);
413
414 if(!store_in_buffer(buf, size, &proxy->received_body, &proxy->received_body_size))
415 {
416 PRINT_INFO("not enough memory (malloc/realloc returned NULL)");
417 return 0;
418 }
419
420 proxy->receiving_done = !more;
421
422 PRINT_VERBOSE2("POST bytes from SPDY: %zu", size);
423
424 call_curl_run = true;
425
426 if(proxy->is_curl_read_paused)
427 {
428 if(CURLE_OK != (ret = curl_easy_pause(proxy->curl_handle, CURLPAUSE_CONT)))
429 {
430 PRINT_INFO2("curl_easy_pause returned %i", ret);
431 abort();
432 }
433 PRINT_VERBOSE("curl_read_cb pause resumed");
434 }
435
436 return SPDY_YES;
437}
438
439
440ssize_t
441response_callback (void *cls,
442 void *buffer,
443 size_t max,
444 bool *more)
445{
446 ssize_t ret;
447 struct Proxy *proxy = (struct Proxy *)cls;
448
449 *more = true;
450
451 assert(!proxy->spdy_error);
452
453 if(proxy->curl_error)
454 {
455 PRINT_VERBOSE("tell spdy about the error");
456 return -1;
457 }
458
459 if(!proxy->http_body_size)//nothing to write now
460 {
461 PRINT_VERBOSE("nothing to write now");
462 if(proxy->curl_done || proxy->curl_error) *more = false;
463 return 0;
464 }
465
466 ret = get_from_buffer(&(proxy->http_body), &(proxy->http_body_size), buffer, max);
467 if(ret < 0)
468 {
469 PRINT_INFO("no memory");
470 //TODO error?
471 return -1;
472 }
473
474 if((proxy->curl_done || proxy->curl_error) && 0 == proxy->http_body_size) *more = false;
475
476 PRINT_VERBOSE2("given bytes to microspdy: %zd", ret);
477
478 return ret;
479}
480
481
482static void
483cleanup(struct Proxy *proxy)
484{
485 int ret;
486
487 //fprintf(stderr, "free proxy for %s\n", proxy->url);
488
489 if(CURLM_OK != (ret = curl_multi_remove_handle(multi_handle, proxy->curl_handle)))
490 {
491 PRINT_INFO2("curl_multi_remove_handle failed (%i)", ret);
492 DIE("bug in cleanup");
493 }
494 debug_num_curls--;
495 //TODO bug on ku6.com or amazon.cn
496 // after curl_multi_remove_handle returned CURLM_BAD_EASY_HANDLE
497 curl_slist_free_all(proxy->curl_headers);
498 curl_easy_cleanup(proxy->curl_handle);
499
500 free(proxy->url);
501 free(proxy);
502}
503
504
505static void
506response_done_callback(void *cls,
507 struct SPDY_Response *response,
508 struct SPDY_Request *request,
509 enum SPDY_RESPONSE_RESULT status,
510 bool streamopened)
511{
512 (void)streamopened;
513 struct Proxy *proxy = (struct Proxy *)cls;
514
515 if(SPDY_RESPONSE_RESULT_SUCCESS != status)
516 {
517 free(proxy->http_body);
518 proxy->http_body = NULL;
519 proxy->spdy_error = true;
520 }
521 cleanup(proxy);
522 SPDY_destroy_request(request);
523 SPDY_destroy_response(response);
524}
525
526
527static size_t
528curl_header_cb(void *ptr, size_t size, size_t nmemb, void *userp)
529{
530 size_t realsize = size * nmemb;
531 struct Proxy *proxy = (struct Proxy *)userp;
532 char *line = (char *)ptr;
533 char *name;
534 char *value;
535 char *status;
536 unsigned int i;
537 unsigned int pos;
538 int ret;
539 int num_values;
540 const char * const * values;
541 bool abort_it;
542
543 //printf("curl_header_cb %s\n", line);
544 if(!*(proxy->session_alive))
545 {
546 PRINT_VERBOSE("headers received, but session is dead");
547 proxy->spdy_error = true;
548 proxy->curl_error = true;
549 return 0;
550 }
551
552 //trailer
553 if(NULL != proxy->response) return 0;
554
555 if('\r' == line[0] || '\n' == line[0])
556 {
557 //all headers were already handled; prepare spdy frames
558 if(NULL == (proxy->response = SPDY_build_response_with_callback(proxy->status,
559 proxy->status_msg,
560 proxy->version,
561 proxy->headers,
562 &response_callback,
563 proxy,
564 0)))
565 //256)))
566 DIE("no response");
567
568 SPDY_name_value_destroy(proxy->headers);
569 proxy->headers = NULL;
570 free(proxy->status_msg);
571 proxy->status_msg = NULL;
572 free(proxy->version);
573 proxy->version = NULL;
574
575 if(SPDY_YES != SPDY_queue_response(proxy->request,
576 proxy->response,
577 true,
578 false,
579 &response_done_callback,
580 proxy))
581 {
582 //DIE("no queue");
583 //TODO right?
584 proxy->spdy_error = true;
585 proxy->curl_error = true;
586 PRINT_VERBOSE2("no queue in curl_header_cb for %s", proxy->url);
587 SPDY_destroy_response(proxy->response);
588 proxy->response = NULL;
589 return 0;
590 }
591
592 call_spdy_run = true;
593
594 return realsize;
595 }
596
597 pos = 0;
598 if(NULL == proxy->version)
599 {
600 //first line from headers
601 //version
602 for(i=pos; i<realsize && ' '!=line[i]; ++i);
603 if(i == realsize)
604 DIE("error on parsing headers");
605 if(NULL == (proxy->version = strndup(line, i - pos)))
606 DIE("No memory");
607 pos = i+1;
608
609 //status (number)
610 for(i=pos; i<realsize && ' '!=line[i] && '\r'!=line[i] && '\n'!=line[i]; ++i);
611 if(NULL == (status = strndup(&(line[pos]), i - pos)))
612 DIE("No memory");
613 proxy->status = atoi(status);
614 free(status);
615 if(i<realsize && '\r'!=line[i] && '\n'!=line[i])
616 {
617 //status (message)
618 pos = i+1;
619 for(i=pos; i<realsize && '\r'!=line[i] && '\n'!=line[i]; ++i);
620 if(NULL == (proxy->status_msg = strndup(&(line[pos]), i - pos)))
621 DIE("No memory");
622 }
623 PRINT_VERBOSE2("Header line received '%s' '%i' '%s' ", proxy->version, proxy->status, proxy->status_msg);
624 return realsize;
625 }
626
627 //other lines
628 //header name
629 for(i=pos; i<realsize && ':'!=line[i] && '\r'!=line[i] && '\n'!=line[i]; ++i)
630 line[i] = tolower(line[i]); //spdy requires lower case
631 if(NULL == (name = strndup(line, i - pos)))
632 DIE("No memory");
633 if(0 == strcmp(SPDY_HTTP_HEADER_CONNECTION, name)
634 || 0 == strcmp(SPDY_HTTP_HEADER_KEEP_ALIVE, name)
635 || 0 == strcmp(SPDY_HTTP_HEADER_TRANSFER_ENCODING, name)
636 )
637 {
638 //forbidden in spdy, ignore
639 free(name);
640 return realsize;
641 }
642 if(i == realsize || '\r'==line[i] || '\n'==line[i])
643 {
644 //no value. is it possible?
645 if(SPDY_YES != SPDY_name_value_add(proxy->headers, name, ""))
646 DIE("SPDY_name_value_add failed");
647 return realsize;
648 }
649
650 //header value
651 pos = i+1;
652 while(pos<realsize && isspace(line[pos])) ++pos; //remove leading space
653 for(i=pos; i<realsize && '\r'!=line[i] && '\n'!=line[i]; ++i);
654 if(NULL == (value = strndup(&(line[pos]), i - pos)))
655 DIE("No memory");
656 PRINT_VERBOSE2("Adding header: '%s': '%s'", name, value);
657 if(SPDY_YES != (ret = SPDY_name_value_add(proxy->headers, name, value)))
658 {
659 abort_it=true;
660 if(NULL != (values = SPDY_name_value_lookup(proxy->headers, name, &num_values)))
661 for(i=0; i<(unsigned int)num_values; ++i)
662 if(0 == strcasecmp(value, values[i]))
663 {
664 abort_it=false;
665 PRINT_VERBOSE2("header appears more than once with same value '%s: %s'", name, value);
666 break;
667 }
668
669 if(abort_it)
670 {
671 PRINT_INFO2("SPDY_name_value_add failed (%i) for '%s'", ret, name);
672 abort();
673 }
674 }
675 free(name);
676 free(value);
677
678 return realsize;
679}
680
681
682static size_t
683curl_write_cb(void *contents, size_t size, size_t nmemb, void *userp)
684{
685 size_t realsize = size * nmemb;
686 struct Proxy *proxy = (struct Proxy *)userp;
687
688 //printf("curl_write_cb %i\n", realsize);
689 if(!*(proxy->session_alive))
690 {
691 PRINT_VERBOSE("data received, but session is dead");
692 proxy->spdy_error = true;
693 proxy->curl_error = true;
694 return 0;
695 }
696
697 if(!store_in_buffer(contents, realsize, &proxy->http_body, &proxy->http_body_size))
698 {
699 PRINT_INFO("not enough memory (malloc/realloc returned NULL)");
700 proxy->curl_error = true;
701 return 0;
702 }
703 /*
704 if(NULL == proxy->http_body)
705 proxy->http_body = malloc(realsize);
706 else
707 proxy->http_body = realloc(proxy->http_body, proxy->http_body_size + realsize);
708 if(NULL == proxy->http_body)
709 {
710 PRINT_INFO("not enough memory (realloc returned NULL)");
711 return 0;
712 }
713
714 memcpy(proxy->http_body + proxy->http_body_size, contents, realsize);
715 proxy->http_body_size += realsize;
716 */
717
718 PRINT_VERBOSE2("received bytes from curl: %zu", realsize);
719
720 call_spdy_run = true;
721
722 return realsize;
723}
724
725
726static size_t
727curl_read_cb(void *ptr, size_t size, size_t nmemb, void *userp)
728{
729 ssize_t ret;
730 size_t max = size * nmemb;
731 struct Proxy *proxy = (struct Proxy *)userp;
732 //void *newbody;
733
734
735 if((proxy->receiving_done && !proxy->received_body_size) || !proxy->is_with_body_data || max < 1)
736 {
737 PRINT_VERBOSE("curl_read_cb last call");
738 return 0;
739 }
740
741 if(!*(proxy->session_alive))
742 {
743 PRINT_VERBOSE("POST is still being sent, but session is dead");
744 return CURL_READFUNC_ABORT;
745 }
746
747 if(!proxy->received_body_size)//nothing to write now
748 {
749 PRINT_VERBOSE("curl_read_cb called paused");
750 proxy->is_curl_read_paused = true;
751 return CURL_READFUNC_PAUSE;//TODO curl pause should be used
752 }
753
754 ret = get_from_buffer(&(proxy->received_body), &(proxy->received_body_size), ptr, max);
755 if(ret < 0)
756 {
757 PRINT_INFO("no memory");
758 return CURL_READFUNC_ABORT;
759 }
760
761 /*
762 if(max >= proxy->received_body_size)
763 {
764 ret = proxy->received_body_size;
765 newbody = NULL;
766 }
767 else
768 {
769 ret = max;
770 if(NULL == (newbody = malloc(proxy->received_body_size - max)))
771 {
772 PRINT_INFO("no memory");
773 return CURL_READFUNC_ABORT;
774 }
775 memcpy(newbody, proxy->received_body + max, proxy->received_body_size - max);
776 }
777 memcpy(ptr, proxy->received_body, ret);
778 free(proxy->received_body);
779 proxy->received_body = newbody;
780 proxy->received_body_size -= ret;
781 * */
782
783 PRINT_VERBOSE2("given POST bytes to curl: %zd", ret);
784
785 return ret;
786}
787
788
789static int
790iterate_cb (void *cls, const char *name, const char * const * value, int num_values)
791{
792 struct Proxy *proxy = (struct Proxy *)cls;
793 struct curl_slist **curl_headers = (&(proxy->curl_headers));
794 char *line;
795 int line_len = strlen(name) + 3; //+ ": \0"
796 int i;
797
798 for(i=0; i<num_values; ++i)
799 {
800 if(i) line_len += 2; //", "
801 line_len += strlen(value[i]);
802 }
803
804 if(NULL == (line = malloc(line_len)))//no recovery
805 DIE("No memory");
806 line[0] = 0;
807
808 strcat(line, name);
809 strcat(line, ": ");
810 //all spdy header names are lower case;
811 //for simplicity here we just capitalize the first letter
812 line[0] = toupper(line[0]);
813
814 for(i=0; i<num_values; ++i)
815 {
816 if(i) strcat(line, ", ");
817 PRINT_VERBOSE2("Adding request header: '%s' len %ld", value[i], strlen(value[i]));
818 strcat(line, value[i]);
819 }
820 PRINT_VERBOSE2("Adding request header: '%s'", line);
821 if(NULL == (*curl_headers = curl_slist_append(*curl_headers, line)))
822 DIE("curl_slist_append failed");
823 free(line);
824
825 return SPDY_YES;
826}
827
828
829static void
830standard_request_handler(void *cls,
831 struct SPDY_Request * request,
832 uint8_t priority,
833 const char *method,
834 const char *path,
835 const char *version,
836 const char *host,
837 const char *scheme,
838 struct SPDY_NameValue * headers,
839 bool more)
840{
841 (void)cls;
842 (void)priority;
843 (void)host;
844 (void)scheme;
845
846 struct Proxy *proxy;
847 int ret;
848 struct URI *uri;
849 struct SPDY_Session *session;
850
851 proxy = SPDY_get_cls_from_request(request);
852 if(NULL != proxy)
853 {
854 //ignore trailers or more headers
855 return;
856 }
857
858 PRINT_VERBOSE2("received request for '%s %s %s'\n", method, path, version);
859
860 //TODO not freed once in a while
861 if(NULL == (proxy = malloc(sizeof(struct Proxy))))
862 DIE("No memory");
863 memset(proxy, 0, sizeof(struct Proxy));
864
865 //fprintf(stderr, "new proxy for %s\n", path);
866
867 session = SPDY_get_session_for_request(request);
868 assert(NULL != session);
869 proxy->session_alive = SPDY_get_cls_from_session(session);
870 assert(NULL != proxy->session_alive);
871
872 SPDY_set_cls_to_request(request, proxy);
873
874 proxy->request = request;
875 proxy->is_with_body_data = more;
876 if(NULL == (proxy->headers = SPDY_name_value_create()))
877 DIE("No memory");
878
879 if(glob_opt.transparent)
880 {
881 if(NULL != glob_opt.http_backend) //use always same host
882 ret = asprintf(&(proxy->url),"%s://%s%s", scheme, glob_opt.http_backend, path);
883 else //use host header
884 ret = asprintf(&(proxy->url),"%s://%s%s", scheme, host, path);
885 if(-1 == ret)
886 DIE("No memory");
887
888 ret = parse_uri(&uri_preg, proxy->url, &uri);
889 if(ret != 0)
890 DIE("parsing built uri failed");
891 }
892 else
893 {
894 ret = parse_uri(&uri_preg, path, &uri);
895 PRINT_INFO2("path %s '%s' '%s'", path, uri->scheme, uri->host);
896 if(ret != 0 || !strlen(uri->scheme) || !strlen(uri->host))
897 DIE("parsing received uri failed");
898
899 if(NULL != glob_opt.http_backend) //use backend host
900 {
901 ret = asprintf(&(proxy->url),"%s://%s%s", uri->scheme, glob_opt.http_backend, uri->path_and_more);
902 if(-1 == ret)
903 DIE("No memory");
904 }
905 else //use request path
906 if(NULL == (proxy->url = strdup(path)))
907 DIE("No memory");
908 }
909
910 free_uri(uri);
911
912 PRINT_VERBOSE2("curl will request '%s'", proxy->url);
913
914 SPDY_name_value_iterate(headers, &iterate_cb, proxy);
915
916 if(NULL == (proxy->curl_handle = curl_easy_init()))
917 {
918 PRINT_INFO("curl_easy_init failed");
919 abort();
920 }
921
922 if(glob_opt.curl_verbose)
923 CURL_SETOPT(proxy->curl_handle, CURLOPT_VERBOSE, 1);
924
925 if(0 == strcmp(SPDY_HTTP_METHOD_POST,method))
926 {
927 if(NULL == (proxy->curl_headers = curl_slist_append(proxy->curl_headers, "Expect:")))
928 DIE("curl_slist_append failed");
929 CURL_SETOPT(proxy->curl_handle, CURLOPT_POST, 1);
930 CURL_SETOPT(proxy->curl_handle, CURLOPT_READFUNCTION, curl_read_cb);
931 CURL_SETOPT(proxy->curl_handle, CURLOPT_READDATA, proxy);
932 }
933
934 if(glob_opt.timeout)
935 CURL_SETOPT(proxy->curl_handle, CURLOPT_TIMEOUT, glob_opt.timeout);
936 CURL_SETOPT(proxy->curl_handle, CURLOPT_URL, proxy->url);
937 if(glob_opt.http10)
938 CURL_SETOPT(proxy->curl_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
939 CURL_SETOPT(proxy->curl_handle, CURLOPT_WRITEFUNCTION, curl_write_cb);
940 CURL_SETOPT(proxy->curl_handle, CURLOPT_WRITEDATA, proxy);
941 CURL_SETOPT(proxy->curl_handle, CURLOPT_HEADERFUNCTION, curl_header_cb);
942 CURL_SETOPT(proxy->curl_handle, CURLOPT_HEADERDATA, proxy);
943 CURL_SETOPT(proxy->curl_handle, CURLOPT_PRIVATE, proxy);
944 CURL_SETOPT(proxy->curl_handle, CURLOPT_HTTPHEADER, proxy->curl_headers);
945 CURL_SETOPT(proxy->curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);//TODO
946 CURL_SETOPT(proxy->curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);
947 if(glob_opt.ipv4 && !glob_opt.ipv6)
948 CURL_SETOPT(proxy->curl_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
949 else if(glob_opt.ipv6 && !glob_opt.ipv4)
950 CURL_SETOPT(proxy->curl_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6);
951
952 if(CURLM_OK != (ret = curl_multi_add_handle(multi_handle, proxy->curl_handle)))
953 {
954 PRINT_INFO2("curl_multi_add_handle failed (%i)", ret);
955 abort();
956 }
957 debug_num_curls++;
958
959 //~5ms additional latency for calling this
960 if(CURLM_OK != (ret = curl_multi_perform(multi_handle, &still_running))
961 && CURLM_CALL_MULTI_PERFORM != ret)
962 {
963 PRINT_INFO2("curl_multi_perform failed (%i)", ret);
964 abort();
965 }
966
967 call_curl_run = true;
968}
969
970
971static int
972run ()
973{
974 unsigned long long timeoutlong = 0;
975 unsigned long long timeout_spdy = 0;
976 long timeout_curl = -1;
977 struct timeval timeout;
978 int ret;
979 int ret_curl;
980 int ret_spdy;
981 fd_set rs;
982 fd_set ws;
983 fd_set es;
984 int maxfd = -1;
985 int maxfd_curl = -1;
986 struct SPDY_Daemon *daemon;
987 CURLMsg *msg;
988 int msgs_left;
989 struct Proxy *proxy;
990 struct sockaddr_in *addr;
991 struct addrinfo hints;
992 char service[NI_MAXSERV];
993 struct addrinfo *gai;
994 enum SPDY_IO_SUBSYSTEM io = glob_opt.notls ? SPDY_IO_SUBSYSTEM_RAW : SPDY_IO_SUBSYSTEM_OPENSSL;
995 enum SPDY_DAEMON_FLAG flags = SPDY_DAEMON_FLAG_NO;
996 //struct SPDY_Response *error_response;
997 char *curl_private;
998
999 signal(SIGPIPE, SIG_IGN);
1000
1001 if (signal(SIGINT, catch_signal) == SIG_ERR)
1002 PRINT_VERBOSE("signal failed");
1003
1004 srand(time(NULL));
1005 if(init_parse_uri(&uri_preg))
1006 DIE("Regexp compilation failed");
1007
1008 SPDY_init();
1009
1010 if(glob_opt.nodelay)
1011 flags |= SPDY_DAEMON_FLAG_NO_DELAY;
1012
1013 if(NULL == glob_opt.listen_host)
1014 {
1015 daemon = SPDY_start_daemon(glob_opt.listen_port,
1016 glob_opt.cert,
1017 glob_opt.cert_key,
1018 &new_session_cb,
1019 &session_closed_cb,
1020 &standard_request_handler,
1021 &spdy_post_data_cb,
1022 NULL,
1023 SPDY_DAEMON_OPTION_SESSION_TIMEOUT,
1024 1800,
1025 SPDY_DAEMON_OPTION_IO_SUBSYSTEM,
1026 io,
1027 SPDY_DAEMON_OPTION_FLAGS,
1028 flags,
1029 SPDY_DAEMON_OPTION_END);
1030 }
1031 else
1032 {
1033 snprintf (service, sizeof(service), "%u", glob_opt.listen_port);
1034 memset (&hints, 0, sizeof(struct addrinfo));
1035 hints.ai_family = AF_INET;
1036 hints.ai_socktype = SOCK_STREAM;
1037
1038 ret = getaddrinfo(glob_opt.listen_host, service, &hints, &gai);
1039 if(ret != 0)
1040 DIE("problem with specified host");
1041
1042 addr = (struct sockaddr_in *) gai->ai_addr;
1043
1044 daemon = SPDY_start_daemon(0,
1045 glob_opt.cert,
1046 glob_opt.cert_key,
1047 &new_session_cb,
1048 &session_closed_cb,
1049 &standard_request_handler,
1050 &spdy_post_data_cb,
1051 NULL,
1052 SPDY_DAEMON_OPTION_SESSION_TIMEOUT,
1053 1800,
1054 SPDY_DAEMON_OPTION_IO_SUBSYSTEM,
1055 io,
1056 SPDY_DAEMON_OPTION_FLAGS,
1057 flags,
1058 SPDY_DAEMON_OPTION_SOCK_ADDR,
1059 addr,
1060 //SPDY_DAEMON_OPTION_MAX_NUM_FRAMES,
1061 //1,
1062 SPDY_DAEMON_OPTION_END);
1063 }
1064
1065 if(NULL==daemon){
1066 printf("no daemon\n");
1067 return 1;
1068 }
1069
1070 multi_handle = curl_multi_init();
1071 if(NULL==multi_handle)
1072 DIE("no multi_handle");
1073
1074 timeout.tv_usec = 0;
1075
1076 do
1077 {
1078 FD_ZERO(&rs);
1079 FD_ZERO(&ws);
1080 FD_ZERO(&es);
1081
1082 PRINT_VERBOSE2("num curls %i", debug_num_curls);
1083
1084 ret_spdy = SPDY_get_timeout(daemon, &timeout_spdy);
1085 if(SPDY_NO == ret_spdy || timeout_spdy > 5000)
1086 timeoutlong = 5000;
1087 else
1088 timeoutlong = timeout_spdy;
1089 PRINT_VERBOSE2("SPDY timeout %lld; %i", timeout_spdy, ret_spdy);
1090
1091 if(CURLM_OK != (ret_curl = curl_multi_timeout(multi_handle, &timeout_curl)))
1092 {
1093 PRINT_VERBOSE2("curl_multi_timeout failed (%i)", ret_curl);
1094 //curl_timeo = timeoutlong;
1095 }
1096 else if(timeout_curl >= 0 && timeoutlong > (unsigned long)timeout_curl)
1097 timeoutlong = (unsigned long)timeout_curl;
1098
1099 PRINT_VERBOSE2("curl timeout %ld", timeout_curl);
1100
1101 timeout.tv_sec = timeoutlong / 1000;
1102 timeout.tv_usec = (timeoutlong % 1000) * 1000;
1103
1104 maxfd = SPDY_get_fdset (daemon,
1105 &rs,
1106 &ws,
1107 &es);
1108 assert(-1 != maxfd);
1109
1110 if(CURLM_OK != (ret = curl_multi_fdset(multi_handle, &rs,
1111 &ws,
1112 &es, &maxfd_curl)))
1113 {
1114 PRINT_INFO2("curl_multi_fdset failed (%i)", ret);
1115 abort();
1116 }
1117
1118 if(maxfd_curl > maxfd)
1119 maxfd = maxfd_curl;
1120
1121 PRINT_VERBOSE2("timeout before %lld %lld", (unsigned long long)timeout.tv_sec, (unsigned long long)timeout.tv_usec);
1122 ret = select(maxfd+1, &rs, &ws, &es, &timeout);
1123 PRINT_VERBOSE2("timeout after %lld %lld; ret is %i", (unsigned long long)timeout.tv_sec, (unsigned long long)timeout.tv_usec, ret);
1124
1125 /*switch(ret) {
1126 case -1:
1127 PRINT_INFO2("select error: %i", errno);
1128 break;
1129 case 0:
1130 break;
1131 default:*/
1132
1133 //the second part should not happen with current implementation
1134 if(ret > 0 || (SPDY_YES == ret_spdy && 0 == timeout_spdy))
1135 {
1136 PRINT_VERBOSE("run spdy");
1137 SPDY_run(daemon);
1138 call_spdy_run = false;
1139 }
1140
1141 //if(ret > 0 || (CURLM_OK == ret_curl && 0 == timeout_curl) || call_curl_run)
1142 {
1143 PRINT_VERBOSE("run curl");
1144 if(CURLM_OK != (ret = curl_multi_perform(multi_handle, &still_running))
1145 && CURLM_CALL_MULTI_PERFORM != ret)
1146 {
1147 PRINT_INFO2("curl_multi_perform failed (%i)", ret);
1148 abort();
1149 }
1150 call_curl_run = false;
1151 }
1152 /*break;
1153 }*/
1154
1155 while ((msg = curl_multi_info_read(multi_handle, &msgs_left))) {
1156 if (msg->msg == CURLMSG_DONE) {
1157 PRINT_VERBOSE("A curl handler is done");
1158 if(CURLE_OK != (ret = curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &curl_private)))
1159 {
1160 PRINT_INFO2("err %i",ret);
1161 abort();
1162 }
1163 assert(NULL != curl_private);
1164 proxy = (struct Proxy *)curl_private;
1165 if(CURLE_OK == msg->data.result)
1166 {
1167 proxy->curl_done = true;
1168 call_spdy_run = true;
1169 //TODO what happens with proxy when the client resets a stream
1170 //and response_done is not yet set for the last frame? is it
1171 //possible?
1172 }
1173 else
1174 {
1175 PRINT_VERBOSE2("bad curl result (%i) for '%s'", msg->data.result, proxy->url);
1176 if(proxy->spdy_done || proxy->spdy_error || (NULL == proxy->response && !*(proxy->session_alive)))
1177 {
1178 PRINT_VERBOSE("cleaning");
1179 SPDY_name_value_destroy(proxy->headers);
1180 SPDY_destroy_request(proxy->request);
1181 SPDY_destroy_response(proxy->response);
1182 cleanup(proxy);
1183 }
1184 else if(NULL == proxy->response && *(proxy->session_alive))
1185 {
1186 //generate error for the client
1187 PRINT_VERBOSE("will send Bad Gateway");
1188 SPDY_name_value_destroy(proxy->headers);
1189 proxy->headers = NULL;
1190 if(NULL == (proxy->response = SPDY_build_response(SPDY_HTTP_BAD_GATEWAY,
1191 NULL,
1192 SPDY_HTTP_VERSION_1_1,
1193 NULL,
1194 ERROR_RESPONSE,
1195 strlen(ERROR_RESPONSE))))
1196 DIE("no response");
1197 if(SPDY_YES != SPDY_queue_response(proxy->request,
1198 proxy->response,
1199 true,
1200 false,
1201 &response_done_callback,
1202 proxy))
1203 {
1204 //clean and forget
1205 PRINT_VERBOSE("cleaning");
1206 SPDY_destroy_request(proxy->request);
1207 SPDY_destroy_response(proxy->response);
1208 cleanup(proxy);
1209 }
1210 }
1211 else
1212 {
1213 proxy->curl_error = true;
1214 }
1215 call_spdy_run = true;
1216 //TODO spdy should be notified to send RST_STREAM
1217 }
1218 }
1219 else PRINT_INFO("shouldn't happen");
1220 }
1221
1222 if(call_spdy_run)
1223 {
1224 PRINT_VERBOSE("second call to SPDY_run");
1225 SPDY_run(daemon);
1226 call_spdy_run = false;
1227 }
1228
1229 if(glob_opt.verbose)
1230 {
1231
1232#ifdef HAVE_CLOCK_GETTIME
1233#ifdef CLOCK_MONOTONIC
1234 struct timespec ts;
1235
1236 if (0 == clock_gettime(CLOCK_MONOTONIC, &ts))
1237 PRINT_VERBOSE2 ("time now %lld %lld",
1238 (unsigned long long) ts.tv_sec,
1239 (unsigned long long) ts.tv_nsec);
1240#endif
1241#endif
1242 }
1243
1244 }
1245 while(loop);
1246
1247 SPDY_stop_daemon(daemon);
1248
1249 curl_multi_cleanup(multi_handle);
1250
1251 SPDY_deinit();
1252
1253 deinit_parse_uri(&uri_preg);
1254
1255 return 0;
1256}
1257
1258
1259static void
1260display_usage()
1261{
1262 printf(
1263 "Usage: microspdy2http -p <PORT> [-c <CERTIFICATE>] [-k <CERT-KEY>]\n"
1264 " [-rvh0DtT] [-b <HTTP-SERVER>] [-l <HOST>]\n\n"
1265 "OPTIONS:\n"
1266 " -p, --port Listening port.\n"
1267 " -l, --host Listening host. If not set, will listen on [::]\n"
1268 " -c, --certificate Path to a certificate file. Requiered if\n"
1269 " --no-tls is not set.\n"
1270 " -k, --certificate-key Path to a key file for the certificate.\n"
1271 " Requiered if --no-tls is not set.\n"
1272 " -b, --backend-server If set, the proxy will connect always to it.\n"
1273 " Otherwise the proxy will connect to the URL\n"
1274 " which is specified in the path or 'Host:'.\n"
1275 " -v, --verbose Print debug information.\n"
1276 " -r, --no-tls Do not use TLS. Client must use SPDY/3.\n"
1277 " -h, --curl-verbose Print debug information for curl.\n"
1278 " -0, --http10 Prefer HTTP/1.0 connections to the next hop.\n"
1279 " -D, --no-delay This makes sense only if --no-tls is used.\n"
1280 " TCP_NODELAY will be used for all sessions' sockets.\n"
1281 " -4, --curl-ipv4 Curl may use IPv4 to connect to the final destination.\n"
1282 " -6, --curl-ipv6 Curl may use IPv6 to connect to the final destination.\n"
1283 " If neither --curl-ipv4 nor --curl-ipv6 is set,\n"
1284 " both will be used by default.\n"
1285 " -T, --timeout Maximum time in seconds for each HTTP transfer.\n"
1286 " Use 0 for no timeout; this is the default value.\n"
1287 " -t, --transparent If set, the proxy will fetch an URL which\n"
1288 " is based on 'Host:' header and requested path.\n"
1289 " Otherwise, full URL in the requested path is required.\n\n"
1290
1291 );
1292}
1293
1294
1295int
1296main (int argc, char *const *argv)
1297{
1298
1299 int getopt_ret;
1300 int option_index;
1301 struct option long_options[] = {
1302 {"port", required_argument, 0, 'p'},
1303 {"certificate", required_argument, 0, 'c'},
1304 {"certificate-key", required_argument, 0, 'k'},
1305 {"backend-server", required_argument, 0, 'b'},
1306 {"no-tls", no_argument, 0, 'r'},
1307 {"verbose", no_argument, 0, 'v'},
1308 {"curl-verbose", no_argument, 0, 'h'},
1309 {"http10", no_argument, 0, '0'},
1310 {"no-delay", no_argument, 0, 'D'},
1311 {"transparent", no_argument, 0, 't'},
1312 {"curl-ipv4", no_argument, 0, '4'},
1313 {"curl-ipv6", no_argument, 0, '6'},
1314 {"timeout", required_argument, 0, 'T'},
1315 {0, 0, 0, 0}
1316 };
1317
1318 while (1)
1319 {
1320 getopt_ret = getopt_long( argc, argv, "p:l:c:k:b:rv0Dth46T:", long_options, &option_index);
1321 if (getopt_ret == -1)
1322 break;
1323
1324 switch(getopt_ret)
1325 {
1326 case 'p':
1327 glob_opt.listen_port = atoi(optarg);
1328 break;
1329
1330 case 'l':
1331 glob_opt.listen_host= strdup(optarg);
1332 if(NULL == glob_opt.listen_host)
1333 return 1;
1334 break;
1335
1336 case 'c':
1337 glob_opt.cert = strdup(optarg);
1338 break;
1339
1340 case 'k':
1341 glob_opt.cert_key = strdup(optarg);
1342 break;
1343
1344 case 'b':
1345 glob_opt.http_backend = strdup(optarg);
1346 if(NULL == glob_opt.http_backend)
1347 return 1;
1348 break;
1349
1350 case 'r':
1351 glob_opt.notls = true;
1352 break;
1353
1354 case 'v':
1355 glob_opt.verbose = true;
1356 break;
1357
1358 case 'h':
1359 glob_opt.curl_verbose = true;
1360 break;
1361
1362 case '0':
1363 glob_opt.http10 = true;
1364 break;
1365
1366 case 'D':
1367 glob_opt.nodelay = true;
1368 break;
1369
1370 case 't':
1371 glob_opt.transparent = true;
1372 break;
1373
1374 case '4':
1375 glob_opt.ipv4 = true;
1376 break;
1377
1378 case '6':
1379 glob_opt.ipv6 = true;
1380 break;
1381
1382 case 'T':
1383 glob_opt.timeout = atoi(optarg);
1384 break;
1385
1386 case 0:
1387 PRINT_INFO("0 from getopt");
1388 break;
1389
1390 case '?':
1391 display_usage();
1392 return 1;
1393
1394 default:
1395 DIE("default from getopt");
1396 }
1397 }
1398
1399 if(
1400 0 == glob_opt.listen_port
1401 || (!glob_opt.notls && (NULL == glob_opt.cert || NULL == glob_opt.cert_key))
1402 //|| !glob_opt.transparent && NULL != glob_opt.http_backend
1403 )
1404 {
1405 display_usage();
1406 return 1;
1407 }
1408
1409 return run();
1410}
1411
diff --git a/src/testspdy/Makefile.am b/src/testspdy/Makefile.am
deleted file mode 100644
index 9d78bec1..00000000
--- a/src/testspdy/Makefile.am
+++ /dev/null
@@ -1,115 +0,0 @@
1# This Makefile.am is in the public domain
2SUBDIRS = .
3
4AM_CFLAGS = -DDATA_DIR=\"$(top_srcdir)/src/datadir/\"
5
6if USE_COVERAGE
7 AM_CFLAGS += -fprofile-arcs -ftest-coverage
8endif
9
10AM_CPPFLAGS = \
11 -I$(top_srcdir) \
12 -I$(top_srcdir)/src/include \
13 -I$(top_srcdir)/src/applicationlayer \
14 $(LIBCURL_CPPFLAGS)
15
16if !HAVE_W32
17PERF_GET_CONCURRENT=perf_get_concurrent
18endif
19
20if ENABLE_SPDY
21if HAVE_OPENSSL
22check_PROGRAMS = \
23 test_daemon_start_stop \
24 test_daemon_start_stop_many \
25 test_struct_namevalue
26
27if HAVE_SPDYLAY
28check_PROGRAMS += \
29 test_new_connection \
30 test_request_response \
31 test_notls \
32 test_request_response_with_callback \
33 test_misc \
34 test_session_timeout
35 #test_requests_with_assets
36if HAVE_CURL_BINARY
37check_PROGRAMS += \
38 test_proxies
39endif
40endif
41endif
42endif
43
44
45TESTS = $(check_PROGRAMS)
46
47
48SPDY_SOURCES= \
49 common.h common.c
50
51SPDY_LDADD= \
52 $(top_builddir)/src/microspdy/libmicrospdy.la \
53 -lz
54
55test_daemon_start_stop_SOURCES = \
56 test_daemon_start_stop.c \
57 $(SPDY_SOURCES)
58test_daemon_start_stop_LDADD = $(SPDY_LDADD)
59
60test_daemon_start_stop_many_SOURCES = \
61 test_daemon_start_stop_many.c \
62 $(SPDY_SOURCES)
63test_daemon_start_stop_many_LDADD = $(SPDY_LDADD)
64
65test_struct_namevalue_SOURCES = \
66 test_struct_namevalue.c \
67 $(SPDY_SOURCES)
68test_struct_namevalue_LDADD = $(SPDY_LDADD)
69
70if HAVE_SPDYLAY
71test_new_connection_SOURCES = \
72 test_new_connection.c \
73 $(SPDY_SOURCES)
74test_new_connection_LDADD = $(SPDY_LDADD) \
75 -lspdylay
76
77test_request_response_SOURCES = \
78 test_request_response.c \
79 $(SPDY_SOURCES)
80test_request_response_LDADD = $(SPDY_LDADD) \
81 -lspdylay
82
83test_notls_SOURCES = \
84 test_notls.c \
85 $(SPDY_SOURCES)
86test_notls_LDADD = $(SPDY_LDADD) \
87 -lspdylay
88
89test_request_response_with_callback_SOURCES = \
90 test_request_response_with_callback.c \
91 $(SPDY_SOURCES)
92test_request_response_with_callback_LDADD = $(SPDY_LDADD)
93
94#test_requests_with_assets_SOURCES = \
95# test_requests_with_assets.c \
96# $(SPDY_SOURCES)
97#test_requests_with_assets_LDADD = $(SPDY_LDADD)
98
99test_misc_SOURCES = \
100 test_misc.c \
101 $(SPDY_SOURCES)
102test_misc_LDADD = $(SPDY_LDADD)
103
104test_session_timeout_SOURCES = \
105 test_session_timeout.c \
106 $(SPDY_SOURCES)
107test_session_timeout_LDADD = $(SPDY_LDADD)
108
109
110test_proxies_SOURCES = \
111 test_proxies.c \
112 $(SPDY_SOURCES)
113test_proxies_LDADD = $(SPDY_LDADD)
114
115endif
diff --git a/src/testspdy/common.c b/src/testspdy/common.c
deleted file mode 100644
index e150209e..00000000
--- a/src/testspdy/common.c
+++ /dev/null
@@ -1,59 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2013 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 common.c
21 * @brief Common functions used by the tests.
22 * @author Andrey Uzunov
23 */
24
25
26#include "common.h"
27#include <sys/time.h>
28
29#ifdef __GNUC__
30#define FUNC_CONSTRUCTOR(f) static void __attribute__ ((constructor)) f
31#define FUNC_DESTRUCTOR(f) static void __attribute__ ((destructor)) f
32#else // !__GNUC__
33#define FUNC_CONSTRUCTOR(f) _MHD_EXTERN void f
34#define FUNC_DESTRUCTOR(f) _MHD_EXTERN void f
35#endif // __GNUC__
36
37FUNC_CONSTRUCTOR (constructor)()
38{
39 printf("\nTEST START -------------------------------------------------------\n");
40}
41
42FUNC_DESTRUCTOR (destructor)()
43{
44 printf("------------------------------------------------------- TEST END\n");
45}
46
47uint16_t
48get_port(uint16_t min)
49{
50 struct timeval tv;
51 gettimeofday(&tv, NULL);
52 if(2 > min) min=2;
53 uint16_t port = min + (tv.tv_usec+10) % ((1 << 16) - min);
54
55 //port = 12345;
56 printf("Port used: %i\n", port);
57
58 return port;
59}
diff --git a/src/testspdy/common.h b/src/testspdy/common.h
deleted file mode 100644
index 70ee2614..00000000
--- a/src/testspdy/common.h
+++ /dev/null
@@ -1,38 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2013 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 common.h
21 * @brief Common functions used by the tests.
22 * @author Andrey Uzunov
23 */
24
25#include <stdlib.h>
26#include <unistd.h>
27#include <stdint.h>
28#include <stdio.h>
29
30#define FAIL_TEST(msg) do{\
31 printf("%i:%s\n", __LINE__, msg);\
32 fflush(stdout);\
33 exit(-10);\
34 }\
35 while(0)
36
37uint16_t
38get_port(uint16_t min);
diff --git a/src/testspdy/test_daemon_start_stop.c b/src/testspdy/test_daemon_start_stop.c
deleted file mode 100644
index d3aec6a1..00000000
--- a/src/testspdy/test_daemon_start_stop.c
+++ /dev/null
@@ -1,49 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2012 Andrey Uzunov
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file daemon_start_stop.c
21 * @brief starts and stops a SPDY daemon
22 * @author Andrey Uzunov
23 */
24
25#include "platform.h"
26#include "microspdy.h"
27#include "common.h"
28
29int
30main()
31{
32 SPDY_init();
33
34 struct SPDY_Daemon *daemon = SPDY_start_daemon(get_port(16123),
35 DATA_DIR "cert-and-key.pem",
36 DATA_DIR "cert-and-key.pem",
37 NULL,NULL,NULL,NULL,NULL,SPDY_DAEMON_OPTION_END);
38
39 if(NULL==daemon){
40 printf("no daemon\n");
41 return 1;
42 }
43
44 SPDY_stop_daemon(daemon);
45
46 SPDY_deinit();
47
48 return 0;
49}
diff --git a/src/testspdy/test_daemon_start_stop_many.c b/src/testspdy/test_daemon_start_stop_many.c
deleted file mode 100644
index b8788def..00000000
--- a/src/testspdy/test_daemon_start_stop_many.c
+++ /dev/null
@@ -1,66 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2012 Andrey Uzunov
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file daemon_start_stop_many.c
21 * @brief starts and stops several SPDY daemons, reusing port numbers
22 * @author Andrey Uzunov
23 */
24
25#include "platform.h"
26#include "microspdy.h"
27#include "common.h"
28
29int
30main()
31{
32 int i;
33 int j;
34 int num_daemons = 3;
35 int num_tries = 5;
36 int port = get_port(15123);
37 struct SPDY_Daemon *daemon[num_daemons];
38
39 SPDY_init();
40
41 for(i=0; i<num_tries; ++i)
42 {
43 for(j=0;j<num_daemons;++j)
44 {
45 daemon[j] = SPDY_start_daemon(port + j,
46 DATA_DIR "cert-and-key.pem",
47 DATA_DIR "cert-and-key.pem",
48 NULL,NULL,NULL,NULL,NULL,SPDY_DAEMON_OPTION_END);
49
50 if(NULL==daemon[j]){
51 printf("no daemon\n");
52 return 1;
53 }
54 }
55
56
57 for(j=0;j<num_daemons;++j)
58 {
59 SPDY_stop_daemon(daemon[j]);
60 }
61 }
62
63 SPDY_deinit();
64
65 return 0;
66}
diff --git a/src/testspdy/test_misc.c b/src/testspdy/test_misc.c
deleted file mode 100644
index 39b672b4..00000000
--- a/src/testspdy/test_misc.c
+++ /dev/null
@@ -1,289 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2013 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 misc.c
21 * @brief tests a lot of small calls and callbacks. TODO mention what
22 * @author Andrey Uzunov
23 */
24
25#include "platform.h"
26#include "microspdy.h"
27#include "stdio.h"
28#include <sys/wait.h>
29#include "common.h"
30
31int port;
32
33#define HTML "<html><head>\
34<link href=\"main.css\" rel=\"stylesheet\" type=\"text/css\" />\
35</head><body>This is libmicrospdy</body></html>"
36
37#define CSS "body{font-size:15px}"
38
39#define SESSION_CLS "1234567890"
40
41#define REQUEST_CLS "1234567890REQ"
42
43pid_t parent;
44pid_t child;
45
46struct SPDY_Session *session1;
47struct SPDY_Session *session2;
48
49void
50killchild()
51{
52 kill(child, SIGKILL);
53 exit(1);
54}
55
56void
57killparent()
58{
59 kill(parent, SIGKILL);
60 _exit(1);
61}
62
63
64void
65create_child()
66{
67 parent = getpid();
68
69 child = fork();
70 if (-1 == child)
71 {
72 fprintf(stderr, "can't fork, error %d\n", errno);
73 exit(EXIT_FAILURE);
74 }
75
76 if (child == 0)
77 {
78 int devnull;
79 char *uri;
80 fflush(stdout);
81 devnull = open("/dev/null", O_WRONLY);
82 if (-1 == devnull)
83 abort ();
84 if (1 != devnull)
85 {
86 dup2(devnull, 1);
87 close(devnull);
88 }
89 asprintf(&uri,"https://127.0.0.1:%i/",port);
90 execlp("spdycat", "spdycat","-anv",uri,NULL );
91 printf("execlp failed\n");
92 killparent();
93 }
94}
95
96void
97response_done_callback(void *cls,
98 struct SPDY_Response * response,
99 struct SPDY_Request * request,
100 enum SPDY_RESPONSE_RESULT status,
101 bool streamopened)
102{
103 (void)status;
104 (void)streamopened;
105
106 if(strcmp(cls,"/main.css"))
107 {
108 session1 = SPDY_get_session_for_request(request);
109 if(NULL == session1)
110 {
111 printf("SPDY_get_session_for_request failed\n");
112 killchild();
113 }
114
115 char *session_cls = strdup(SESSION_CLS);
116 SPDY_set_cls_to_session(session1,session_cls);
117 }
118 else
119 {
120 session2 = SPDY_get_session_for_request(request);
121 if(session1 != session2)
122 {
123 printf("SPDY_get_session_for_request failed the second time\n");
124 killchild();
125 }
126 printf("SPDY_get_session_for_request tested...\n");
127
128 void *session_cls = SPDY_get_cls_from_session(session2);
129 if(NULL == session_cls || strcmp(session_cls, SESSION_CLS))
130 {
131 printf("SPDY_get_cls_from_session failed\n");
132 killchild();
133 }
134 printf("SPDY_set_cls_to_session tested...\n");
135 printf("SPDY_get_cls_from_session tested...\n");
136
137 void *request_cls = SPDY_get_cls_from_request(request);
138 if(NULL == request_cls || strcmp(request_cls, REQUEST_CLS))
139 {
140 printf("SPDY_get_cls_from_request failed\n");
141 killchild();
142 }
143 printf("SPDY_set_cls_to_request tested...\n");
144 printf("SPDY_get_cls_from_request tested...\n");
145 }
146
147 SPDY_destroy_request(request);
148 SPDY_destroy_response(response);
149 free(cls);
150}
151
152void
153standard_request_handler(void *cls,
154 struct SPDY_Request * request,
155 uint8_t priority,
156 const char *method,
157 const char *path,
158 const char *version,
159 const char *host,
160 const char *scheme,
161 struct SPDY_NameValue * headers,
162 bool more)
163{
164 (void)cls;
165 (void)request;
166 (void)priority;
167 (void)host;
168 (void)scheme;
169 (void)headers;
170 (void)method;
171 (void)version;
172 (void)more;
173
174 struct SPDY_Response *response=NULL;
175 char *cls_path = strdup(path);
176
177 if(strcmp(path,"/main.css")==0)
178 {
179 char *request_cls = strdup(REQUEST_CLS);
180 SPDY_set_cls_to_request(request,request_cls);
181 response = SPDY_build_response(200,NULL,SPDY_HTTP_VERSION_1_1,NULL,CSS,strlen(CSS));
182 }
183 else
184 {
185 response = SPDY_build_response(200,NULL,SPDY_HTTP_VERSION_1_1,NULL,HTML,strlen(HTML));
186 }
187
188 if(NULL==response){
189 fprintf(stdout,"no response obj\n");
190 killchild();
191 }
192
193 if(SPDY_queue_response(request,response,true,false,&response_done_callback,cls_path)!=SPDY_YES)
194 {
195 fprintf(stdout,"queue\n");
196 killchild();
197 }
198}
199
200int
201parentproc()
202{
203 int childstatus;
204 unsigned long long timeoutlong=0;
205 struct timeval timeout;
206 int ret;
207 fd_set read_fd_set;
208 fd_set write_fd_set;
209 fd_set except_fd_set;
210 int maxfd = -1;
211 struct SPDY_Daemon *daemon;
212
213 daemon = SPDY_start_daemon(port,
214 DATA_DIR "cert-and-key.pem",
215 DATA_DIR "cert-and-key.pem",
216 NULL,
217 NULL,
218 &standard_request_handler,
219 NULL,
220 NULL,
221 SPDY_DAEMON_OPTION_SESSION_TIMEOUT,
222 1800,
223 SPDY_DAEMON_OPTION_END);
224
225 if(NULL==daemon){
226 printf("no daemon\n");
227 return 1;
228 }
229
230 create_child();
231
232 do
233 {
234 FD_ZERO(&read_fd_set);
235 FD_ZERO(&write_fd_set);
236 FD_ZERO(&except_fd_set);
237
238 ret = SPDY_get_timeout(daemon, &timeoutlong);
239 if(SPDY_NO == ret || timeoutlong > 1000)
240 {
241 timeout.tv_sec = 1;
242 timeout.tv_usec = 0;
243 }
244 else
245 {
246 timeout.tv_sec = timeoutlong / 1000;
247 timeout.tv_usec = (timeoutlong % 1000) * 1000;
248 }
249
250 maxfd = SPDY_get_fdset (daemon,
251 &read_fd_set,
252 &write_fd_set,
253 &except_fd_set);
254
255 ret = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout);
256
257 switch(ret) {
258 case -1:
259 printf("select error: %i\n", errno);
260 break;
261 case 0:
262
263 break;
264 default:
265 SPDY_run(daemon);
266
267 break;
268 }
269 }
270 while(waitpid(child,&childstatus,WNOHANG) != child);
271
272 SPDY_stop_daemon(daemon);
273
274 return WEXITSTATUS(childstatus);
275}
276
277
278int
279main()
280{
281 port = get_port(13123);
282 SPDY_init();
283
284 int ret = parentproc();
285
286 SPDY_deinit();
287
288 return ret;
289}
diff --git a/src/testspdy/test_new_connection.c b/src/testspdy/test_new_connection.c
deleted file mode 100644
index 3543c983..00000000
--- a/src/testspdy/test_new_connection.c
+++ /dev/null
@@ -1,1012 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2012 Andrey Uzunov
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file test_new_connection.c
21 * @brief tests new connection callback. spdycli.c
22 * code is reused here
23 * @author Andrey Uzunov
24 * @author Tatsuhiro Tsujikawa
25 */
26
27//TODO child exits with ret val 1 sometimes
28
29#include "platform.h"
30#include "microspdy.h"
31#include <sys/wait.h>
32#include "common.h"
33
34#define RESPONSE_BODY "<html><body><b>Hi, this is libmicrospdy!</b></body></html>"
35
36#define CLS "anything"
37
38int port;
39int loop = 1;
40
41pid_t parent;
42pid_t child;
43
44int
45spdylay_printf(const char *format, ...)
46{
47 (void)format;
48
49 return 0;
50}
51
52int
53spdylay_fprintf(FILE *stream, const char *format, ...)
54{
55 (void)stream;
56 (void)format;
57
58 return 0;
59}
60
61void
62killchild(int pid, char *message)
63{
64 printf("%s\n",message);
65 kill(pid, SIGKILL);
66 exit(1);
67}
68
69void
70killparent(int pid, char *message)
71{
72 printf("%s\n",message);
73 kill(pid, SIGKILL);
74 _exit(2);
75}
76
77
78/*****
79 * start of code needed to utilize spdylay
80 */
81
82#include <stdint.h>
83#include <stdlib.h>
84#include <unistd.h>
85#include <fcntl.h>
86#include <sys/types.h>
87#include <sys/socket.h>
88#include <netdb.h>
89#include <netinet/in.h>
90#include <netinet/tcp.h>
91#include <poll.h>
92#include <signal.h>
93#include <stdio.h>
94#include <assert.h>
95
96#include <spdylay/spdylay.h>
97
98#include <openssl/ssl.h>
99#include <openssl/err.h>
100
101enum {
102 IO_NONE,
103 WANT_READ,
104 WANT_WRITE
105};
106
107struct Connection {
108 SSL *ssl;
109 spdylay_session *session;
110 /* WANT_READ if SSL connection needs more input; or WANT_WRITE if it
111 needs more output; or IO_NONE. This is necessary because SSL/TLS
112 re-negotiation is possible at any time. Spdylay API offers
113 similar functions like spdylay_session_want_read() and
114 spdylay_session_want_write() but they do not take into account
115 SSL connection. */
116 int want_io;
117};
118
119struct Request {
120 char *host;
121 uint16_t port;
122 /* In this program, path contains query component as well. */
123 char *path;
124 /* This is the concatenation of host and port with ":" in
125 between. */
126 char *hostport;
127 /* Stream ID for this request. */
128 int32_t stream_id;
129 /* The gzip stream inflater for the compressed response. */
130 spdylay_gzip *inflater;
131};
132
133struct URI {
134 const char *host;
135 size_t hostlen;
136 uint16_t port;
137 /* In this program, path contains query component as well. */
138 const char *path;
139 size_t pathlen;
140 const char *hostport;
141 size_t hostportlen;
142};
143
144/*
145 * Returns copy of string |s| with the length |len|. The returned
146 * string is NULL-terminated.
147 */
148static char* strcopy(const char *s, size_t len)
149{
150 char *dst;
151 dst = malloc(len+1);
152 if (NULL == dst)
153 abort ();
154 memcpy(dst, s, len);
155 dst[len] = '\0';
156 return dst;
157}
158
159/*
160 * Prints error message |msg| and exit.
161 */
162static void die(const char *msg)
163{
164 fprintf(stderr, "FATAL: %s\n", msg);
165 exit(EXIT_FAILURE);
166}
167
168/*
169 * Prints error containing the function name |func| and message |msg|
170 * and exit.
171 */
172static void dief(const char *func, const char *msg)
173{
174 fprintf(stderr, "FATAL: %s: %s\n", func, msg);
175 exit(EXIT_FAILURE);
176}
177
178/*
179 * Prints error containing the function name |func| and error code
180 * |error_code| and exit.
181 */
182static void diec(const char *func, int error_code)
183{
184 fprintf(stderr, "FATAL: %s: error_code=%d, msg=%s\n", func, error_code,
185 spdylay_strerror(error_code));
186 exit(EXIT_FAILURE);
187}
188
189/*
190 * Check response is content-encoding: gzip. We need this because SPDY
191 * client is required to support gzip.
192 */
193static void check_gzip(struct Request *req, char **nv)
194{
195 int gzip = 0;
196 size_t i;
197 for(i = 0; nv[i]; i += 2) {
198 if(strcmp("content-encoding", nv[i]) == 0) {
199 gzip = strcmp("gzip", nv[i+1]) == 0;
200 break;
201 }
202 }
203 if(gzip) {
204 int rv;
205 if(req->inflater) {
206 return;
207 }
208 rv = spdylay_gzip_inflate_new(&req->inflater);
209 if(rv != 0) {
210 die("Can't allocate inflate stream.");
211 }
212 }
213}
214
215/*
216 * The implementation of spdylay_send_callback type. Here we write
217 * |data| with size |length| to the network and return the number of
218 * bytes actually written. See the documentation of
219 * spdylay_send_callback for the details.
220 */
221static ssize_t send_callback(spdylay_session *session,
222 const uint8_t *data, size_t length, int flags,
223 void *user_data)
224{
225 (void)session;
226 (void)flags;
227
228 struct Connection *connection;
229 ssize_t rv;
230 connection = (struct Connection*)user_data;
231 connection->want_io = IO_NONE;
232 ERR_clear_error();
233 rv = SSL_write(connection->ssl, data, length);
234 if(rv < 0) {
235 int err = SSL_get_error(connection->ssl, rv);
236 if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
237 connection->want_io = (err == SSL_ERROR_WANT_READ ?
238 WANT_READ : WANT_WRITE);
239 rv = SPDYLAY_ERR_WOULDBLOCK;
240 } else {
241 rv = SPDYLAY_ERR_CALLBACK_FAILURE;
242 }
243 }
244 return rv;
245}
246
247/*
248 * The implementation of spdylay_recv_callback type. Here we read data
249 * from the network and write them in |buf|. The capacity of |buf| is
250 * |length| bytes. Returns the number of bytes stored in |buf|. See
251 * the documentation of spdylay_recv_callback for the details.
252 */
253static ssize_t recv_callback(spdylay_session *session,
254 uint8_t *buf, size_t length, int flags,
255 void *user_data)
256{
257 (void)session;
258 (void)flags;
259
260 struct Connection *connection;
261 ssize_t rv;
262 connection = (struct Connection*)user_data;
263 connection->want_io = IO_NONE;
264 ERR_clear_error();
265 rv = SSL_read(connection->ssl, buf, length);
266 if(rv < 0) {
267 int err = SSL_get_error(connection->ssl, rv);
268 if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
269 connection->want_io = (err == SSL_ERROR_WANT_READ ?
270 WANT_READ : WANT_WRITE);
271 rv = SPDYLAY_ERR_WOULDBLOCK;
272 } else {
273 rv = SPDYLAY_ERR_CALLBACK_FAILURE;
274 }
275 } else if(rv == 0) {
276 rv = SPDYLAY_ERR_EOF;
277 }
278 return rv;
279}
280
281/*
282 * The implementation of spdylay_before_ctrl_send_callback type. We
283 * use this function to get stream ID of the request. This is because
284 * stream ID is not known when we submit the request
285 * (spdylay_submit_request).
286 */
287static void before_ctrl_send_callback(spdylay_session *session,
288 spdylay_frame_type type,
289 spdylay_frame *frame,
290 void *user_data)
291{
292 (void)user_data;
293
294 if(type == SPDYLAY_SYN_STREAM) {
295 struct Request *req;
296 int stream_id = frame->syn_stream.stream_id;
297 req = spdylay_session_get_stream_user_data(session, stream_id);
298 if(req && req->stream_id == -1) {
299 req->stream_id = stream_id;
300 spdylay_printf("[INFO] Stream ID = %d\n", stream_id);
301 }
302 }
303}
304
305static void on_ctrl_send_callback(spdylay_session *session,
306 spdylay_frame_type type,
307 spdylay_frame *frame, void *user_data)
308{
309 (void)user_data;
310
311 char **nv;
312 const char *name = NULL;
313 int32_t stream_id;
314 size_t i;
315 switch(type) {
316 case SPDYLAY_SYN_STREAM:
317 nv = frame->syn_stream.nv;
318 name = "SYN_STREAM";
319 stream_id = frame->syn_stream.stream_id;
320 break;
321 default:
322 break;
323 }
324 if(name && spdylay_session_get_stream_user_data(session, stream_id)) {
325 spdylay_printf("[INFO] C ----------------------------> S (%s)\n", name);
326 for(i = 0; nv[i]; i += 2) {
327 spdylay_printf(" %s: %s\n", nv[i], nv[i+1]);
328 }
329 }
330}
331
332static void on_ctrl_recv_callback(spdylay_session *session,
333 spdylay_frame_type type,
334 spdylay_frame *frame, void *user_data)
335{
336 (void)user_data;
337
338 struct Request *req;
339 char **nv;
340 const char *name = NULL;
341 int32_t stream_id;
342 size_t i;
343 switch(type) {
344 case SPDYLAY_SYN_REPLY:
345 nv = frame->syn_reply.nv;
346 name = "SYN_REPLY";
347 stream_id = frame->syn_reply.stream_id;
348 break;
349 case SPDYLAY_HEADERS:
350 nv = frame->headers.nv;
351 name = "HEADERS";
352 stream_id = frame->headers.stream_id;
353 break;
354 default:
355 break;
356 }
357 if(!name) {
358 return;
359 }
360 req = spdylay_session_get_stream_user_data(session, stream_id);
361 if(req) {
362 check_gzip(req, nv);
363 spdylay_printf("[INFO] C <---------------------------- S (%s)\n", name);
364 for(i = 0; nv[i]; i += 2) {
365 spdylay_printf(" %s: %s\n", nv[i], nv[i+1]);
366 }
367 }
368}
369
370/*
371 * The implementation of spdylay_on_stream_close_callback type. We use
372 * this function to know the response is fully received. Since we just
373 * fetch 1 resource in this program, after reception of the response,
374 * we submit GOAWAY and close the session.
375 */
376static void on_stream_close_callback(spdylay_session *session,
377 int32_t stream_id,
378 spdylay_status_code status_code,
379 void *user_data)
380{
381 (void)user_data;
382 (void)status_code;
383 struct Request *req;
384 req = spdylay_session_get_stream_user_data(session, stream_id);
385 if(req) {
386 int rv;
387 rv = spdylay_submit_goaway(session, SPDYLAY_GOAWAY_OK);
388 if(rv != 0) {
389 diec("spdylay_submit_goaway", rv);
390 }
391 }
392}
393
394#define MAX_OUTLEN 4096
395
396/*
397 * The implementation of spdylay_on_data_chunk_recv_callback type. We
398 * use this function to print the received response body.
399 */
400static void on_data_chunk_recv_callback(spdylay_session *session, uint8_t flags,
401 int32_t stream_id,
402 const uint8_t *data, size_t len,
403 void *user_data)
404{
405 (void)user_data;
406 (void)flags;
407
408 struct Request *req;
409 req = spdylay_session_get_stream_user_data(session, stream_id);
410 if(req) {
411 spdylay_printf("[INFO] C <---------------------------- S (DATA)\n");
412 spdylay_printf(" %lu bytes\n", (unsigned long int)len);
413 if(req->inflater) {
414 while(len > 0) {
415 uint8_t out[MAX_OUTLEN];
416 size_t outlen = MAX_OUTLEN;
417 size_t tlen = len;
418 int rv;
419 rv = spdylay_gzip_inflate(req->inflater, out, &outlen, data, &tlen);
420 if(rv == -1) {
421 spdylay_submit_rst_stream(session, stream_id, SPDYLAY_INTERNAL_ERROR);
422 break;
423 }
424 fwrite(out, 1, outlen, stdout);
425 data += tlen;
426 len -= tlen;
427 }
428 } else {
429 /* TODO add support gzip */
430 fwrite(data, 1, len, stdout);
431
432 //check if the data is correct
433 if(strcmp(RESPONSE_BODY, (char *)data) != 0)
434 killparent(parent, "\nreceived data is not the same");
435 }
436 spdylay_printf("\n");
437 }
438}
439
440/*
441 * Setup callback functions. Spdylay API offers many callback
442 * functions, but most of them are optional. The send_callback is
443 * always required. Since we use spdylay_session_recv(), the
444 * recv_callback is also required.
445 */
446static void setup_spdylay_callbacks(spdylay_session_callbacks *callbacks)
447{
448 memset(callbacks, 0, sizeof(spdylay_session_callbacks));
449 callbacks->send_callback = send_callback;
450 callbacks->recv_callback = recv_callback;
451 callbacks->before_ctrl_send_callback = before_ctrl_send_callback;
452 callbacks->on_ctrl_send_callback = on_ctrl_send_callback;
453 callbacks->on_ctrl_recv_callback = on_ctrl_recv_callback;
454 callbacks->on_stream_close_callback = on_stream_close_callback;
455 callbacks->on_data_chunk_recv_callback = on_data_chunk_recv_callback;
456}
457
458/*
459 * Callback function for SSL/TLS NPN. Since this program only supports
460 * SPDY protocol, if server does not offer SPDY protocol the Spdylay
461 * library supports, we terminate program.
462 */
463static int select_next_proto_cb(SSL* ssl,
464 unsigned char **out, unsigned char *outlen,
465 const unsigned char *in, unsigned int inlen,
466 void *arg)
467{
468 (void)ssl;
469
470 int rv;
471 uint16_t *spdy_proto_version;
472 /* spdylay_select_next_protocol() selects SPDY protocol version the
473 Spdylay library supports. */
474 rv = spdylay_select_next_protocol(out, outlen, in, inlen);
475 if(rv <= 0) {
476 die("Server did not advertise spdy/2 or spdy/3 protocol.");
477 }
478 spdy_proto_version = (uint16_t*)arg;
479 *spdy_proto_version = rv;
480 return SSL_TLSEXT_ERR_OK;
481}
482
483/*
484 * Setup SSL context. We pass |spdy_proto_version| to get negotiated
485 * SPDY protocol version in NPN callback.
486 */
487static void init_ssl_ctx(SSL_CTX *ssl_ctx, uint16_t *spdy_proto_version)
488{
489 /* Disable SSLv2 and enable all workarounds for buggy servers */
490 SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2);
491 SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
492 SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
493 /* Set NPN callback */
494 SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb,
495 spdy_proto_version);
496}
497
498static void ssl_handshake(SSL *ssl, int fd)
499{
500 int rv;
501 if(SSL_set_fd(ssl, fd) == 0) {
502 dief("SSL_set_fd", ERR_error_string(ERR_get_error(), NULL));
503 }
504 ERR_clear_error();
505 rv = SSL_connect(ssl);
506 if(rv <= 0) {
507 dief("SSL_connect", ERR_error_string(ERR_get_error(), NULL));
508 }
509}
510
511/*
512 * Connects to the host |host| and port |port|. This function returns
513 * the file descriptor of the client socket.
514 */
515static int connect_to(const char *host, uint16_t port)
516{
517 struct addrinfo hints;
518 int fd = -1;
519 int rv;
520 char service[NI_MAXSERV];
521 struct addrinfo *res, *rp;
522 snprintf(service, sizeof(service), "%u", port);
523 memset(&hints, 0, sizeof(struct addrinfo));
524 hints.ai_family = AF_UNSPEC;
525 hints.ai_socktype = SOCK_STREAM;
526 rv = getaddrinfo(host, service, &hints, &res);
527 if(rv != 0) {
528 dief("getaddrinfo", gai_strerror(rv));
529 }
530 for(rp = res; rp; rp = rp->ai_next) {
531 fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
532 if(fd == -1) {
533 continue;
534 }
535 while((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 &&
536 errno == EINTR);
537 if(rv == 0) {
538 break;
539 }
540 MHD_socket_close_(fd);
541 fd = -1;
542 }
543 freeaddrinfo(res);
544 return fd;
545}
546
547static void make_non_block(int fd)
548{
549 int flags, rv;
550 while((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR);
551 if(flags == -1) {
552 dief("fcntl", strerror(errno));
553 }
554 while((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR);
555 if(rv == -1) {
556 dief("fcntl", strerror(errno));
557 }
558}
559
560/*
561 * Setting TCP_NODELAY is not mandatory for the SPDY protocol.
562 */
563static void set_tcp_nodelay(int fd)
564{
565 int val = 1;
566 int rv;
567 rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val));
568 if(rv == -1) {
569 dief("setsockopt", strerror(errno));
570 }
571}
572
573/*
574 * Update |pollfd| based on the state of |connection|.
575 */
576static void ctl_poll(struct pollfd *pollfd, struct Connection *connection)
577{
578 pollfd->events = 0;
579 if(spdylay_session_want_read(connection->session) ||
580 connection->want_io == WANT_READ) {
581 pollfd->events |= POLLIN;
582 }
583 if(spdylay_session_want_write(connection->session) ||
584 connection->want_io == WANT_WRITE) {
585 pollfd->events |= POLLOUT;
586 }
587}
588
589/*
590 * Submits the request |req| to the connection |connection|. This
591 * function does not send packets; just append the request to the
592 * internal queue in |connection->session|.
593 */
594static void submit_request(struct Connection *connection, struct Request *req)
595{
596 int pri = 0;
597 int rv;
598 const char *nv[15];
599 /* We always use SPDY/3 style header even if the negotiated protocol
600 version is SPDY/2. The library translates the header name as
601 necessary. Make sure that the last item is NULL! */
602 nv[0] = ":method"; nv[1] = "GET";
603 nv[2] = ":path"; nv[3] = req->path;
604 nv[4] = ":version"; nv[5] = "HTTP/1.1";
605 nv[6] = ":scheme"; nv[7] = "https";
606 nv[8] = ":host"; nv[9] = req->hostport;
607 nv[10] = "accept"; nv[11] = "*/*";
608 nv[12] = "user-agent"; nv[13] = "spdylay/"SPDYLAY_VERSION;
609 nv[14] = NULL;
610 rv = spdylay_submit_request(connection->session, pri, nv, NULL, req);
611 if(rv != 0) {
612 diec("spdylay_submit_request", rv);
613 }
614}
615
616/*
617 * Performs the network I/O.
618 */
619static void exec_io(struct Connection *connection)
620{
621 int rv;
622 rv = spdylay_session_recv(connection->session);
623 if(rv != 0) {
624 diec("spdylay_session_recv", rv);
625 }
626 rv = spdylay_session_send(connection->session);
627 if(rv != 0) {
628 diec("spdylay_session_send", rv);
629 }
630}
631
632static void request_init(struct Request *req, const struct URI *uri)
633{
634 req->host = strcopy(uri->host, uri->hostlen);
635 req->port = uri->port;
636 req->path = strcopy(uri->path, uri->pathlen);
637 req->hostport = strcopy(uri->hostport, uri->hostportlen);
638 req->stream_id = -1;
639 req->inflater = NULL;
640}
641
642static void request_free(struct Request *req)
643{
644 free(req->host);
645 free(req->path);
646 free(req->hostport);
647 spdylay_gzip_inflate_del(req->inflater);
648}
649
650/*
651 * Fetches the resource denoted by |uri|.
652 */
653static void
654fetch_uri(const struct URI *uri)
655{
656 spdylay_session_callbacks callbacks;
657 int fd;
658 SSL_CTX *ssl_ctx;
659 SSL *ssl;
660 struct Request req;
661 struct Connection connection;
662 int rv;
663 nfds_t npollfds = 1;
664 struct pollfd pollfds[1];
665 uint16_t spdy_proto_version;
666
667 request_init(&req, uri);
668
669 setup_spdylay_callbacks(&callbacks);
670
671 /* Establish connection and setup SSL */
672 fd = connect_to(req.host, req.port);
673 if (-1 == fd)
674 abort ();
675 ssl_ctx = SSL_CTX_new(SSLv23_client_method());
676 if(ssl_ctx == NULL) {
677 dief("SSL_CTX_new", ERR_error_string(ERR_get_error(), NULL));
678 }
679 init_ssl_ctx(ssl_ctx, &spdy_proto_version);
680 ssl = SSL_new(ssl_ctx);
681 if(ssl == NULL) {
682 dief("SSL_new", ERR_error_string(ERR_get_error(), NULL));
683 }
684 /* To simplify the program, we perform SSL/TLS handshake in blocking
685 I/O. */
686 ssl_handshake(ssl, fd);
687
688 connection.ssl = ssl;
689 connection.want_io = IO_NONE;
690
691 /* Here make file descriptor non-block */
692 make_non_block(fd);
693 set_tcp_nodelay(fd);
694
695 spdylay_printf("[INFO] SPDY protocol version = %d\n", spdy_proto_version);
696 rv = spdylay_session_client_new(&connection.session, spdy_proto_version,
697 &callbacks, &connection);
698 if(rv != 0) {
699 diec("spdylay_session_client_new", rv);
700 }
701
702 /* Submit the HTTP request to the outbound queue. */
703 submit_request(&connection, &req);
704
705 pollfds[0].fd = fd;
706 ctl_poll(pollfds, &connection);
707
708 /* Event loop */
709 while(spdylay_session_want_read(connection.session) ||
710 spdylay_session_want_write(connection.session)) {
711 int nfds = poll(pollfds, npollfds, -1);
712 if(nfds == -1) {
713 dief("poll", strerror(errno));
714 }
715 if(pollfds[0].revents & (POLLIN | POLLOUT)) {
716 exec_io(&connection);
717 }
718 if((pollfds[0].revents & POLLHUP) || (pollfds[0].revents & POLLERR)) {
719 die("Connection error");
720 }
721 ctl_poll(pollfds, &connection);
722 }
723
724 /* Resource cleanup */
725 spdylay_session_del(connection.session);
726 SSL_shutdown(ssl);
727 SSL_free(ssl);
728 SSL_CTX_free(ssl_ctx);
729 shutdown(fd, SHUT_WR);
730 MHD_socket_close_ (fd);
731 request_free(&req);
732}
733
734
735static int
736parse_uri(struct URI *res, const char *uri)
737{
738 /* We only interested in https */
739 size_t len, i, offset;
740 memset(res, 0, sizeof(struct URI));
741 len = strlen(uri);
742 if(len < 9 || memcmp("https://", uri, 8) != 0) {
743 return -1;
744 }
745 offset = 8;
746 res->host = res->hostport = &uri[offset];
747 res->hostlen = 0;
748 if(uri[offset] == '[') {
749 /* IPv6 literal address */
750 ++offset;
751 ++res->host;
752 for(i = offset; i < len; ++i) {
753 if(uri[i] == ']') {
754 res->hostlen = i-offset;
755 offset = i+1;
756 break;
757 }
758 }
759 } else {
760 const char delims[] = ":/?#";
761 for(i = offset; i < len; ++i) {
762 if(strchr(delims, uri[i]) != NULL) {
763 break;
764 }
765 }
766 res->hostlen = i-offset;
767 offset = i;
768 }
769 if(res->hostlen == 0) {
770 return -1;
771 }
772 /* Assuming https */
773 res->port = 443;
774 if(offset < len) {
775 if(uri[offset] == ':') {
776 /* port */
777 const char delims[] = "/?#";
778 int port = 0;
779 ++offset;
780 for(i = offset; i < len; ++i) {
781 if(strchr(delims, uri[i]) != NULL) {
782 break;
783 }
784 if('0' <= uri[i] && uri[i] <= '9') {
785 port *= 10;
786 port += uri[i]-'0';
787 if(port > 65535) {
788 return -1;
789 }
790 } else {
791 return -1;
792 }
793 }
794 if(port == 0) {
795 return -1;
796 }
797 offset = i;
798 res->port = port;
799 }
800 }
801 res->hostportlen = uri+offset-res->host;
802 for(i = offset; i < len; ++i) {
803 if(uri[i] == '#') {
804 break;
805 }
806 }
807 if(i-offset == 0) {
808 res->path = "/";
809 res->pathlen = 1;
810 } else {
811 res->path = &uri[offset];
812 res->pathlen = i-offset;
813 }
814 return 0;
815}
816
817
818/*****
819 * end of code needed to utilize spdylay
820 */
821
822
823/*****
824 * start of code needed to utilize microspdy
825 */
826
827void
828new_session_callback (void *cls,
829 struct SPDY_Session * session)
830{
831 char ipstr[1024];
832
833 struct sockaddr *addr;
834 socklen_t addr_len = SPDY_get_remote_addr(session, &addr);
835
836 if(!addr_len)
837 {
838 printf("SPDY_get_remote_addr");
839 abort();
840 }
841
842 if(strcmp(CLS,cls)!=0)
843 {
844 killchild(child,"wrong cls");
845 }
846
847 if(AF_INET == addr->sa_family)
848 {
849 struct sockaddr_in * addr4 = (struct sockaddr_in *) addr;
850 if(NULL == inet_ntop(AF_INET, &(addr4->sin_addr), ipstr, sizeof(ipstr)))
851 {
852 killchild(child,"inet_ntop");
853 }
854 printf("New connection from: %s:%i\n", ipstr, ntohs(addr4->sin_port));
855
856 loop = 0;
857 }
858#if HAVE_INET6
859 else if(AF_INET6 == addr->sa_family)
860 {
861 struct sockaddr_in6 * addr6 = (struct sockaddr_in6 *) addr;
862 if(NULL == inet_ntop(AF_INET6, &(addr6->sin6_addr), ipstr, sizeof(ipstr)))
863 {
864 killchild(child,"inet_ntop");
865 }
866 printf("New connection from: %s:%i\n", ipstr, ntohs(addr6->sin6_port));
867
868 loop = 0;
869 }
870#endif
871 else
872 {
873 killchild(child,"wrong family");
874 }
875}
876
877/*****
878 * end of code needed to utilize microspdy
879 */
880
881//child process
882void
883childproc(int parent)
884{
885 struct URI uri;
886 struct sigaction act;
887 int rv;
888 char *uristr;
889
890 memset(&act, 0, sizeof(struct sigaction));
891 act.sa_handler = SIG_IGN;
892 sigaction(SIGPIPE, &act, 0);
893
894 asprintf(&uristr, "https://127.0.0.1:%i/",port);
895
896 SSL_load_error_strings();
897 SSL_library_init();
898
899 rv = parse_uri(&uri, uristr);
900 if(rv != 0) {
901 killparent(parent,"parse_uri failed");
902 }
903 fetch_uri(&uri);
904}
905
906//parent proc
907int
908parentproc(int child)
909{
910 int childstatus = 0;
911 unsigned long long timeoutlong=0;
912 struct timeval timeout;
913 int ret;
914 fd_set read_fd_set;
915 fd_set write_fd_set;
916 fd_set except_fd_set;
917 int maxfd = -1;
918 struct SPDY_Daemon *daemon;
919
920 SPDY_init();
921
922 daemon = SPDY_start_daemon(port,
923 DATA_DIR "cert-and-key.pem",
924 DATA_DIR "cert-and-key.pem",
925 &new_session_callback,NULL,NULL,NULL,CLS,SPDY_DAEMON_OPTION_END);
926
927 if(NULL==daemon){
928 printf("no daemon\n");
929 return 1;
930 }
931
932 do
933 {
934 FD_ZERO(&read_fd_set);
935 FD_ZERO(&write_fd_set);
936 FD_ZERO(&except_fd_set);
937
938 ret = SPDY_get_timeout(daemon, &timeoutlong);
939 if(SPDY_NO == ret || timeoutlong > 1000)
940 {
941 timeout.tv_sec = 1;
942 timeout.tv_usec = 0;
943 }
944 else
945 {
946 timeout.tv_sec = timeoutlong / 1000;
947 timeout.tv_usec = (timeoutlong % 1000) * 1000;
948 }
949
950 maxfd = SPDY_get_fdset (daemon,
951 &read_fd_set,
952 &write_fd_set,
953 &except_fd_set);
954
955 ret = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout);
956
957 switch(ret) {
958 case -1:
959 printf("select error: %i\n", errno);
960 killchild(child, "select error");
961 break;
962 case 0:
963
964 break;
965 default:
966 SPDY_run(daemon);
967
968 break;
969 }
970 }
971 while(loop && waitpid(child,&childstatus,WNOHANG) != child);
972
973 SPDY_stop_daemon(daemon);
974
975 SPDY_deinit();
976
977 if(loop)
978 return WEXITSTATUS(childstatus);
979 if(waitpid(child,&childstatus,WNOHANG) == child)
980 return WEXITSTATUS(childstatus);
981
982 kill(child,SIGKILL);
983
984 waitpid(child,&childstatus,0);
985
986 return 0;
987}
988
989int main()
990{
991 port = get_port(14123);
992 parent = getpid();
993
994 child = fork();
995 if (child == -1)
996 {
997 fprintf(stderr, "can't fork, error %d\n", errno);
998 exit(EXIT_FAILURE);
999 }
1000
1001 if (child == 0)
1002 {
1003 childproc(parent);
1004 _exit(0);
1005 }
1006 else
1007 {
1008 int ret = parentproc(child);
1009 exit(ret);
1010 }
1011 return 1;
1012}
diff --git a/src/testspdy/test_notls.c b/src/testspdy/test_notls.c
deleted file mode 100644
index 4b70edc5..00000000
--- a/src/testspdy/test_notls.c
+++ /dev/null
@@ -1,973 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2012 Andrey Uzunov
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file request_response.c
21 * @brief tests receiving request and sending response. spdycli.c (spdylay)
22 * code is reused here
23 * @author Andrey Uzunov
24 * @author Tatsuhiro Tsujikawa
25 */
26
27#include "platform.h"
28#include "microspdy.h"
29#include <sys/wait.h>
30#include "common.h"
31
32#define RESPONSE_BODY "<html><body><b>Hi, this is libmicrospdy!</b></body></html>"
33
34#define CLS "anything"
35
36pid_t parent;
37pid_t child;
38char *rcvbuf;
39int rcvbuf_c = 0;
40
41int session_closed_called = 0;
42
43void
44killchild(int pid, char *message)
45{
46 printf("%s\n",message);
47 kill(pid, SIGKILL);
48 exit(1);
49}
50
51void
52killparent(int pid, char *message)
53{
54 printf("%s\n",message);
55 kill(pid, SIGKILL);
56 _exit(1);
57}
58
59
60/*****
61 * start of code needed to utilize spdylay
62 */
63
64#include <stdint.h>
65#include <stdlib.h>
66#include <unistd.h>
67#include <fcntl.h>
68#include <sys/types.h>
69#include <sys/socket.h>
70#include <netdb.h>
71#include <netinet/in.h>
72#include <netinet/tcp.h>
73#include <poll.h>
74#include <signal.h>
75#include <stdio.h>
76#include <assert.h>
77
78#include <spdylay/spdylay.h>
79
80enum {
81 IO_NONE,
82 WANT_READ,
83 WANT_WRITE
84};
85
86struct Connection {
87 spdylay_session *session;
88 /* WANT_READ if SSL connection needs more input; or WANT_WRITE if it
89 needs more output; or IO_NONE. This is necessary because SSL/TLS
90 re-negotiation is possible at any time. Spdylay API offers
91 similar functions like spdylay_session_want_read() and
92 spdylay_session_want_write() but they do not take into account
93 SSL connection. */
94 int want_io;
95 int fd;
96};
97
98struct Request {
99 char *host;
100 uint16_t port;
101 /* In this program, path contains query component as well. */
102 char *path;
103 /* This is the concatenation of host and port with ":" in
104 between. */
105 char *hostport;
106 /* Stream ID for this request. */
107 int32_t stream_id;
108 /* The gzip stream inflater for the compressed response. */
109 spdylay_gzip *inflater;
110};
111
112struct URI {
113 const char *host;
114 size_t hostlen;
115 uint16_t port;
116 /* In this program, path contains query component as well. */
117 const char *path;
118 size_t pathlen;
119 const char *hostport;
120 size_t hostportlen;
121};
122
123/*
124 * Returns copy of string |s| with the length |len|. The returned
125 * string is NULL-terminated.
126 */
127static char* strcopy(const char *s, size_t len)
128{
129 char *dst;
130 dst = malloc(len+1);
131 if (NULL == dst)
132 abort ();
133 memcpy(dst, s, len);
134 dst[len] = '\0';
135 return dst;
136}
137
138/*
139 * Prints error message |msg| and exit.
140 */
141static void die(const char *msg)
142{
143 fprintf(stderr, "FATAL: %s\n", msg);
144 exit(EXIT_FAILURE);
145}
146
147/*
148 * Prints error containing the function name |func| and message |msg|
149 * and exit.
150 */
151static void dief(const char *func, const char *msg)
152{
153 fprintf(stderr, "FATAL: %s: %s\n", func, msg);
154 exit(EXIT_FAILURE);
155}
156
157/*
158 * Prints error containing the function name |func| and error code
159 * |error_code| and exit.
160 */
161static void diec(const char *func, int error_code)
162{
163 fprintf(stderr, "FATAL: %s: error_code=%d, msg=%s\n", func, error_code,
164 spdylay_strerror(error_code));
165 exit(EXIT_FAILURE);
166}
167
168/*
169 * Check response is content-encoding: gzip. We need this because SPDY
170 * client is required to support gzip.
171 */
172static void check_gzip(struct Request *req, char **nv)
173{
174 int gzip = 0;
175 size_t i;
176 for(i = 0; nv[i]; i += 2) {
177 if(strcmp("content-encoding", nv[i]) == 0) {
178 gzip = strcmp("gzip", nv[i+1]) == 0;
179 break;
180 }
181 }
182 if(gzip) {
183 int rv;
184 if(req->inflater) {
185 return;
186 }
187 rv = spdylay_gzip_inflate_new(&req->inflater);
188 if(rv != 0) {
189 die("Can't allocate inflate stream.");
190 }
191 }
192}
193
194/*
195 * The implementation of spdylay_send_callback type. Here we write
196 * |data| with size |length| to the network and return the number of
197 * bytes actually written. See the documentation of
198 * spdylay_send_callback for the details.
199 */
200static ssize_t send_callback(spdylay_session *session,
201 const uint8_t *data, size_t length, int flags,
202 void *user_data)
203{
204 (void)session;
205 (void)flags;
206
207 struct Connection *connection;
208 ssize_t rv;
209 connection = (struct Connection*)user_data;
210 connection->want_io = IO_NONE;
211
212 rv = write(connection->fd,
213 data,
214 length);
215
216 if (rv < 0)
217 {
218 switch(errno)
219 {
220 case EAGAIN:
221 #if EAGAIN != EWOULDBLOCK
222 case EWOULDBLOCK:
223 #endif
224 connection->want_io = WANT_WRITE;
225 rv = SPDYLAY_ERR_WOULDBLOCK;
226 break;
227
228 default:
229 rv = SPDYLAY_ERR_CALLBACK_FAILURE;
230 }
231 }
232 return rv;
233}
234
235/*
236 * The implementation of spdylay_recv_callback type. Here we read data
237 * from the network and write them in |buf|. The capacity of |buf| is
238 * |length| bytes. Returns the number of bytes stored in |buf|. See
239 * the documentation of spdylay_recv_callback for the details.
240 */
241static ssize_t recv_callback(spdylay_session *session,
242 uint8_t *buf, size_t length, int flags,
243 void *user_data)
244{
245 (void)session;
246 (void)flags;
247
248 struct Connection *connection;
249 ssize_t rv;
250 connection = (struct Connection*)user_data;
251 connection->want_io = IO_NONE;
252
253 rv = read(connection->fd,
254 buf,
255 length);
256
257 if (rv < 0)
258 {
259 switch(errno)
260 {
261 case EAGAIN:
262 #if EAGAIN != EWOULDBLOCK
263 case EWOULDBLOCK:
264 #endif
265 connection->want_io = WANT_READ;
266 rv = SPDYLAY_ERR_WOULDBLOCK;
267 break;
268
269 default:
270 rv = SPDYLAY_ERR_CALLBACK_FAILURE;
271 }
272 }
273 else if(rv == 0)
274 rv = SPDYLAY_ERR_EOF;
275 return rv;
276}
277
278/*
279 * The implementation of spdylay_before_ctrl_send_callback type. We
280 * use this function to get stream ID of the request. This is because
281 * stream ID is not known when we submit the request
282 * (spdylay_submit_request).
283 */
284static void before_ctrl_send_callback(spdylay_session *session,
285 spdylay_frame_type type,
286 spdylay_frame *frame,
287 void *user_data)
288{
289 (void)user_data;
290
291 if(type == SPDYLAY_SYN_STREAM) {
292 struct Request *req;
293 int stream_id = frame->syn_stream.stream_id;
294 req = spdylay_session_get_stream_user_data(session, stream_id);
295 if(req && req->stream_id == -1) {
296 req->stream_id = stream_id;
297 printf("[INFO] Stream ID = %d\n", stream_id);
298 }
299 }
300}
301
302static void on_ctrl_send_callback(spdylay_session *session,
303 spdylay_frame_type type,
304 spdylay_frame *frame, void *user_data)
305{
306 (void)user_data;
307
308 char **nv;
309 const char *name = NULL;
310 int32_t stream_id;
311 size_t i;
312 switch(type) {
313 case SPDYLAY_SYN_STREAM:
314 nv = frame->syn_stream.nv;
315 name = "SYN_STREAM";
316 stream_id = frame->syn_stream.stream_id;
317 break;
318 default:
319 break;
320 }
321 if(name && spdylay_session_get_stream_user_data(session, stream_id)) {
322 printf("[INFO] C ----------------------------> S (%s)\n", name);
323 for(i = 0; nv[i]; i += 2) {
324 printf(" %s: %s\n", nv[i], nv[i+1]);
325 }
326 }
327}
328
329static void on_ctrl_recv_callback(spdylay_session *session,
330 spdylay_frame_type type,
331 spdylay_frame *frame, void *user_data)
332{
333 (void)user_data;
334
335 struct Request *req;
336 char **nv;
337 const char *name = NULL;
338 int32_t stream_id;
339 size_t i;
340 switch(type) {
341 case SPDYLAY_SYN_REPLY:
342 nv = frame->syn_reply.nv;
343 name = "SYN_REPLY";
344 stream_id = frame->syn_reply.stream_id;
345 break;
346 case SPDYLAY_HEADERS:
347 nv = frame->headers.nv;
348 name = "HEADERS";
349 stream_id = frame->headers.stream_id;
350 break;
351 default:
352 break;
353 }
354 if(!name) {
355 return;
356 }
357 req = spdylay_session_get_stream_user_data(session, stream_id);
358 if(req) {
359 check_gzip(req, nv);
360 printf("[INFO] C <---------------------------- S (%s)\n", name);
361 for(i = 0; nv[i]; i += 2) {
362 printf(" %s: %s\n", nv[i], nv[i+1]);
363 }
364 }
365}
366
367/*
368 * The implementation of spdylay_on_stream_close_callback type. We use
369 * this function to know the response is fully received. Since we just
370 * fetch 1 resource in this program, after reception of the response,
371 * we submit GOAWAY and close the session.
372 */
373static void on_stream_close_callback(spdylay_session *session,
374 int32_t stream_id,
375 spdylay_status_code status_code,
376 void *user_data)
377{
378 (void)status_code;
379 (void)user_data;
380
381 struct Request *req;
382 req = spdylay_session_get_stream_user_data(session, stream_id);
383 if(req) {
384 int rv;
385 rv = spdylay_submit_goaway(session, SPDYLAY_GOAWAY_OK);
386 if(rv != 0) {
387 diec("spdylay_submit_goaway", rv);
388 }
389 }
390}
391
392#define MAX_OUTLEN 4096
393
394/*
395 * The implementation of spdylay_on_data_chunk_recv_callback type. We
396 * use this function to print the received response body.
397 */
398static void on_data_chunk_recv_callback(spdylay_session *session, uint8_t flags,
399 int32_t stream_id,
400 const uint8_t *data, size_t len,
401 void *user_data)
402{
403 (void)flags;
404 (void)user_data;
405
406 struct Request *req;
407 req = spdylay_session_get_stream_user_data(session, stream_id);
408 if(req) {
409 printf("[INFO] C <---------------------------- S (DATA)\n");
410 printf(" %lu bytes\n", (unsigned long int)len);
411 if(req->inflater) {
412 while(len > 0) {
413 uint8_t out[MAX_OUTLEN];
414 size_t outlen = MAX_OUTLEN;
415 size_t tlen = len;
416 int rv;
417 rv = spdylay_gzip_inflate(req->inflater, out, &outlen, data, &tlen);
418 if(rv == -1) {
419 spdylay_submit_rst_stream(session, stream_id, SPDYLAY_INTERNAL_ERROR);
420 break;
421 }
422 fwrite(out, 1, outlen, stdout);
423 data += tlen;
424 len -= tlen;
425 }
426 } else {
427 /* TODO add support gzip */
428 fwrite(data, 1, len, stdout);
429
430 //check if the data is correct
431 //if(strcmp(RESPONSE_BODY, data) != 0)
432 //killparent(parent, "\nreceived data is not the same");
433 if(len + rcvbuf_c > strlen(RESPONSE_BODY))
434 killparent(parent, "\nreceived data is not the same");
435
436 strcpy(rcvbuf + rcvbuf_c,(char*)data);
437 rcvbuf_c+=len;
438 }
439 printf("\n");
440 }
441}
442
443/*
444 * Setup callback functions. Spdylay API offers many callback
445 * functions, but most of them are optional. The send_callback is
446 * always required. Since we use spdylay_session_recv(), the
447 * recv_callback is also required.
448 */
449static void setup_spdylay_callbacks(spdylay_session_callbacks *callbacks)
450{
451 memset(callbacks, 0, sizeof(spdylay_session_callbacks));
452 callbacks->send_callback = send_callback;
453 callbacks->recv_callback = recv_callback;
454 callbacks->before_ctrl_send_callback = before_ctrl_send_callback;
455 callbacks->on_ctrl_send_callback = on_ctrl_send_callback;
456 callbacks->on_ctrl_recv_callback = on_ctrl_recv_callback;
457 callbacks->on_stream_close_callback = on_stream_close_callback;
458 callbacks->on_data_chunk_recv_callback = on_data_chunk_recv_callback;
459}
460
461
462/*
463 * Connects to the host |host| and port |port|. This function returns
464 * the file descriptor of the client socket.
465 */
466static int connect_to(const char *host, uint16_t port)
467{
468 struct addrinfo hints;
469 int fd = -1;
470 int rv;
471 char service[NI_MAXSERV];
472 struct addrinfo *res, *rp;
473 snprintf(service, sizeof(service), "%u", port);
474 memset(&hints, 0, sizeof(struct addrinfo));
475 hints.ai_family = AF_UNSPEC;
476 hints.ai_socktype = SOCK_STREAM;
477 rv = getaddrinfo(host, service, &hints, &res);
478 if(rv != 0) {
479 dief("getaddrinfo", gai_strerror(rv));
480 }
481 for(rp = res; rp; rp = rp->ai_next) {
482 fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
483 if(fd == -1) {
484 continue;
485 }
486 while((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 &&
487 errno == EINTR);
488 if(rv == 0) {
489 break;
490 }
491 MHD_socket_close_(fd);
492 fd = -1;
493 dief("connect", strerror(errno));
494 }
495 freeaddrinfo(res);
496 return fd;
497}
498
499static void make_non_block(int fd)
500{
501 int flags, rv;
502 while((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR);
503 if(flags == -1) {
504 dief("fcntl1", strerror(errno));
505 }
506 while((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR);
507 if(rv == -1) {
508 dief("fcntl2", strerror(errno));
509 }
510}
511
512/*
513 * Setting TCP_NODELAY is not mandatory for the SPDY protocol.
514 */
515static void set_tcp_nodelay(int fd)
516{
517 int val = 1;
518 int rv;
519 rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val));
520 if(rv == -1) {
521 dief("setsockopt", strerror(errno));
522 }
523}
524
525/*
526 * Update |pollfd| based on the state of |connection|.
527 */
528static void ctl_poll(struct pollfd *pollfd, struct Connection *connection)
529{
530 pollfd->events = 0;
531 if(spdylay_session_want_read(connection->session) ||
532 connection->want_io == WANT_READ) {
533 pollfd->events |= POLLIN;
534 }
535 if(spdylay_session_want_write(connection->session) ||
536 connection->want_io == WANT_WRITE) {
537 pollfd->events |= POLLOUT;
538 }
539}
540
541/*
542 * Submits the request |req| to the connection |connection|. This
543 * function does not send packets; just append the request to the
544 * internal queue in |connection->session|.
545 */
546static void submit_request(struct Connection *connection, struct Request *req)
547{
548 int pri = 0;
549 int rv;
550 const char *nv[15];
551 /* We always use SPDY/3 style header even if the negotiated protocol
552 version is SPDY/2. The library translates the header name as
553 necessary. Make sure that the last item is NULL! */
554 nv[0] = ":method"; nv[1] = "GET";
555 nv[2] = ":path"; nv[3] = req->path;
556 nv[4] = ":version"; nv[5] = "HTTP/1.1";
557 nv[6] = ":scheme"; nv[7] = "https";
558 nv[8] = ":host"; nv[9] = req->hostport;
559 nv[10] = "accept"; nv[11] = "*/*";
560 nv[12] = "user-agent"; nv[13] = "spdylay/"SPDYLAY_VERSION;
561 nv[14] = NULL;
562 rv = spdylay_submit_request(connection->session, pri, nv, NULL, req);
563 if(rv != 0) {
564 diec("spdylay_submit_request", rv);
565 }
566}
567
568/*
569 * Performs the network I/O.
570 */
571static void exec_io(struct Connection *connection)
572{
573 int rv;
574 rv = spdylay_session_recv(connection->session);
575 if(rv != 0) {
576 diec("spdylay_session_recv", rv);
577 }
578 rv = spdylay_session_send(connection->session);
579 if(rv != 0) {
580 diec("spdylay_session_send", rv);
581 }
582}
583
584static void request_init(struct Request *req, const struct URI *uri)
585{
586 req->host = strcopy(uri->host, uri->hostlen);
587 req->port = uri->port;
588 req->path = strcopy(uri->path, uri->pathlen);
589 req->hostport = strcopy(uri->hostport, uri->hostportlen);
590 req->stream_id = -1;
591 req->inflater = NULL;
592}
593
594static void request_free(struct Request *req)
595{
596 free(req->host);
597 free(req->path);
598 free(req->hostport);
599 spdylay_gzip_inflate_del(req->inflater);
600}
601
602/*
603 * Fetches the resource denoted by |uri|.
604 */
605static void fetch_uri(const struct URI *uri)
606{
607 spdylay_session_callbacks callbacks;
608 int fd;
609 struct Request req;
610 struct Connection connection;
611 int rv;
612 nfds_t npollfds = 1;
613 struct pollfd pollfds[1];
614 uint16_t spdy_proto_version = 3;
615
616 request_init(&req, uri);
617
618 setup_spdylay_callbacks(&callbacks);
619
620 /* Establish connection and setup SSL */
621 fd = connect_to(req.host, req.port);
622 if (-1 == fd)
623 abort ();
624
625 connection.fd = fd;
626 connection.want_io = IO_NONE;
627
628 /* Here make file descriptor non-block */
629 make_non_block(fd);
630 set_tcp_nodelay(fd);
631
632 printf("[INFO] SPDY protocol version = %d\n", spdy_proto_version);
633 rv = spdylay_session_client_new(&connection.session, spdy_proto_version,
634 &callbacks, &connection);
635 if(rv != 0) {
636 diec("spdylay_session_client_new", rv);
637 }
638
639 /* Submit the HTTP request to the outbound queue. */
640 submit_request(&connection, &req);
641
642 pollfds[0].fd = fd;
643 ctl_poll(pollfds, &connection);
644
645 /* Event loop */
646 while(spdylay_session_want_read(connection.session) ||
647 spdylay_session_want_write(connection.session)) {
648 int nfds = poll(pollfds, npollfds, -1);
649 if(nfds == -1) {
650 dief("poll", strerror(errno));
651 }
652 if(pollfds[0].revents & (POLLIN | POLLOUT)) {
653 exec_io(&connection);
654 }
655 if((pollfds[0].revents & POLLHUP) || (pollfds[0].revents & POLLERR)) {
656 die("Connection error");
657 }
658 ctl_poll(pollfds, &connection);
659 }
660
661 /* Resource cleanup */
662 spdylay_session_del(connection.session);
663 shutdown(fd, SHUT_WR);
664 MHD_socket_close_(fd);
665 request_free(&req);
666}
667
668static int parse_uri(struct URI *res, const char *uri)
669{
670 /* We only interested in https */
671 size_t len, i, offset;
672 memset(res, 0, sizeof(struct URI));
673 len = strlen(uri);
674 if(len < 9 || memcmp("https://", uri, 8) != 0) {
675 return -1;
676 }
677 offset = 8;
678 res->host = res->hostport = &uri[offset];
679 res->hostlen = 0;
680 if(uri[offset] == '[') {
681 /* IPv6 literal address */
682 ++offset;
683 ++res->host;
684 for(i = offset; i < len; ++i) {
685 if(uri[i] == ']') {
686 res->hostlen = i-offset;
687 offset = i+1;
688 break;
689 }
690 }
691 } else {
692 const char delims[] = ":/?#";
693 for(i = offset; i < len; ++i) {
694 if(strchr(delims, uri[i]) != NULL) {
695 break;
696 }
697 }
698 res->hostlen = i-offset;
699 offset = i;
700 }
701 if(res->hostlen == 0) {
702 return -1;
703 }
704 /* Assuming https */
705 res->port = 443;
706 if(offset < len) {
707 if(uri[offset] == ':') {
708 /* port */
709 const char delims[] = "/?#";
710 int port = 0;
711 ++offset;
712 for(i = offset; i < len; ++i) {
713 if(strchr(delims, uri[i]) != NULL) {
714 break;
715 }
716 if('0' <= uri[i] && uri[i] <= '9') {
717 port *= 10;
718 port += uri[i]-'0';
719 if(port > 65535) {
720 return -1;
721 }
722 } else {
723 return -1;
724 }
725 }
726 if(port == 0) {
727 return -1;
728 }
729 offset = i;
730 res->port = port;
731 }
732 }
733 res->hostportlen = uri+offset-res->host;
734 for(i = offset; i < len; ++i) {
735 if(uri[i] == '#') {
736 break;
737 }
738 }
739 if(i-offset == 0) {
740 res->path = "/";
741 res->pathlen = 1;
742 } else {
743 res->path = &uri[offset];
744 res->pathlen = i-offset;
745 }
746 return 0;
747}
748
749
750/*****
751 * end of code needed to utilize spdylay
752 */
753
754
755/*****
756 * start of code needed to utilize microspdy
757 */
758
759
760void
761standard_request_handler(void *cls,
762 struct SPDY_Request * request,
763 uint8_t priority,
764 const char *method,
765 const char *path,
766 const char *version,
767 const char *host,
768 const char *scheme,
769 struct SPDY_NameValue * headers,
770 bool more)
771{
772 (void)cls;
773 (void)request;
774 (void)priority;
775 (void)host;
776 (void)scheme;
777 (void)headers;
778 (void)method;
779 (void)version;
780 (void)more;
781
782 struct SPDY_Response *response=NULL;
783
784 if(strcmp(CLS,cls)!=0)
785 {
786 killchild(child,"wrong cls");
787 }
788
789 response = SPDY_build_response(200,NULL,SPDY_HTTP_VERSION_1_1,NULL,RESPONSE_BODY,strlen(RESPONSE_BODY));
790
791 if(NULL==response){
792 fprintf(stdout,"no response obj\n");
793 exit(3);
794 }
795
796 if(SPDY_queue_response(request,response,true,false,NULL,(void*)strdup(path))!=SPDY_YES)
797 {
798 fprintf(stdout,"queue\n");
799 exit(4);
800 }
801}
802
803void
804session_closed_handler (void *cls,
805 struct SPDY_Session * session,
806 int by_client)
807{
808 printf("session_closed_handler called\n");
809
810 if(strcmp(CLS,cls)!=0)
811 {
812 killchild(child,"wrong cls");
813 }
814
815 if(SPDY_YES != by_client)
816 {
817 //killchild(child,"wrong by_client");
818 printf("session closed by server\n");
819 }
820 else
821 {
822 printf("session closed by client\n");
823 }
824
825 if(NULL == session)
826 {
827 killchild(child,"session is NULL");
828 }
829
830 session_closed_called = 1;
831}
832
833
834/*****
835 * end of code needed to utilize microspdy
836 */
837
838//child process
839void
840childproc(int port)
841{
842 struct URI uri;
843 struct sigaction act;
844 int rv;
845 char *uristr;
846
847 memset(&act, 0, sizeof(struct sigaction));
848 act.sa_handler = SIG_IGN;
849 sigaction(SIGPIPE, &act, 0);
850
851 usleep(10000);
852 asprintf(&uristr, "https://127.0.0.1:%i/",port);
853 if(NULL == (rcvbuf = malloc(strlen(RESPONSE_BODY)+1)))
854 killparent(parent,"no memory");
855
856 rv = parse_uri(&uri, uristr);
857 if(rv != 0) {
858 killparent(parent,"parse_uri failed");
859 }
860 fetch_uri(&uri);
861
862 if(strcmp(rcvbuf, RESPONSE_BODY))
863 killparent(parent,"received data is different");
864}
865
866//parent proc
867int
868parentproc( int port)
869{
870 int childstatus;
871 unsigned long long timeoutlong=0;
872 struct timeval timeout;
873 int ret;
874 fd_set read_fd_set;
875 fd_set write_fd_set;
876 fd_set except_fd_set;
877 int maxfd = -1;
878 struct SPDY_Daemon *daemon;
879
880 SPDY_init();
881
882 daemon = SPDY_start_daemon(port,
883 NULL,
884 NULL,
885 NULL,&session_closed_handler,&standard_request_handler,NULL,CLS,
886 SPDY_DAEMON_OPTION_IO_SUBSYSTEM, SPDY_IO_SUBSYSTEM_RAW,
887 SPDY_DAEMON_OPTION_FLAGS, SPDY_DAEMON_FLAG_NO_DELAY,
888 SPDY_DAEMON_OPTION_END);
889
890 if(NULL==daemon){
891 printf("no daemon\n");
892 return 1;
893 }
894
895 do
896 {
897 FD_ZERO(&read_fd_set);
898 FD_ZERO(&write_fd_set);
899 FD_ZERO(&except_fd_set);
900
901 ret = SPDY_get_timeout(daemon, &timeoutlong);
902 if(SPDY_NO == ret || timeoutlong > 1000)
903 {
904 timeout.tv_sec = 1;
905 timeout.tv_usec = 0;
906 }
907 else
908 {
909 timeout.tv_sec = timeoutlong / 1000;
910 timeout.tv_usec = (timeoutlong % 1000) * 1000;
911 }
912
913 maxfd = SPDY_get_fdset (daemon,
914 &read_fd_set,
915 &write_fd_set,
916 &except_fd_set);
917
918 ret = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout);
919
920 switch(ret) {
921 case -1:
922 printf("select error: %i\n", errno);
923 killchild(child, "select error");
924 break;
925 case 0:
926
927 break;
928 default:
929 SPDY_run(daemon);
930
931 break;
932 }
933 }
934 while(waitpid(child,&childstatus,WNOHANG) != child);
935
936 //give chance to the client to close socket and handle this in run
937 usleep(100000);
938 SPDY_run(daemon);
939
940 SPDY_stop_daemon(daemon);
941
942 SPDY_deinit();
943
944 return WEXITSTATUS(childstatus);
945}
946
947int main()
948{
949 int port = get_port(12123);
950 parent = getpid();
951
952 child = fork();
953 if (child == -1)
954 {
955 fprintf(stderr, "can't fork, error %d\n", errno);
956 exit(EXIT_FAILURE);
957 }
958
959 if (child == 0)
960 {
961 childproc(port);
962 _exit(0);
963 }
964 else
965 {
966 int ret = parentproc(port);
967 if(1 == session_closed_called && 0 == ret)
968 exit(0);
969 else
970 exit(ret ? ret : 21);
971 }
972 return 1;
973}
diff --git a/src/testspdy/test_proxies.c b/src/testspdy/test_proxies.c
deleted file mode 100644
index 44a0c1bd..00000000
--- a/src/testspdy/test_proxies.c
+++ /dev/null
@@ -1,255 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2013 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 test_proxies.c
21 * @brief test curl > mhd2spdylay > microspdy2http > mhd
22 * @author Andrey Uzunov
23 */
24
25#include "platform.h"
26#include "microspdy.h"
27#include "common.h"
28#include <sys/wait.h>
29#include <stdio.h> /* printf, stderr, fprintf */
30#include <sys/types.h> /* pid_t */
31#include <unistd.h> /* _exit, fork */
32#include <stdlib.h> /* exit */
33#include <errno.h> /* errno */
34#include <sys/wait.h> /* pid_t */
35#include "common.h"
36
37#ifdef _WIN32
38#ifndef WIN32_LEAN_AND_MEAN
39#define WIN32_LEAN_AND_MEAN 1
40#endif /* !WIN32_LEAN_AND_MEAN */
41#include <windows.h>
42#endif
43
44#define EXPECTED_BODY "<html><head><title>libmicrohttpd demo</title></head><body>libmicrohttpd demo</body></html>"
45
46
47pid_t parent;
48 pid_t child_mhd;
49 pid_t child_spdy2http;
50 pid_t child_mhd2spdy;
51 pid_t child_curl;
52
53 uint16_t mhd_port;
54 uint16_t spdy2http_port;
55 uint16_t mhd2spdy_port;
56
57void
58killproc(int pid, const char *message)
59{
60 printf("%s\nkilling %i\n",message,pid);
61 kill(pid, SIGKILL);
62}
63
64
65void killchildren()
66{
67 if(0 != child_mhd)
68 killproc(child_mhd,"kill mhd\n");
69 if(0 != child_spdy2http)
70 killproc(child_spdy2http,"kill spdy2http\n");
71 if(0 != child_mhd2spdy)
72 killproc(child_mhd2spdy,"kill mhd2spdy\n");
73 if(0 != child_curl)
74 killproc(child_curl,"kill curl\n");
75}
76
77pid_t au_fork()
78{
79 pid_t child = fork();
80 if (child == -1)
81 {
82 killchildren();
83
84 killproc(parent,"fork failed\n");
85 }
86
87 return child;
88}
89
90
91int main()
92{
93 //pid_t child;
94 int childstatus;
95 pid_t wpid;
96
97 parent = getpid();
98 mhd_port = get_port(4000);
99 spdy2http_port = get_port(4100);
100 mhd2spdy_port = get_port(4200);
101
102 child_mhd = au_fork();
103 if (child_mhd == 0)
104 {
105 //run MHD
106 pid_t devnull;
107 char *port_s;
108
109 close(1);
110 devnull = open("/dev/null", O_WRONLY);
111 if (-1 == devnull)
112 abort();
113 if (1 != devnull)
114 {
115 dup2(devnull, 1);
116 close(devnull);
117 }
118 asprintf(&port_s, "%i", mhd_port);
119 execlp ("../examples/minimal_example", "minimal_example", port_s, NULL);
120 fprintf(stderr, "executing mhd failed\nFor this test 'make' must be run before 'make check'!\n");
121 //killchildren();
122 _exit(1);
123 }
124
125
126 child_spdy2http = au_fork();
127 if (child_spdy2http == 0)
128 {
129 //run spdy2http
130 pid_t devnull;
131 char *port_s;
132 //char *url;
133
134 close(1);
135 devnull = open("/dev/null", O_WRONLY);
136 if (-1 == devnull)
137 abort();
138 if (1 != devnull)
139 {
140 dup2(devnull, 1);
141 close(devnull);
142 }
143 //asprintf(&url, "127.0.0.1:%i", mhd_port);
144 asprintf(&port_s, "%i", spdy2http_port);
145 sleep(1);
146 execlp ("../spdy2http/microspdy2http", "microspdy2http", "-v4rtT", "10", "-p", port_s, NULL);
147 fprintf(stderr, "executing microspdy2http failed\n");
148 //killchildren();
149 _exit(1);
150 }
151
152 child_mhd2spdy = au_fork();
153 if (child_mhd2spdy == 0)
154 {
155 //run MHD2sdpy
156 pid_t devnull;
157 char *port_s;
158 char *url;
159
160 close(1);
161 devnull = open("/dev/null", O_WRONLY);
162 if (-1 == devnull)
163 abort();
164 if (1 != devnull)
165 {
166 dup2(devnull, 1);
167 close(devnull);
168 }
169 asprintf(&url, "http://127.0.0.1:%i", spdy2http_port);
170 asprintf(&port_s, "%i", mhd2spdy_port);
171 sleep(2);
172 execlp ("../examples/mhd2spdy", "mhd2spdy", "-vosb", url, "-p", port_s, NULL);
173 fprintf(stderr, "executing mhd2spdy failed\n");
174 //killchildren();
175 _exit(1);
176 }
177
178 child_curl = au_fork();
179 if (child_curl == 0)
180 {
181 //run curl
182 FILE *p;
183 pid_t devnull;
184 char *cmd;
185 unsigned int i;
186 int retc;
187 char buf[strlen(EXPECTED_BODY) + 1];
188
189 close(1);
190 devnull = open("/dev/null", O_WRONLY);
191 if (-1 == devnull)
192 abort ();
193 if (1 != devnull)
194 {
195 dup2(devnull, 1);
196 close(devnull);
197 }
198
199 asprintf (&cmd, "curl --proxy http://127.0.0.1:%i http://127.0.0.1:%i/", mhd2spdy_port, mhd_port);
200 sleep(3);
201 p = popen(cmd, "r");
202 if (p != NULL)
203 {
204 for (i = 0; i < strlen(EXPECTED_BODY) && !feof(p); i++)
205 {
206 retc = fgetc (p);
207 if (EOF == retc)
208 abort (); /* what did feof(p) do there!? */
209 buf[i] = (char) retc;
210 }
211
212 pclose(p);
213 buf[i] = 0;
214 _exit(strcmp(EXPECTED_BODY, buf));
215 }
216 fprintf(stderr, "executing curl failed\n");
217 //killchildren();
218 _exit(1);
219 }
220
221 do
222 {
223 wpid = waitpid(child_mhd,&childstatus,WNOHANG);
224 if(wpid == child_mhd)
225 {
226 fprintf(stderr, "mhd died unexpectedly\n");
227 killchildren();
228 return 1;
229 }
230
231 wpid = waitpid(child_spdy2http,&childstatus,WNOHANG);
232 if(wpid == child_spdy2http)
233 {
234 fprintf(stderr, "spdy2http died unexpectedly\n");
235 killchildren();
236 return 1;
237 }
238
239 wpid = waitpid(child_mhd2spdy,&childstatus,WNOHANG);
240 if(wpid == child_mhd2spdy)
241 {
242 fprintf(stderr, "mhd2spdy died unexpectedly\n");
243 killchildren();
244 return 1;
245 }
246
247 if(waitpid(child_curl,&childstatus,WNOHANG) == child_curl)
248 {
249 killchildren();
250 return WEXITSTATUS(childstatus);
251 }
252 sleep(1);
253 }
254 while(true);
255}
diff --git a/src/testspdy/test_request_response.c b/src/testspdy/test_request_response.c
deleted file mode 100644
index 36a85572..00000000
--- a/src/testspdy/test_request_response.c
+++ /dev/null
@@ -1,1029 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2012 Andrey Uzunov
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file test_request_response.c
21 * @brief tests receiving request and sending response. spdycli.c (spdylay)
22 * code is reused here
23 * @author Andrey Uzunov
24 * @author Tatsuhiro Tsujikawa
25 */
26
27#include "platform.h"
28#include "microspdy.h"
29#include <sys/wait.h>
30#include "common.h"
31
32#define RESPONSE_BODY "<html><body><b>Hi, this is libmicrospdy!</b></body></html>"
33
34#define CLS "anything"
35
36pid_t parent;
37pid_t child;
38char *rcvbuf;
39int rcvbuf_c = 0;
40
41int session_closed_called = 0;
42
43void
44killchild(int pid, char *message)
45{
46 printf("%s\n",message);
47 kill(pid, SIGKILL);
48 exit(1);
49}
50
51void
52killparent(int pid, char *message)
53{
54 printf("%s\n",message);
55 kill(pid, SIGKILL);
56 _exit(1);
57}
58
59
60/*****
61 * start of code needed to utilize spdylay
62 */
63
64#include <stdint.h>
65#include <stdlib.h>
66#include <unistd.h>
67#include <fcntl.h>
68#include <sys/types.h>
69#include <sys/socket.h>
70#include <netdb.h>
71#include <netinet/in.h>
72#include <netinet/tcp.h>
73#include <poll.h>
74#include <signal.h>
75#include <stdio.h>
76#include <assert.h>
77
78#include <spdylay/spdylay.h>
79
80#include <openssl/ssl.h>
81#include <openssl/err.h>
82
83enum {
84 IO_NONE,
85 WANT_READ,
86 WANT_WRITE
87};
88
89struct Connection {
90 SSL *ssl;
91 spdylay_session *session;
92 /* WANT_READ if SSL connection needs more input; or WANT_WRITE if it
93 needs more output; or IO_NONE. This is necessary because SSL/TLS
94 re-negotiation is possible at any time. Spdylay API offers
95 similar functions like spdylay_session_want_read() and
96 spdylay_session_want_write() but they do not take into account
97 SSL connection. */
98 int want_io;
99};
100
101struct Request {
102 char *host;
103 uint16_t port;
104 /* In this program, path contains query component as well. */
105 char *path;
106 /* This is the concatenation of host and port with ":" in
107 between. */
108 char *hostport;
109 /* Stream ID for this request. */
110 int32_t stream_id;
111 /* The gzip stream inflater for the compressed response. */
112 spdylay_gzip *inflater;
113};
114
115struct URI {
116 const char *host;
117 size_t hostlen;
118 uint16_t port;
119 /* In this program, path contains query component as well. */
120 const char *path;
121 size_t pathlen;
122 const char *hostport;
123 size_t hostportlen;
124};
125
126/*
127 * Returns copy of string |s| with the length |len|. The returned
128 * string is NULL-terminated.
129 */
130static char* strcopy(const char *s, size_t len)
131{
132 char *dst;
133 dst = malloc(len+1);
134 if (NULL == dst)
135 abort ();
136 memcpy(dst, s, len);
137 dst[len] = '\0';
138 return dst;
139}
140
141/*
142 * Prints error message |msg| and exit.
143 */
144static void die(const char *msg)
145{
146 fprintf(stderr, "FATAL: %s\n", msg);
147 exit(EXIT_FAILURE);
148}
149
150/*
151 * Prints error containing the function name |func| and message |msg|
152 * and exit.
153 */
154static void dief(const char *func, const char *msg)
155{
156 fprintf(stderr, "FATAL: %s: %s\n", func, msg);
157 exit(EXIT_FAILURE);
158}
159
160/*
161 * Prints error containing the function name |func| and error code
162 * |error_code| and exit.
163 */
164static void diec(const char *func, int error_code)
165{
166 fprintf(stderr, "FATAL: %s: error_code=%d, msg=%s\n", func, error_code,
167 spdylay_strerror(error_code));
168 exit(EXIT_FAILURE);
169}
170
171/*
172 * Check response is content-encoding: gzip. We need this because SPDY
173 * client is required to support gzip.
174 */
175static void check_gzip(struct Request *req, char **nv)
176{
177 int gzip = 0;
178 size_t i;
179 for(i = 0; nv[i]; i += 2) {
180 if(strcmp("content-encoding", nv[i]) == 0) {
181 gzip = strcmp("gzip", nv[i+1]) == 0;
182 break;
183 }
184 }
185 if(gzip) {
186 int rv;
187 if(req->inflater) {
188 return;
189 }
190 rv = spdylay_gzip_inflate_new(&req->inflater);
191 if(rv != 0) {
192 die("Can't allocate inflate stream.");
193 }
194 }
195}
196
197/*
198 * The implementation of spdylay_send_callback type. Here we write
199 * |data| with size |length| to the network and return the number of
200 * bytes actually written. See the documentation of
201 * spdylay_send_callback for the details.
202 */
203static ssize_t send_callback(spdylay_session *session,
204 const uint8_t *data, size_t length, int flags,
205 void *user_data)
206{
207 (void)session;
208 (void)flags;
209
210 struct Connection *connection;
211 ssize_t rv;
212 connection = (struct Connection*)user_data;
213 connection->want_io = IO_NONE;
214 ERR_clear_error();
215 rv = SSL_write(connection->ssl, data, length);
216 if(rv < 0) {
217 int err = SSL_get_error(connection->ssl, rv);
218 if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
219 connection->want_io = (err == SSL_ERROR_WANT_READ ?
220 WANT_READ : WANT_WRITE);
221 rv = SPDYLAY_ERR_WOULDBLOCK;
222 } else {
223 rv = SPDYLAY_ERR_CALLBACK_FAILURE;
224 }
225 }
226 return rv;
227}
228
229/*
230 * The implementation of spdylay_recv_callback type. Here we read data
231 * from the network and write them in |buf|. The capacity of |buf| is
232 * |length| bytes. Returns the number of bytes stored in |buf|. See
233 * the documentation of spdylay_recv_callback for the details.
234 */
235static ssize_t recv_callback(spdylay_session *session,
236 uint8_t *buf, size_t length, int flags,
237 void *user_data)
238{
239 (void)session;
240 (void)flags;
241
242 struct Connection *connection;
243 ssize_t rv;
244 connection = (struct Connection*)user_data;
245 connection->want_io = IO_NONE;
246 ERR_clear_error();
247 rv = SSL_read(connection->ssl, buf, length);
248 if(rv < 0) {
249 int err = SSL_get_error(connection->ssl, rv);
250 if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
251 connection->want_io = (err == SSL_ERROR_WANT_READ ?
252 WANT_READ : WANT_WRITE);
253 rv = SPDYLAY_ERR_WOULDBLOCK;
254 } else {
255 rv = SPDYLAY_ERR_CALLBACK_FAILURE;
256 }
257 } else if(rv == 0) {
258 rv = SPDYLAY_ERR_EOF;
259 }
260 return rv;
261}
262
263/*
264 * The implementation of spdylay_before_ctrl_send_callback type. We
265 * use this function to get stream ID of the request. This is because
266 * stream ID is not known when we submit the request
267 * (spdylay_submit_request).
268 */
269static void before_ctrl_send_callback(spdylay_session *session,
270 spdylay_frame_type type,
271 spdylay_frame *frame,
272 void *user_data)
273{
274 (void)user_data;
275
276 if(type == SPDYLAY_SYN_STREAM) {
277 struct Request *req;
278 int stream_id = frame->syn_stream.stream_id;
279 req = spdylay_session_get_stream_user_data(session, stream_id);
280 if(req && req->stream_id == -1) {
281 req->stream_id = stream_id;
282 printf("[INFO] Stream ID = %d\n", stream_id);
283 }
284 }
285}
286
287static void on_ctrl_send_callback(spdylay_session *session,
288 spdylay_frame_type type,
289 spdylay_frame *frame, void *user_data)
290{
291 (void)user_data;
292
293 char **nv;
294 const char *name = NULL;
295 int32_t stream_id;
296 size_t i;
297 switch(type) {
298 case SPDYLAY_SYN_STREAM:
299 nv = frame->syn_stream.nv;
300 name = "SYN_STREAM";
301 stream_id = frame->syn_stream.stream_id;
302 break;
303 default:
304 break;
305 }
306 if(name && spdylay_session_get_stream_user_data(session, stream_id)) {
307 printf("[INFO] C ----------------------------> S (%s)\n", name);
308 for(i = 0; nv[i]; i += 2) {
309 printf(" %s: %s\n", nv[i], nv[i+1]);
310 }
311 }
312}
313
314static void on_ctrl_recv_callback(spdylay_session *session,
315 spdylay_frame_type type,
316 spdylay_frame *frame, void *user_data)
317{
318 (void)user_data;
319
320 struct Request *req;
321 char **nv;
322 const char *name = NULL;
323 int32_t stream_id;
324 size_t i;
325 switch(type) {
326 case SPDYLAY_SYN_REPLY:
327 nv = frame->syn_reply.nv;
328 name = "SYN_REPLY";
329 stream_id = frame->syn_reply.stream_id;
330 break;
331 case SPDYLAY_HEADERS:
332 nv = frame->headers.nv;
333 name = "HEADERS";
334 stream_id = frame->headers.stream_id;
335 break;
336 default:
337 break;
338 }
339 if(!name) {
340 return;
341 }
342 req = spdylay_session_get_stream_user_data(session, stream_id);
343 if(req) {
344 check_gzip(req, nv);
345 printf("[INFO] C <---------------------------- S (%s)\n", name);
346 for(i = 0; nv[i]; i += 2) {
347 printf(" %s: %s\n", nv[i], nv[i+1]);
348 }
349 }
350}
351
352/*
353 * The implementation of spdylay_on_stream_close_callback type. We use
354 * this function to know the response is fully received. Since we just
355 * fetch 1 resource in this program, after reception of the response,
356 * we submit GOAWAY and close the session.
357 */
358static void on_stream_close_callback(spdylay_session *session,
359 int32_t stream_id,
360 spdylay_status_code status_code,
361 void *user_data)
362{
363 (void)user_data;
364 (void)status_code;
365
366 struct Request *req;
367 req = spdylay_session_get_stream_user_data(session, stream_id);
368 if(req) {
369 int rv;
370 rv = spdylay_submit_goaway(session, SPDYLAY_GOAWAY_OK);
371 if(rv != 0) {
372 diec("spdylay_submit_goaway", rv);
373 }
374 }
375}
376
377#define MAX_OUTLEN 4096
378
379/*
380 * The implementation of spdylay_on_data_chunk_recv_callback type. We
381 * use this function to print the received response body.
382 */
383static void on_data_chunk_recv_callback(spdylay_session *session, uint8_t flags,
384 int32_t stream_id,
385 const uint8_t *data, size_t len,
386 void *user_data)
387{
388 (void)user_data;
389 (void)flags;
390
391 struct Request *req;
392 req = spdylay_session_get_stream_user_data(session, stream_id);
393 if(req) {
394 printf("[INFO] C <---------------------------- S (DATA)\n");
395 printf(" %lu bytes\n", (unsigned long int)len);
396 if(req->inflater) {
397 while(len > 0) {
398 uint8_t out[MAX_OUTLEN];
399 size_t outlen = MAX_OUTLEN;
400 size_t tlen = len;
401 int rv;
402 rv = spdylay_gzip_inflate(req->inflater, out, &outlen, data, &tlen);
403 if(rv == -1) {
404 spdylay_submit_rst_stream(session, stream_id, SPDYLAY_INTERNAL_ERROR);
405 break;
406 }
407 fwrite(out, 1, outlen, stdout);
408 data += tlen;
409 len -= tlen;
410 }
411 } else {
412 /* TODO add support gzip */
413 fwrite(data, 1, len, stdout);
414
415 //check if the data is correct
416 //if(strcmp(RESPONSE_BODY, data) != 0)
417 //killparent(parent, "\nreceived data is not the same");
418 if(len + rcvbuf_c > strlen(RESPONSE_BODY))
419 killparent(parent, "\nreceived data is not the same");
420
421 strcpy(rcvbuf + rcvbuf_c,(char*)data);
422 rcvbuf_c+=len;
423 }
424 printf("\n");
425 }
426}
427
428/*
429 * Setup callback functions. Spdylay API offers many callback
430 * functions, but most of them are optional. The send_callback is
431 * always required. Since we use spdylay_session_recv(), the
432 * recv_callback is also required.
433 */
434static void setup_spdylay_callbacks(spdylay_session_callbacks *callbacks)
435{
436 memset(callbacks, 0, sizeof(spdylay_session_callbacks));
437 callbacks->send_callback = send_callback;
438 callbacks->recv_callback = recv_callback;
439 callbacks->before_ctrl_send_callback = before_ctrl_send_callback;
440 callbacks->on_ctrl_send_callback = on_ctrl_send_callback;
441 callbacks->on_ctrl_recv_callback = on_ctrl_recv_callback;
442 callbacks->on_stream_close_callback = on_stream_close_callback;
443 callbacks->on_data_chunk_recv_callback = on_data_chunk_recv_callback;
444}
445
446/*
447 * Callback function for SSL/TLS NPN. Since this program only supports
448 * SPDY protocol, if server does not offer SPDY protocol the Spdylay
449 * library supports, we terminate program.
450 */
451static int select_next_proto_cb(SSL* ssl,
452 unsigned char **out, unsigned char *outlen,
453 const unsigned char *in, unsigned int inlen,
454 void *arg)
455{
456 (void)ssl;
457
458 int rv;
459 uint16_t *spdy_proto_version;
460 /* spdylay_select_next_protocol() selects SPDY protocol version the
461 Spdylay library supports. */
462 rv = spdylay_select_next_protocol(out, outlen, in, inlen);
463 if(rv <= 0) {
464 die("Server did not advertise spdy/2 or spdy/3 protocol.");
465 }
466 spdy_proto_version = (uint16_t*)arg;
467 *spdy_proto_version = rv;
468 return SSL_TLSEXT_ERR_OK;
469}
470
471/*
472 * Setup SSL context. We pass |spdy_proto_version| to get negotiated
473 * SPDY protocol version in NPN callback.
474 */
475static void init_ssl_ctx(SSL_CTX *ssl_ctx, uint16_t *spdy_proto_version)
476{
477 /* Disable SSLv2 and enable all workarounds for buggy servers */
478 SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2);
479 SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
480 SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
481 /* Set NPN callback */
482 SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb,
483 spdy_proto_version);
484}
485
486static void ssl_handshake(SSL *ssl, int fd)
487{
488 int rv;
489 if(SSL_set_fd(ssl, fd) == 0) {
490 dief("SSL_set_fd", ERR_error_string(ERR_get_error(), NULL));
491 }
492 ERR_clear_error();
493 rv = SSL_connect(ssl);
494 if(rv <= 0) {
495 dief("SSL_connect", ERR_error_string(ERR_get_error(), NULL));
496 }
497}
498
499/*
500 * Connects to the host |host| and port |port|. This function returns
501 * the file descriptor of the client socket.
502 */
503static int connect_to(const char *host, uint16_t port)
504{
505 struct addrinfo hints;
506 int fd = -1;
507 int rv;
508 char service[NI_MAXSERV];
509 struct addrinfo *res, *rp;
510 snprintf(service, sizeof(service), "%u", port);
511 memset(&hints, 0, sizeof(struct addrinfo));
512 hints.ai_family = AF_UNSPEC;
513 hints.ai_socktype = SOCK_STREAM;
514 rv = getaddrinfo(host, service, &hints, &res);
515 if(rv != 0) {
516 dief("getaddrinfo", gai_strerror(rv));
517 }
518 for(rp = res; rp; rp = rp->ai_next) {
519 fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
520 if(fd == -1) {
521 continue;
522 }
523 while((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 &&
524 errno == EINTR);
525 if(rv == 0) {
526 break;
527 }
528 MHD_socket_close_(fd);
529 fd = -1;
530 }
531 freeaddrinfo(res);
532 return fd;
533}
534
535static void make_non_block(int fd)
536{
537 int flags, rv;
538 while((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR);
539 if(flags == -1) {
540 dief("fcntl", strerror(errno));
541 }
542 while((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR);
543 if(rv == -1) {
544 dief("fcntl", strerror(errno));
545 }
546}
547
548/*
549 * Setting TCP_NODELAY is not mandatory for the SPDY protocol.
550 */
551static void set_tcp_nodelay(int fd)
552{
553 int val = 1;
554 int rv;
555 rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val));
556 if(rv == -1) {
557 dief("setsockopt", strerror(errno));
558 }
559}
560
561/*
562 * Update |pollfd| based on the state of |connection|.
563 */
564static void ctl_poll(struct pollfd *pollfd, struct Connection *connection)
565{
566 pollfd->events = 0;
567 if(spdylay_session_want_read(connection->session) ||
568 connection->want_io == WANT_READ) {
569 pollfd->events |= POLLIN;
570 }
571 if(spdylay_session_want_write(connection->session) ||
572 connection->want_io == WANT_WRITE) {
573 pollfd->events |= POLLOUT;
574 }
575}
576
577/*
578 * Submits the request |req| to the connection |connection|. This
579 * function does not send packets; just append the request to the
580 * internal queue in |connection->session|.
581 */
582static void submit_request(struct Connection *connection, struct Request *req)
583{
584 int pri = 0;
585 int rv;
586 const char *nv[15];
587 /* We always use SPDY/3 style header even if the negotiated protocol
588 version is SPDY/2. The library translates the header name as
589 necessary. Make sure that the last item is NULL! */
590 nv[0] = ":method"; nv[1] = "GET";
591 nv[2] = ":path"; nv[3] = req->path;
592 nv[4] = ":version"; nv[5] = "HTTP/1.1";
593 nv[6] = ":scheme"; nv[7] = "https";
594 nv[8] = ":host"; nv[9] = req->hostport;
595 nv[10] = "accept"; nv[11] = "*/*";
596 nv[12] = "user-agent"; nv[13] = "spdylay/"SPDYLAY_VERSION;
597 nv[14] = NULL;
598 rv = spdylay_submit_request(connection->session, pri, nv, NULL, req);
599 if(rv != 0) {
600 diec("spdylay_submit_request", rv);
601 }
602}
603
604/*
605 * Performs the network I/O.
606 */
607static void exec_io(struct Connection *connection)
608{
609 int rv;
610 rv = spdylay_session_recv(connection->session);
611 if(rv != 0) {
612 diec("spdylay_session_recv", rv);
613 }
614 rv = spdylay_session_send(connection->session);
615 if(rv != 0) {
616 diec("spdylay_session_send", rv);
617 }
618}
619
620static void request_init(struct Request *req, const struct URI *uri)
621{
622 req->host = strcopy(uri->host, uri->hostlen);
623 req->port = uri->port;
624 req->path = strcopy(uri->path, uri->pathlen);
625 req->hostport = strcopy(uri->hostport, uri->hostportlen);
626 req->stream_id = -1;
627 req->inflater = NULL;
628}
629
630static void request_free(struct Request *req)
631{
632 free(req->host);
633 free(req->path);
634 free(req->hostport);
635 spdylay_gzip_inflate_del(req->inflater);
636}
637
638/*
639 * Fetches the resource denoted by |uri|.
640 */
641static void fetch_uri(const struct URI *uri)
642{
643 spdylay_session_callbacks callbacks;
644 int fd;
645 SSL_CTX *ssl_ctx;
646 SSL *ssl;
647 struct Request req;
648 struct Connection connection;
649 int rv;
650 nfds_t npollfds = 1;
651 struct pollfd pollfds[1];
652 uint16_t spdy_proto_version;
653
654 request_init(&req, uri);
655
656 setup_spdylay_callbacks(&callbacks);
657
658 /* Establish connection and setup SSL */
659 fd = connect_to(req.host, req.port);
660 if (-1 == fd)
661 abort ();
662 ssl_ctx = SSL_CTX_new(SSLv23_client_method());
663 if(ssl_ctx == NULL) {
664 dief("SSL_CTX_new", ERR_error_string(ERR_get_error(), NULL));
665 }
666 init_ssl_ctx(ssl_ctx, &spdy_proto_version);
667 ssl = SSL_new(ssl_ctx);
668 if(ssl == NULL) {
669 dief("SSL_new", ERR_error_string(ERR_get_error(), NULL));
670 }
671 /* To simplify the program, we perform SSL/TLS handshake in blocking
672 I/O. */
673 ssl_handshake(ssl, fd);
674
675 connection.ssl = ssl;
676 connection.want_io = IO_NONE;
677
678 /* Here make file descriptor non-block */
679 make_non_block(fd);
680 set_tcp_nodelay(fd);
681
682 printf("[INFO] SPDY protocol version = %d\n", spdy_proto_version);
683 rv = spdylay_session_client_new(&connection.session, spdy_proto_version,
684 &callbacks, &connection);
685 if(rv != 0) {
686 diec("spdylay_session_client_new", rv);
687 }
688
689 /* Submit the HTTP request to the outbound queue. */
690 submit_request(&connection, &req);
691
692 pollfds[0].fd = fd;
693 ctl_poll(pollfds, &connection);
694
695 /* Event loop */
696 while(spdylay_session_want_read(connection.session) ||
697 spdylay_session_want_write(connection.session)) {
698 int nfds = poll(pollfds, npollfds, -1);
699 if(nfds == -1) {
700 dief("poll", strerror(errno));
701 }
702 if(pollfds[0].revents & (POLLIN | POLLOUT)) {
703 exec_io(&connection);
704 }
705 if((pollfds[0].revents & POLLHUP) || (pollfds[0].revents & POLLERR)) {
706 die("Connection error");
707 }
708 ctl_poll(pollfds, &connection);
709 }
710
711 /* Resource cleanup */
712 spdylay_session_del(connection.session);
713 SSL_shutdown(ssl);
714 SSL_free(ssl);
715 SSL_CTX_free(ssl_ctx);
716 shutdown(fd, SHUT_WR);
717 MHD_socket_close_(fd);
718 request_free(&req);
719}
720
721static int parse_uri(struct URI *res, const char *uri)
722{
723 /* We only interested in https */
724 size_t len, i, offset;
725 memset(res, 0, sizeof(struct URI));
726 len = strlen(uri);
727 if(len < 9 || memcmp("https://", uri, 8) != 0) {
728 return -1;
729 }
730 offset = 8;
731 res->host = res->hostport = &uri[offset];
732 res->hostlen = 0;
733 if(uri[offset] == '[') {
734 /* IPv6 literal address */
735 ++offset;
736 ++res->host;
737 for(i = offset; i < len; ++i) {
738 if(uri[i] == ']') {
739 res->hostlen = i-offset;
740 offset = i+1;
741 break;
742 }
743 }
744 } else {
745 const char delims[] = ":/?#";
746 for(i = offset; i < len; ++i) {
747 if(strchr(delims, uri[i]) != NULL) {
748 break;
749 }
750 }
751 res->hostlen = i-offset;
752 offset = i;
753 }
754 if(res->hostlen == 0) {
755 return -1;
756 }
757 /* Assuming https */
758 res->port = 443;
759 if(offset < len) {
760 if(uri[offset] == ':') {
761 /* port */
762 const char delims[] = "/?#";
763 int port = 0;
764 ++offset;
765 for(i = offset; i < len; ++i) {
766 if(strchr(delims, uri[i]) != NULL) {
767 break;
768 }
769 if('0' <= uri[i] && uri[i] <= '9') {
770 port *= 10;
771 port += uri[i]-'0';
772 if(port > 65535) {
773 return -1;
774 }
775 } else {
776 return -1;
777 }
778 }
779 if(port == 0) {
780 return -1;
781 }
782 offset = i;
783 res->port = port;
784 }
785 }
786 res->hostportlen = uri+offset-res->host;
787 for(i = offset; i < len; ++i) {
788 if(uri[i] == '#') {
789 break;
790 }
791 }
792 if(i-offset == 0) {
793 res->path = "/";
794 res->pathlen = 1;
795 } else {
796 res->path = &uri[offset];
797 res->pathlen = i-offset;
798 }
799 return 0;
800}
801
802
803/*****
804 * end of code needed to utilize spdylay
805 */
806
807
808/*****
809 * start of code needed to utilize microspdy
810 */
811
812
813void
814standard_request_handler(void *cls,
815 struct SPDY_Request * request,
816 uint8_t priority,
817 const char *method,
818 const char *path,
819 const char *version,
820 const char *host,
821 const char *scheme,
822 struct SPDY_NameValue * headers,
823 bool more)
824{
825 (void)cls;
826 (void)request;
827 (void)priority;
828 (void)host;
829 (void)scheme;
830 (void)headers;
831 (void)method;
832 (void)version;
833
834 struct SPDY_Response *response=NULL;
835
836 if(strcmp(CLS,cls)!=0)
837 {
838 killchild(child,"wrong cls");
839 }
840
841 if(false != more){
842 fprintf(stdout,"more has wrong value\n");
843 exit(5);
844 }
845
846 response = SPDY_build_response(200,NULL,SPDY_HTTP_VERSION_1_1,NULL,RESPONSE_BODY,strlen(RESPONSE_BODY));
847
848 if(NULL==response){
849 fprintf(stdout,"no response obj\n");
850 exit(3);
851 }
852
853 if(SPDY_queue_response(request,response,true,false,NULL,(void*)strdup(path))!=SPDY_YES)
854 {
855 fprintf(stdout,"queue\n");
856 exit(4);
857 }
858}
859
860void
861session_closed_handler (void *cls,
862 struct SPDY_Session * session,
863 int by_client)
864{
865 printf("session_closed_handler called\n");
866
867 if(strcmp(CLS,cls)!=0)
868 {
869 killchild(child,"wrong cls");
870 }
871
872 if(SPDY_YES != by_client)
873 {
874 //killchild(child,"wrong by_client");
875 printf("session closed by server\n");
876 }
877 else
878 {
879 printf("session closed by client\n");
880 }
881
882 if(NULL == session)
883 {
884 killchild(child,"session is NULL");
885 }
886
887 session_closed_called = 1;
888}
889
890
891/*****
892 * end of code needed to utilize microspdy
893 */
894
895//child process
896void
897childproc(int port)
898{
899 struct URI uri;
900 struct sigaction act;
901 int rv;
902 char *uristr;
903
904 memset(&act, 0, sizeof(struct sigaction));
905 act.sa_handler = SIG_IGN;
906 sigaction(SIGPIPE, &act, 0);
907
908 asprintf(&uristr, "https://127.0.0.1:%i/",port);
909 if(NULL == (rcvbuf = malloc(strlen(RESPONSE_BODY)+1)))
910 killparent(parent,"no memory");
911
912 SSL_load_error_strings();
913 SSL_library_init();
914
915 rv = parse_uri(&uri, uristr);
916 if(rv != 0) {
917 killparent(parent,"parse_uri failed");
918 }
919 fetch_uri(&uri);
920
921 if(strcmp(rcvbuf, RESPONSE_BODY))
922 killparent(parent,"received data is different");
923}
924
925//parent proc
926int
927parentproc( int port)
928{
929 int childstatus;
930 unsigned long long timeoutlong=0;
931 struct timeval timeout;
932 int ret;
933 fd_set read_fd_set;
934 fd_set write_fd_set;
935 fd_set except_fd_set;
936 int maxfd = -1;
937 struct SPDY_Daemon *daemon;
938
939 SPDY_init();
940
941 daemon = SPDY_start_daemon(port,
942 DATA_DIR "cert-and-key.pem",
943 DATA_DIR "cert-and-key.pem",
944 NULL,&session_closed_handler,&standard_request_handler,NULL,CLS,SPDY_DAEMON_OPTION_END);
945
946 if(NULL==daemon){
947 printf("no daemon\n");
948 return 1;
949 }
950
951 do
952 {
953 FD_ZERO(&read_fd_set);
954 FD_ZERO(&write_fd_set);
955 FD_ZERO(&except_fd_set);
956
957 ret = SPDY_get_timeout(daemon, &timeoutlong);
958 if(SPDY_NO == ret || timeoutlong > 1000)
959 {
960 timeout.tv_sec = 1;
961 timeout.tv_usec = 0;
962 }
963 else
964 {
965 timeout.tv_sec = timeoutlong / 1000;
966 timeout.tv_usec = (timeoutlong % 1000) * 1000;
967 }
968
969 maxfd = SPDY_get_fdset (daemon,
970 &read_fd_set,
971 &write_fd_set,
972 &except_fd_set);
973
974 ret = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout);
975
976 switch(ret) {
977 case -1:
978 printf("select error: %i\n", errno);
979 killchild(child, "select error");
980 break;
981 case 0:
982
983 break;
984 default:
985 SPDY_run(daemon);
986
987 break;
988 }
989 }
990 while(waitpid(child,&childstatus,WNOHANG) != child);
991
992 //give chance to the client to close socket and handle this in run
993 usleep(100000);
994 SPDY_run(daemon);
995
996 SPDY_stop_daemon(daemon);
997
998 SPDY_deinit();
999
1000 return WEXITSTATUS(childstatus);
1001}
1002
1003int main()
1004{
1005 int port = get_port(12123);
1006 parent = getpid();
1007
1008 child = fork();
1009 if (child == -1)
1010 {
1011 fprintf(stderr, "can't fork, error %d\n", errno);
1012 exit(EXIT_FAILURE);
1013 }
1014
1015 if (child == 0)
1016 {
1017 childproc(port);
1018 _exit(0);
1019 }
1020 else
1021 {
1022 int ret = parentproc(port);
1023 if(1 == session_closed_called && 0 == ret)
1024 exit(0);
1025 else
1026 exit(ret ? ret : 21);
1027 }
1028 return 1;
1029}
diff --git a/src/testspdy/test_request_response_with_callback.c b/src/testspdy/test_request_response_with_callback.c
deleted file mode 100644
index 95fc263d..00000000
--- a/src/testspdy/test_request_response_with_callback.c
+++ /dev/null
@@ -1,320 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2013 Andrey Uzunov
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file request_response_with_callback.c
21 * @brief tests responses with callbacks
22 * @author Andrey Uzunov
23 */
24
25#include "platform.h"
26#include "microspdy.h"
27#include "stdio.h"
28#include <sys/wait.h>
29#include <ctype.h>
30#include "common.h"
31#include <sys/time.h>
32#include <sys/stat.h>
33
34int port;
35
36pid_t parent;
37pid_t child;
38
39int run = 1;
40int chunk_size=1;
41
42void
43killchild()
44{
45 kill(child, SIGKILL);
46 exit(1);
47}
48
49void
50killparent()
51{
52 kill(parent, SIGKILL);
53 _exit(1);
54}
55
56ssize_t
57response_callback (void *cls,
58 void *buffer,
59 size_t max,
60 bool *more)
61{
62 FILE *fd =(FILE*)cls;
63
64 size_t n;
65 if(chunk_size % 2)
66 n = chunk_size;
67 else
68 n = max - chunk_size;
69
70 if(n < 1) n = 1;
71 else if (n > max) n=max;
72 chunk_size++;
73
74 int ret = fread(buffer,1,n,fd);
75 *more = feof(fd) == 0;
76
77 //printf("more is %i\n",*more);
78
79 if(!(*more))
80 fclose(fd);
81
82 return ret;
83}
84
85
86void
87response_done_callback(void *cls,
88 struct SPDY_Response * response,
89 struct SPDY_Request * request,
90 enum SPDY_RESPONSE_RESULT status,
91 bool streamopened)
92{
93 (void)status;
94 (void)streamopened;
95
96 printf("answer for %s was sent\n", (char*)cls);
97
98 SPDY_destroy_request(request);
99 SPDY_destroy_response(response);
100 free(cls);
101
102 run = 0;
103}
104
105void
106standard_request_handler(void *cls,
107 struct SPDY_Request * request,
108 uint8_t priority,
109 const char *method,
110 const char *path,
111 const char *version,
112 const char *host,
113 const char *scheme,
114 struct SPDY_NameValue * headers,
115 bool more)
116{
117 (void)cls;
118 (void)request;
119 (void)priority;
120 (void)host;
121 (void)scheme;
122 (void)headers;
123 (void)method;
124 (void)version;
125 (void)more;
126
127 struct SPDY_Response *response=NULL;
128 struct SPDY_NameValue *resp_headers;
129
130 printf("received request for '%s %s %s'\n", method, path, version);
131
132 FILE *fd = fopen(DATA_DIR "spdy-draft.txt","r");
133
134 if(NULL == (resp_headers = SPDY_name_value_create()))
135 {
136 fprintf(stdout,"SPDY_name_value_create failed\n");
137 killchild();
138 }
139 if(SPDY_YES != SPDY_name_value_add(resp_headers,SPDY_HTTP_HEADER_CONTENT_TYPE,"text/plain"))
140 {
141 fprintf(stdout,"SPDY_name_value_add failed\n");
142 killchild();
143 }
144
145 response = SPDY_build_response_with_callback(200,NULL,
146 SPDY_HTTP_VERSION_1_1,resp_headers,&response_callback,fd,SPDY_MAX_SUPPORTED_FRAME_SIZE);
147 SPDY_name_value_destroy(resp_headers);
148
149 if(NULL==response){
150 fprintf(stdout,"no response obj\n");
151 killchild();
152 }
153
154 void *clspath = strdup(path);
155
156 if(SPDY_queue_response(request,response,true,false,&response_done_callback,clspath)!=SPDY_YES)
157 {
158 fprintf(stdout,"queue\n");
159 killchild();
160 }
161}
162
163int
164parentproc()
165{
166 int childstatus;
167 unsigned long long timeoutlong=0;
168 struct timeval timeout;
169 int ret;
170 fd_set read_fd_set;
171 fd_set write_fd_set;
172 fd_set except_fd_set;
173 int maxfd = -1;
174 struct SPDY_Daemon *daemon;
175
176 SPDY_init();
177
178 daemon = SPDY_start_daemon(port,
179 DATA_DIR "cert-and-key.pem",
180 DATA_DIR "cert-and-key.pem",
181 NULL,
182 NULL,
183 &standard_request_handler,
184 NULL,
185 NULL,
186 SPDY_DAEMON_OPTION_SESSION_TIMEOUT,
187 1800,
188 SPDY_DAEMON_OPTION_END);
189
190 if(NULL==daemon){
191 printf("no daemon\n");
192 return 1;
193 }
194
195 do
196 {
197 FD_ZERO(&read_fd_set);
198 FD_ZERO(&write_fd_set);
199 FD_ZERO(&except_fd_set);
200
201 ret = SPDY_get_timeout(daemon, &timeoutlong);
202 if(SPDY_NO == ret || timeoutlong > 1000)
203 {
204 timeout.tv_sec = 1;
205 timeout.tv_usec = 0;
206 }
207 else
208 {
209 timeout.tv_sec = timeoutlong / 1000;
210 timeout.tv_usec = (timeoutlong % 1000) * 1000;
211 }
212
213 maxfd = SPDY_get_fdset (daemon,
214 &read_fd_set,
215 &write_fd_set,
216 &except_fd_set);
217
218 ret = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout);
219
220 switch(ret) {
221 case -1:
222 printf("select error: %i\n", errno);
223 break;
224 case 0:
225
226 break;
227 default:
228 SPDY_run(daemon);
229
230 break;
231 }
232 }
233 while(waitpid(child,&childstatus,WNOHANG) != child);
234
235 SPDY_stop_daemon(daemon);
236
237 SPDY_deinit();
238
239 return WEXITSTATUS(childstatus);
240}
241
242#define MD5_LEN 32
243
244int
245md5(char *cmd, char *md5_sum)
246{
247 FILE *p = popen(cmd, "r");
248 if (p == NULL) return 0;
249
250 int i, ch;
251 for (i = 0; i < MD5_LEN && isxdigit(ch = fgetc(p)); i++) {
252 *md5_sum++ = ch;
253 }
254
255 *md5_sum = '\0';
256 pclose(p);
257 return i == MD5_LEN;
258}
259
260int
261childproc()
262{
263 char *cmd1;
264 char *cmd2;
265 char md5_sum1[33];
266 char md5_sum2[33];
267 int ret;
268 struct timeval tv1;
269 struct timeval tv2;
270 struct stat st;
271 //int secs;
272 uint64_t usecs;
273
274 asprintf(&cmd1, "spdycat https://127.0.0.1:%i/ | md5sum",port);
275 asprintf(&cmd2, "md5sum " DATA_DIR "spdy-draft.txt");
276
277 gettimeofday(&tv1, NULL);
278 md5(cmd1,md5_sum1);
279 gettimeofday(&tv2, NULL);
280 md5(cmd2,md5_sum2);
281
282 printf("downloaded file md5: %s\n", md5_sum1);
283 printf("original file md5: %s\n", md5_sum2);
284 ret = strcmp(md5_sum1, md5_sum2);
285
286 if(0 == ret && 0 == stat(DATA_DIR "spdy-draft.txt", &st))
287 {
288 usecs = (uint64_t)1000000 * (uint64_t)(tv2.tv_sec - tv1.tv_sec) + tv2.tv_usec - tv1.tv_usec;
289 printf("%lld bytes read in %llu usecs\n", (long long)st.st_size, (long long unsigned )usecs);
290 }
291
292 return ret;
293}
294
295
296int
297main()
298{
299 port = get_port(11123);
300 parent = getpid();
301
302 child = fork();
303 if (-1 == child)
304 {
305 fprintf(stderr, "can't fork, error %d\n", errno);
306 exit(EXIT_FAILURE);
307 }
308
309 if (child == 0)
310 {
311 int ret = childproc();
312 _exit(ret);
313 }
314 else
315 {
316 int ret = parentproc();
317 exit(ret);
318 }
319 return 1;
320}
diff --git a/src/testspdy/test_requests_with_assets.c b/src/testspdy/test_requests_with_assets.c
deleted file mode 100644
index 16d09214..00000000
--- a/src/testspdy/test_requests_with_assets.c
+++ /dev/null
@@ -1,302 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2012 Andrey Uzunov
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file requests_with_assets.c
21 * @brief tests several requests for an HTML and all its assets. For
22 * client spdycat (from spdylay) is used. The latter uses
23 * libxml2.
24 * @author Andrey Uzunov
25 */
26
27#include "platform.h"
28#include "microspdy.h"
29#include "common.h"
30#include <sys/wait.h>
31#include <stdio.h> /* printf, stderr, fprintf */
32#include <sys/types.h> /* pid_t */
33#include <unistd.h> /* _exit, fork */
34#include <stdlib.h> /* exit */
35#include <errno.h> /* errno */
36#include <sys/wait.h> /* pid_t */
37#include "common.h"
38
39#define HTML "<html>\
40<head>\
41<link href=\"file1.css\" rel=\"stylesheet\" type=\"text/css\" />\
42<link href=\"file2.css\" rel=\"stylesheet\" type=\"text/css\" />\
43<link href=\"file3.css\" rel=\"stylesheet\" type=\"text/css\" />\
44</head>\
45<body><b>Hi, this is libmicrospdy!</b>\
46</body></html>"
47
48#define CSS "@media all{body{font-family:verdana,arial;color:#333;background-color:#fff;margin:0;padding:0}#navcontainer ul{padding-left:0;background:#005cb9 url(http://cdn.computerhope.com/backbar.jpg) repeat-x left top;padding-bottom:0;padding-top:0;color:#fff;float:right;font-weight:700;width:100%;border-top:1px solid #333;border-bottom:1px solid #333;margin:0}#navcontainer ul li a{color:#fff;text-decoration:none;float:left;border-top:1px solid #fff;border-right:1px solid #333;border-left:1px solid #fff;border-bottom:1px solid #333;padding:.2em 1em}#navcontainer ul li a:hover{background:url(http://cdn.computerhope.com/backbar2.jpg) repeat-x left top;background-color:#9fcfff;color:#333;border-top:1px solid #333;border-right:1px solid #fff;border-left:1px solid #333;border-bottom:1px solid #fff}a:visited{color:#636}a{color:#2a70d0}#content a{text-decoration:none;border-bottom:1px solid #DBDBDB}#content a:hover,a:active,a:focus{color:#c33;border-bottom:1px solid #c33}img{border:0}#content-container1{float:left;width:100%;background:#fff url(http://cdn.computerhope.com/back.jpg) repeat-y 0}.print,.email,.edit,.share,.up,.down,.book,.folder,.issue,.driver,.history,.news,.btips,.tips,.warn,.phone,.forum,.question{background:url(chs.png) no-repeat top left}#container{padding-left:150px;padding-right:265px}#container .column{position:relative;float:left}#content{width:100%;padding:20px}#left-bar{width:150px;margin-left:-100%;left:225px;padding:10px}#container > #left-bar{left:-190px}#right-bar{width:205px;margin-right:-265px;padding:0 10px}#topad{background:#9fcfff;text-align:center;padding:35px 0 4px}#leftad{clear:both;background:inherit;height:auto;margin:15px 0 0}#content ul{position:relative;margin:10px 0 10px 10px;padding:0}#content ul li{list-style-type:none;background:url(http://cdn.computerhope.com/arrow.png) no-repeat top left;background-position:0 5px;line-height:1.5625;padding:0 0 8px 23px}ol li{margin-bottom:8px;line-height:1.5625}.print,.email,.edit,.share{padding-left:23px}.print{background-position:0 -868px;width:16px;height:16px}.email{background-position:0 -469px;width:16px;height:16px}.edit{background-position:0 -403px;width:16px;height:16px}.share{background-position:0 -1002px;width:16px;height:16px}#left-bar li.title{color:#005cb9;font-weight:700;margin:1em 0}#right-box{width:180px;border:1px solid #005cb9;border-radius:15px 15px 15px 15px;background:#ebebeb;margin:90px 0 0;padding:10px}#right-box ul.poll{margin-top:15px;font-weight:700;margin-bottom:10px}.up,.down{padding-left:20px;text-decoration:none;color:#333}.up{background-position:0 -1068px;width:16px;height:16px}.down{background-position:0 -269px;width:16px;height:16px}#right-box li.title{color:#333;font-weight:700;margin:1em 0 0}#header{background:#9fcfff}#containercol2{background-color:#d0e8ff;width:700px;overflow:hidden;margin:0 auto}#containercol2 ul.col2{width:700px;list-style:none;float:left;padding:0}#containercol2 ul.col2 li h2{border:1px solid #005cb9;background:url(http://cdn.computerhope.com/backbar.jpg) repeat-x left top;color:#fff;font-size:large;text-align:center}#containercol2 ul.col2 li{float:left;width:340px;padding:5px}#containercol2 ul li.headline{border-bottom:1px solid #327dac;background:gray}#bottomad{margin:14px 0 0}input.btn,input.bbtn{color:#333;background:#9fcfff;font-weight:700;border:1px solid #005cb9;border-top:1px solid #eee;border-left:1px solid #eee;cursor:pointer;margin:4px 0 0}input.sbar,input.bsbar{color:#333;width:110px;background:#fff}input.btn{width:115px;font-size:medium}input.sbar{font-size:medium}input.bbtn{width:110px;font-size:large}input.bsbar{width:350px;font-size:18px;margin-right:5px}h1{font-size:175%;margin-bottom:25px;border-bottom:1px solid #dadada;padding-bottom:.17em;letter-spacing:-.05em;font-weight:700}.ce{text-align:center}.tab{margin-left:40px}p{line-height:1.5625}.tabb{margin-left:40px;font-weight:700;line-height:1.4}.dtab{margin-left:80px}.dd{font-weight:700;margin-left:7px}.lb{margin-left:5px}.bld{font-weight:700}.bb{font-size:14pt;color:#005cb9;font-weight:700}.bbl{font-size:14pt;font-weight:700}.nb{color:#005cb9;font-weight:700}.rg{color:gray;font-weight:700}.sg{font-size:10pt;color:gray}.sm{font-size:small}.rb{color:#fff;font-weight:700;text-indent:.3cm}.wt{color:#fff;font-weight:700}.bwt{color:#fff;font-weight:700;font-size:14pt}.large{font-size:x-large}.red{color:red}table{clear:both}.mtable,.mtable2{border:0 solid silver;background-color:#e5e5e5;border-spacing:2px 1px;width:98%;margin-left:auto;margin-right:auto}table.mtable td,table.mtable2 td{border-spacing:5px 10px;padding:9px}table.mtable th,table.mtable2 th{background:#005cb9 url(http://cdn.computerhope.com/backbar.jpg) repeat-x left top;color:#fff;font-weight:700;padding:5px}table.mtable a{border-bottom:0!important}table.mtable tr:hover td{background-color:#eee;cursor:pointer}td{vertical-align:top}.tcb{background:#005cb9 url(http://cdn.computerhope.com/backbar.jpg) repeat-x left top}.tclb{background-color:#9fcfff}.tcllb{background-color:#d0e8ff}.tcw{background-color:#fff}.tcg{background-color:#ebebeb}.tcbl{background-color:#333}.tcy{border:1px solid #005cb9;background-color:#f1f5f9;overflow:auto;padding:15px}.icell{padding-left:15px;padding-bottom:3px}.mlb{background-color:#9fcfff;padding-left:15px;padding-bottom:3px;white-space:nowrap;width:120px;vertical-align:top}#footer{background:url(http://cdn.computerhope.com/footback.jpg) repeat-x left top;background-color:#d0e8ff;clear:both;padding:5px}#footer ul li{list-style-type:none;display:inline;background:inherit;margin:0}#footer li a{float:left;text-decoration:none;width:300px;border-bottom:1px dotted #327dac;padding:0 0 10px 10px}#footer li a:hover{background:#005cb9;color:#fff}#creditfooter{display:none}.legal{text-align:center;font-size:11px}.legal a{text-decoration:none;color:#333}.floatLeft{float:left;clear:left;margin-right:20px;margin-bottom:10px}.floatRight{float:right;margin-left:20px;margin-bottom:10px}.floatRightClear{float:right;clear:right;margin-left:20px}:first-child + html #container{overflow:hidden}.book,.folder,.issue,.driver,.history,.news,.btips,.tips,.warn,.phone,.forum,.question{padding-left:22px;font-weight:700}.book{background-position:0 0;width:17px;height:18px}.tips{background-position:0 -68px;width:17px;height:17px}.btips{background-position:0 -135px;width:17px;height:17px}.history{background-position:0 -202px;width:17px;height:17px}.driver{background-position:0 -335px;width:17px;height:18px}.folder{background-position:0 -535px;width:17px;height:16px}.issue{background-position:0 -601px;width:17px;height:18px}.news{background-position:0 -669px;width:17px;height:14px}.forum{background-position:0 -733px;width:17px;height:18px}.phone{background-position:0 -801px;width:17px;height:17px}.question{background-position:0 -934px;width:17px;height:18px}.warn{background-position:0 -1134px;width:16px;height:16px}textarea,input{border:1px solid #ccc;border-top:1px solid #8d8e90;border-left:1px solid #8d8e90}textarea:focus,input:focus{border:1px solid #005cb9}#left-bar ul,#right-box ul,#footer ul{margin:0;padding:0}#right-box li.poll,#navcontainer ul li{display:inline}#noprint{margin:1px 0 0}#left-bar ul li,#right-box ul li{margin-left:10px;list-style-type:none;padding:0}#right-box a,#left-bar a{color:#333}}@media print{#header,#navcontainer,#topad,#left-bar,#right-bar,#bottomad,#footer,#search,#buttons,#noprint{display:none!important}#content a{text-decoration:none;color:#000}#content,#container{font-family:\"Times New Roman\",Times;background:transparent!important;text-indent:0!important;width:100%!important;border:0!important;float:none!important;position:static!important;overflow:visible!important;line-height:1;margin:0!important;padding:0!important}h1{font-size:14pt;margin-bottom:5px;border-bottom:0;padding-bottom:0;letter-spacing:-.05em;font-weight:700}h2{font-size:13pt}.bb{font-size:13pt;color:#005cb9;font-weight:700}#content ul li:before{content:\"\00bb \0020\"}#content .nb,.bb{font-weight:700;color:#000}table{margin-top:30px;margin-bottom:30px;border-collapse:collapse}th,td{border:1px solid #333}}"
49
50#define JS "var _gaq = _gaq || [];\
51_gaq.push(['_setAccount', 'UA-222222222222222222222222-1']);\
52_gaq.push(['_trackPageview']);\
53(function() {\
54var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;\
55//ga.src = ('https:' == document.location.protocol ? 'ZZZhttps://ssl' : 'ZZZhttp://www') + '.google-analytics.com/ga.js';\
56var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);\
57})();"
58
59int port;
60
61#define NUM_CLIENTS 50
62
63pid_t parent;
64int html_req_count;
65int html_resp_count;
66
67int child_c;
68int children_pid[NUM_CLIENTS];
69int children_status[NUM_CLIENTS];
70
71int session_closed_called;
72
73void
74new_child(pid_t pid)
75{
76 //todo ids overflow
77 children_pid[child_c] = pid;
78 children_status[child_c++] = 1;
79}
80
81int
82alive_children()
83{
84 int i;
85 int dead;
86 int status;
87 int ret = 0;
88
89 for(i=NUM_CLIENTS-1;i>=0;--i)
90 {
91 if (1 != children_status[i])
92 continue;
93 dead = waitpid(children_pid[i], &status, WNOHANG);
94 if (0 == dead)
95 {
96 ret = 1;
97 continue;
98 }
99 if (WEXITSTATUS(status) != 0)
100 {
101 for (i=NUM_CLIENTS-1;i>=0;--i)
102 if (1 == children_status[i])
103 kill (children_pid[i], SIGKILL);
104 exit (WEXITSTATUS(status));
105 }
106 children_status[i] = 2;
107 }
108 return ret;
109}
110
111void
112killchild(int pid, char *message)
113{
114 printf("%s\nkilling %i\n",message,pid);
115 kill(pid, SIGKILL);
116 exit(1);
117}
118
119void
120killparent(int pid, const char *message)
121{
122 printf("%s\nkilling %i\n",message,pid);
123 kill(pid, SIGKILL);
124 _exit(1);
125}
126
127void
128standard_request_handler(void *cls,
129 struct SPDY_Request * request,
130 uint8_t priority,
131 const char *method,
132 const char *path,
133 const char *version,
134 const char *host,
135 const char *scheme,
136 struct SPDY_NameValue * headers,
137 bool more)
138{
139 (void)cls;
140 (void)request;
141 (void)priority;
142 (void)host;
143 (void)scheme;
144 (void)headers;
145 (void)method;
146 (void)version;
147 (void)more;
148
149 struct SPDY_Response *response;
150
151 if(NULL != strstr(path,".css"))
152 {
153 response = SPDY_build_response(200,NULL,SPDY_HTTP_VERSION_1_1,NULL,CSS,strlen(CSS));
154 if(strcmp("/file3.css",path)==0)++html_resp_count;
155 }
156 /*else if(NULL != strstr(path,".js"))
157 {
158 response = SPDY_build_response(200,NULL,SPDY_HTTP_VERSION_1_1,NULL,JS,strlen(JS));
159 }*/
160 else
161 {
162 response = SPDY_build_response(200,NULL,SPDY_HTTP_VERSION_1_1,NULL,HTML,strlen(HTML));
163 }
164
165 if(NULL==response){
166 fprintf(stdout,"no response obj\n");
167 exit(3);
168 }
169
170 if(SPDY_queue_response(request,response,true,false,NULL,(void*)strdup(path))!=SPDY_YES)
171 {
172 fprintf(stdout,"queue\n");
173 exit(4);
174 }
175
176
177}
178
179void
180run_spdycat()
181{
182 pid_t child;
183 ++html_req_count;
184 child = fork();
185 if (child == -1)
186 {
187 killparent(parent,"fork failed\n");
188 }
189
190 if (child == 0)
191 {
192 int devnull;
193
194 close(1);
195 devnull = open("/dev/null", O_WRONLY);
196 if (1 != devnull)
197 {
198 dup2(devnull, 1);
199 close(devnull);
200 }
201 char *uri;
202 asprintf (&uri, "https://127.0.0.1:%i/%i.html", port, html_req_count);
203 execlp ("spdycat", "spdycat", "-anv", uri, NULL);
204 killparent (parent, "executing spdycat failed");
205 }
206 else
207 {
208 new_child(child);
209 }
210}
211
212int
213parentproc()
214{
215 unsigned long long timeoutlong=0;
216 struct timeval timeout;
217 int ret;
218 fd_set read_fd_set;
219 fd_set write_fd_set;
220 fd_set except_fd_set;
221 int maxfd = -1;
222 struct SPDY_Daemon *daemon;
223
224 SPDY_init();
225
226 daemon = SPDY_start_daemon(port,
227 DATA_DIR "cert-and-key.pem",
228 DATA_DIR "cert-and-key.pem",
229 NULL,
230 NULL,
231 &standard_request_handler,
232 NULL,
233 NULL,
234 SPDY_DAEMON_OPTION_END);
235
236 if(NULL==daemon){
237 printf("no daemon\n");
238 return 1;
239 }
240
241 do
242 {
243 if(NUM_CLIENTS > html_req_count)
244 {
245 run_spdycat();
246 }
247
248 FD_ZERO(&read_fd_set);
249 FD_ZERO(&write_fd_set);
250 FD_ZERO(&except_fd_set);
251
252 ret = SPDY_get_timeout(daemon, &timeoutlong);
253 if(SPDY_NO == ret || timeoutlong > 1000)
254 {
255 timeout.tv_sec = 1;
256 timeout.tv_usec = 0;
257 }
258 else
259 {
260 timeout.tv_sec = timeoutlong / 1000;
261 timeout.tv_usec = (timeoutlong % 1000) * 1000;
262 }
263
264 maxfd = SPDY_get_fdset (daemon,
265 &read_fd_set,
266 &write_fd_set,
267 &except_fd_set);
268
269 ret = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout);
270
271 switch(ret) {
272 case -1:
273 printf("select error: %i\n", errno);
274 break;
275 case 0:
276
277 break;
278 default:
279 SPDY_run(daemon);
280
281 break;
282 }
283 }
284 while(alive_children());
285
286 SPDY_stop_daemon(daemon);
287
288 SPDY_deinit();
289
290 return html_resp_count != html_req_count;
291}
292
293int main()
294{
295 parent = getpid();
296 port = get_port(10123);
297
298 int ret = parentproc();
299 exit(ret);
300
301 return 1;
302}
diff --git a/src/testspdy/test_session_timeout.c b/src/testspdy/test_session_timeout.c
deleted file mode 100644
index ec1eced5..00000000
--- a/src/testspdy/test_session_timeout.c
+++ /dev/null
@@ -1,338 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2013 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_timeout.c
21 * @brief tests closing sessions after set timeout. Openssl is used for
22 * client
23 * @author Andrey Uzunov
24 */
25
26#include "platform.h"
27#include "microspdy.h"
28#include "stdio.h"
29#include <sys/wait.h>
30#include <ctype.h>
31#include "common.h"
32#include <sys/time.h>
33#include <sys/stat.h>
34#include "../microspdy/internal.h"
35
36#define TIMEOUT 2
37#define SELECT_MS_TIMEOUT 20
38
39int port;
40
41pid_t parent;
42pid_t child;
43
44int run = 1;
45int chunk_size=1;
46int new_session;
47int closed_session;
48int do_sleep;
49
50
51
52static unsigned long long
53monotonic_time (void)
54{
55#ifdef HAVE_CLOCK_GETTIME
56#ifdef CLOCK_MONOTONIC
57 struct timespec ts;
58 if (0 == clock_gettime (CLOCK_MONOTONIC, &ts))
59 return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
60#endif
61#endif
62 return time (NULL) * 1000;
63}
64
65
66static void
67killchild(char *msg)
68{
69 printf("%s\n",msg);
70 kill(child, SIGKILL);
71 exit(1);
72}
73
74
75static void
76killparent(char *msg)
77{
78 printf("%s\n",msg);
79 kill(parent, SIGKILL);
80 _exit(1);
81}
82
83
84static void
85new_session_cb (void *cls,
86 struct SPDY_Session * session)
87{
88 (void)cls;
89 (void)session;
90
91 if(!new_session)do_sleep = 1;
92 new_session = 1;
93 printf("new session\n");
94}
95
96
97static void
98closed_session_cb (void *cls,
99 struct SPDY_Session * session,
100 int by_client)
101{
102 (void)cls;
103 (void)session;
104
105 printf("closed_session_cb called\n");
106
107 if(SPDY_YES == by_client)
108 {
109 killchild("closed by the client");
110 }
111 if(closed_session)
112 {
113 killchild("closed_session_cb called twice");
114 }
115
116 closed_session = 1;
117}
118
119
120static int
121parentproc()
122{
123 int childstatus;
124 unsigned long long timeoutlong=0;
125 struct timeval timeout;
126 int ret;
127 fd_set read_fd_set;
128 fd_set write_fd_set;
129 fd_set except_fd_set;
130 int maxfd = -1;
131 struct SPDY_Daemon *daemon;
132 unsigned long long beginning = 0;
133 unsigned long long now;
134
135 SPDY_init();
136
137 daemon = SPDY_start_daemon(port,
138 DATA_DIR "cert-and-key.pem",
139 DATA_DIR "cert-and-key.pem",
140 &new_session_cb,
141 &closed_session_cb,
142 NULL,
143 NULL,
144 NULL,
145 SPDY_DAEMON_OPTION_SESSION_TIMEOUT,
146 TIMEOUT,
147 SPDY_DAEMON_OPTION_END);
148
149 if(NULL==daemon){
150 printf("no daemon\n");
151 return 1;
152 }
153
154 do
155 {
156 do_sleep=0;
157 FD_ZERO(&read_fd_set);
158 FD_ZERO(&write_fd_set);
159 FD_ZERO(&except_fd_set);
160
161 ret = SPDY_get_timeout(daemon, &timeoutlong);
162
163 if(new_session && !closed_session)
164 {
165 if(SPDY_NO == ret)
166 {
167 killchild("SPDY_get_timeout returned wrong SPDY_NO");
168 }
169 /*if(timeoutlong)
170 {
171 killchild("SPDY_get_timeout returned wrong timeout");
172 }*/
173 now = monotonic_time ();
174 if(now - beginning > TIMEOUT*1000 + SELECT_MS_TIMEOUT)
175 {
176 printf("Started at: %llums\n",beginning);
177 printf("Now is: %llums\n",now);
178 printf("Timeout is: %i\n",TIMEOUT);
179 printf("Select Timeout is: %ims\n",SELECT_MS_TIMEOUT);
180 printf("SPDY_get_timeout gave: %llums\n",timeoutlong);
181 killchild("Timeout passed but session was not closed");
182 }
183 if(timeoutlong > beginning + TIMEOUT *1000)
184 {
185 printf("Started at: %llums\n",beginning);
186 printf("Now is: %llums\n",now);
187 printf("Timeout is: %i\n",TIMEOUT);
188 printf("Select Timeout is: %ims\n",SELECT_MS_TIMEOUT);
189 printf("SPDY_get_timeout gave: %llums\n",timeoutlong);
190 killchild("SPDY_get_timeout returned wrong timeout");
191 }
192 }
193 else
194 {
195 if(SPDY_YES == ret)
196 {
197 killchild("SPDY_get_timeout returned wrong SPDY_YES");
198 }
199 }
200
201 if(SPDY_NO == ret || timeoutlong >= 1000)
202 {
203 timeout.tv_sec = 1;
204 timeout.tv_usec = 0;
205 }
206 else
207 {
208 timeout.tv_sec = timeoutlong / 1000;
209 timeout.tv_usec = (timeoutlong % 1000) * 1000;
210 }
211
212 //ignore values
213 timeout.tv_sec = 0;
214 timeout.tv_usec = SELECT_MS_TIMEOUT * 1000;
215
216 maxfd = SPDY_get_fdset (daemon,
217 &read_fd_set,
218 &write_fd_set,
219 &except_fd_set);
220
221 ret = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout);
222
223 switch(ret) {
224 case -1:
225 printf("select error: %i\n", errno);
226 break;
227 case 0:
228 /*if(new_session)
229 {
230 killchild("select returned wrong number");
231 }*/
232 break;
233 default:
234 SPDY_run(daemon);
235 if(0 == beginning)
236 {
237 beginning = monotonic_time ();
238 }
239 /*if(do_sleep)
240 {
241 sleep(TIMEOUT);
242 do_sleep = 0;
243 }*/
244 break;
245 }
246 }
247 while(waitpid(child,&childstatus,WNOHANG) != child);
248
249 if(!new_session || !closed_session)
250 {
251 killchild("child is dead, callback wasn't called");
252 }
253
254 ret = SPDY_get_timeout(daemon, &timeoutlong);
255
256 if(SPDY_YES == ret)
257 {
258 killchild("SPDY_get_timeout returned wrong SPDY_YES after child died");
259 }
260
261 SPDY_stop_daemon(daemon);
262
263 SPDY_deinit();
264
265 return 0;
266}
267
268
269static int
270childproc()
271{
272 pid_t devnull;
273 int out;
274
275 out=dup(1);
276 if (-1 == out)
277 abort();
278 //close(0);
279 close(1);
280 close(2);
281 /*devnull = open("/dev/null", O_RDONLY);
282 if (0 != devnull)
283 {
284 dup2(devnull, 0);
285 close(devnull);
286 }*/
287 devnull = open("/dev/null", O_WRONLY);
288 if (-1 == devnull)
289 abort ();
290 if (1 != devnull)
291 {
292 dup2(devnull, 1);
293 close(devnull);
294 }
295 devnull = open("/dev/null", O_WRONLY);
296 if (-1 == devnull)
297 abort ();
298 if (2 != devnull)
299 {
300 dup2(devnull, 2);
301 close(devnull);
302 }
303 char *uri;
304 asprintf (&uri, "127.0.0.1:%i", port);
305 execlp ("openssl", "openssl", "s_client", "-connect", uri, NULL);
306 close(1);
307 dup2(out,1);
308 close(out);
309 killparent ("executing openssl failed");
310 return 1;
311}
312
313
314int
315main()
316{
317 port = get_port(11123);
318 parent = getpid();
319
320 child = fork();
321 if (-1 == child)
322 {
323 fprintf(stderr, "can't fork, error %d\n", errno);
324 exit(EXIT_FAILURE);
325 }
326
327 if (child == 0)
328 {
329 int ret = childproc();
330 _exit(ret);
331 }
332 else
333 {
334 int ret = parentproc();
335 exit(ret);
336 }
337 return 1;
338}
diff --git a/src/testspdy/test_struct_namevalue.c b/src/testspdy/test_struct_namevalue.c
deleted file mode 100644
index 54e79349..00000000
--- a/src/testspdy/test_struct_namevalue.c
+++ /dev/null
@@ -1,346 +0,0 @@
1/*
2 This file is part of libmicrospdy
3 Copyright Copyright (C) 2012 Andrey Uzunov
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file struct_namevalue.c
21 * @brief tests all the API functions for handling struct SPDY_NameValue
22 * @author Andrey Uzunov
23 */
24
25#include "platform.h"
26#include "microspdy.h"
27#include "common.h"
28#include "../microspdy/structures.h"
29#include "../microspdy/alstructures.h"
30
31char *pairs[] = {"one","1","two","2","three","3","four","4","five","5"};
32char *pairs_with_dups[] = {"one","1","two","2","one","11","two","22","three","3","two","222","two","2222","four","","five","5"};//82
33char *pairs_with_empty[] = {"name","","name","value"};
34char *pairs_different[] = {"30","thirty","40","fouthy"};
35int size;
36int size2;
37int brake_at = 3;
38bool flag;
39
40
41int
42iterate_cb (void *cls, const char *name, const char * const * value, int num_values)
43{
44 int *c = (int*)cls;
45
46 if(*c < 0 || *c > size)
47 exit(11);
48
49 if(strcmp(name,pairs[*c]) != 0)
50 {
51 FAIL_TEST("name is wrong\n");
52 }
53
54 if(1 != num_values)
55 {
56 FAIL_TEST("num_values is wrong\n");
57 }
58
59 if(strcmp(value[0],pairs[(*c)+1]) != 0)
60 {
61 FAIL_TEST("value is wrong\n");
62 }
63
64 (*c)+=2;
65
66 return SPDY_YES;
67}
68
69int
70iterate_brake_cb (void *cls, const char *name, const char * const *value, int num_values)
71{
72 (void)name;
73 (void)value;
74 (void)num_values;
75
76 int *c = (int*)cls;
77
78 if(*c < 0 || *c >= brake_at)
79 {
80 FAIL_TEST("iteration was not interrupted\n");
81 }
82
83 (*c)++;
84
85 if(*c == brake_at) return SPDY_NO;
86
87 return SPDY_YES;
88}
89
90int
91main()
92{
93 SPDY_init();
94
95 const char *const*value;
96 const char *const*value2;
97 int i;
98 int j;
99 int cls = 0;
100 int ret;
101 int ret2;
102 void *ob1;
103 void *ob2;
104 void *ob3;
105 void *stream;
106 char data[] = "anything";
107 struct SPDY_NameValue *container;
108 struct SPDY_NameValue *container2;
109 struct SPDY_NameValue *container3;
110 struct SPDY_NameValue *container_arr[2];
111
112 size = sizeof(pairs)/sizeof(pairs[0]);
113
114 if(NULL == (container = SPDY_name_value_create ()))
115 {
116 FAIL_TEST("SPDY_name_value_create failed\n");
117 }
118
119 if(NULL != SPDY_name_value_lookup (container, "anything", &ret))
120 {
121 FAIL_TEST("SPDY_name_value_lookup failed\n");
122 }
123
124 if(SPDY_name_value_iterate (container, NULL, NULL) != 0)
125 {
126 FAIL_TEST("SPDY_name_value_iterate failed\n");
127 }
128
129 for(i=0;i<size; i+=2)
130 {
131 if(SPDY_YES != SPDY_name_value_add(container,pairs[i],pairs[i+1]))
132 {
133 FAIL_TEST("SPDY_name_value_add failed\n");
134 }
135
136 if(SPDY_name_value_iterate (container, NULL, NULL) != ((i / 2) + 1))
137 {
138 FAIL_TEST("SPDY_name_value_iterate failed\n");
139 }
140 }
141
142 if(NULL != SPDY_name_value_lookup (container, "anything", &ret))
143 {
144 FAIL_TEST("SPDY_name_value_lookup failed\n");
145 }
146
147 for(i=size - 2; i >= 0; i-=2)
148 {
149 value = SPDY_name_value_lookup(container,pairs[i], &ret);
150 if(NULL == value || 1 !=ret || strcmp(value[0], pairs[i+1]) != 0)
151 {
152 printf("%p; %i; %i\n", value, ret,
153 (NULL == value) ? -1 : strcmp(value[0], pairs[i+1]));
154 FAIL_TEST("SPDY_name_value_lookup failed\n");
155 }
156 }
157
158 SPDY_name_value_iterate (container, &iterate_cb, &cls);
159
160 cls = 0;
161 if(SPDY_name_value_iterate (container, &iterate_brake_cb, &cls) != brake_at)
162 {
163 FAIL_TEST("SPDY_name_value_iterate with brake failed\n");
164 }
165
166 SPDY_name_value_destroy(container);
167
168 //check everything with NULL values
169 for(i=0; i<7; ++i)
170 {
171 printf("%i ",i);
172 ob1 = (i & 4) ? data : NULL;
173 ob2 = (i & 2) ? data : NULL;
174 ob3 = (i & 1) ? data : NULL;
175 if(SPDY_INPUT_ERROR != SPDY_name_value_add(ob1,ob2,ob3))
176 {
177 FAIL_TEST("SPDY_name_value_add with NULLs failed\n");
178 }
179 }
180 printf("\n");
181 fflush(stdout);
182
183 if(SPDY_INPUT_ERROR != SPDY_name_value_iterate(NULL,NULL,NULL))
184 {
185 FAIL_TEST("SPDY_name_value_iterate with NULLs failed\n");
186 }
187
188 for(i=0; i<7; ++i)
189 {
190 printf("%i ",i);
191 ob1 = (i & 4) ? data : NULL;
192 ob2 = (i & 2) ? data : NULL;
193 ob3 = (i & 1) ? data : NULL;
194 if(NULL != SPDY_name_value_lookup(ob1,ob2,ob3))
195 {
196 FAIL_TEST("SPDY_name_value_lookup with NULLs failed\n");
197 }
198 }
199 printf("\n");
200 SPDY_name_value_destroy(NULL);
201
202 if(NULL == (container = SPDY_name_value_create ()))
203 {
204 FAIL_TEST("SPDY_name_value_create failed\n");
205 }
206
207 size = sizeof(pairs_with_dups)/sizeof(pairs_with_dups[0]);
208
209 for(i=0;i<size; i+=2)
210 {
211 if(SPDY_YES != SPDY_name_value_add(container,pairs_with_dups[i],pairs_with_dups[i+1]))
212 {
213 FAIL_TEST("SPDY_name_value_add failed\n");
214 }
215 }
216 if(SPDY_name_value_iterate (container, NULL, NULL) != atoi(pairs_with_dups[size - 1]))
217 {
218 FAIL_TEST("SPDY_name_value_iterate failed\n");
219 }
220 for(i=size - 2; i >= 0; i-=2)
221 {
222 value = SPDY_name_value_lookup(container,pairs_with_dups[i], &ret);
223 if(NULL == value)
224 {
225 FAIL_TEST("SPDY_name_value_lookup failed\n");
226 }
227 flag = false;
228 for(j=0; j<ret; ++j)
229 if(0 == strcmp(pairs_with_dups[i + 1], value[j]))
230 {
231 if(flag)
232 FAIL_TEST("SPDY_name_value_lookup failed\n");
233 flag=true;
234 }
235
236 if(!flag)
237 FAIL_TEST("SPDY_name_value_lookup failed\n");
238 }
239 if(SPDY_NO != SPDY_name_value_add(container,pairs_with_dups[0],pairs_with_dups[1]))
240 FAIL_TEST("SPDY_name_value_add failed\n");
241
242 SPDY_name_value_destroy(container);
243
244 if(NULL == (container = SPDY_name_value_create ()))
245 {
246 FAIL_TEST("SPDY_name_value_create failed\n");
247 }
248
249 size = sizeof(pairs_with_empty)/sizeof(pairs_with_empty[0]);
250
251 for(i=0;i<size; i+=2)
252 {
253 if(SPDY_YES != SPDY_name_value_add(container,pairs_with_empty[i],pairs_with_empty[i+1]))
254 {
255 FAIL_TEST("SPDY_name_value_add failed\n");
256 }
257 value = SPDY_name_value_lookup(container,pairs_with_empty[i], &ret);
258 if(NULL == value || 1 != ret)
259 {
260 printf("%p; %i\n", value, ret);
261 FAIL_TEST("SPDY_name_value_lookup failed\n");
262 }
263 }
264
265 ret = SPDY_name_value_iterate(container, NULL, NULL);
266 if(SPDY_INPUT_ERROR != SPDY_name_value_add(container, "capitalLeter","anything")
267 || SPDY_name_value_iterate(container, NULL, NULL) != ret)
268 {
269 FAIL_TEST("SPDY_name_value_add failed\n");
270 }
271
272 SPDY_name_value_destroy(container);
273
274 if(NULL == (container = SPDY_name_value_create ()))
275 {
276 FAIL_TEST("SPDY_name_value_create failed\n");
277 }
278
279 size = sizeof(pairs_with_dups)/sizeof(pairs_with_dups[0]);
280
281 for(i=0;i<size; i+=2)
282 {
283 if(SPDY_YES != SPDY_name_value_add(container,pairs_with_dups[i],pairs_with_dups[i+1]))
284 {
285 FAIL_TEST("SPDY_name_value_add failed\n");
286 }
287 }
288
289 if(NULL == (container2 = SPDY_name_value_create ()))
290 {
291 FAIL_TEST("SPDY_name_value_create failed\n");
292 }
293
294 size2 = sizeof(pairs_different)/sizeof(pairs_different[0]);
295
296 for(i=0;i<size2; i+=2)
297 {
298 if(SPDY_YES != SPDY_name_value_add(container2,pairs_different[i],pairs_different[i+1]))
299 {
300 FAIL_TEST("SPDY_name_value_add failed\n");
301 }
302 }
303
304 container_arr[0] = container;
305 container_arr[1] = container2;
306 if(0 > (ret = SPDYF_name_value_to_stream(container_arr, 2, &stream)) || NULL == stream)
307 FAIL_TEST("SPDYF_name_value_to_stream failed\n");
308 ret = SPDYF_name_value_from_stream(stream, ret, &container3);
309 if(SPDY_YES != ret)
310 FAIL_TEST("SPDYF_name_value_from_stream failed\n");
311
312 if(SPDY_name_value_iterate(container3, NULL, NULL)
313 != (SPDY_name_value_iterate(container, NULL, NULL) + SPDY_name_value_iterate(container2, NULL, NULL)))
314 FAIL_TEST("SPDYF_name_value_from_stream failed\n");
315
316 for(i=size - 2; i >= 0; i-=2)
317 {
318 value = SPDY_name_value_lookup(container,pairs_with_dups[i], &ret);
319 if(NULL == value)
320 FAIL_TEST("SPDY_name_value_lookup failed\n");
321 value2 = SPDY_name_value_lookup(container3,pairs_with_dups[i], &ret2);
322 if(NULL == value2)
323 FAIL_TEST("SPDY_name_value_lookup failed\n");
324
325 for(j=0; j<ret; ++j)
326 if(0 != strcmp(value2[j], value[j]))
327 FAIL_TEST("SPDY_name_value_lookup failed\n");
328 }
329 for(i=size2 - 2; i >= 0; i-=2)
330 {
331 value = SPDY_name_value_lookup(container2,pairs_different[i], &ret);
332 if(NULL == value)
333 FAIL_TEST("SPDY_name_value_lookup failed\n");
334 value2 = SPDY_name_value_lookup(container3,pairs_different[i], &ret2);
335 if(NULL == value2)
336 FAIL_TEST("SPDY_name_value_lookup failed\n");
337
338 for(j=0; j<ret; ++j)
339 if(0 != strcmp(value2[j], value[j]))
340 FAIL_TEST("SPDY_name_value_lookup failed\n");
341 }
342
343 SPDY_deinit();
344
345 return 0;
346}