diff options
Diffstat (limited to 'src')
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 | |||
7 | endif | 7 | endif |
8 | endif | 8 | endif |
9 | endif | 9 | endif |
10 | if ENABLE_SPDY | ||
11 | if HAVE_OPENSSL | ||
12 | microspdy = microspdy | ||
13 | if HAVE_CURL | ||
14 | microspdy += spdy2http | ||
15 | endif | ||
16 | #if HAVE_SPDYLAY | ||
17 | microspdy += testspdy | ||
18 | #endif | ||
19 | endif | ||
20 | endif | ||
21 | 10 | ||
22 | SUBDIRS = include platform microhttpd $(microspdy) $(curltests) $(zzuftests) . | 11 | SUBDIRS = include platform microhttpd $(curltests) $(zzuftests) . |
23 | 12 | ||
24 | if BUILD_EXAMPLES | 13 | if BUILD_EXAMPLES |
25 | SUBDIRS += examples | 14 | SUBDIRS += examples |
@@ -27,5 +16,4 @@ endif | |||
27 | 16 | ||
28 | EXTRA_DIST = \ | 17 | EXTRA_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 | |||
4 | Network Working Group M. Belshe | ||
5 | Internet-Draft Twist | ||
6 | Expires: August 4, 2012 R. Peon | ||
7 | Google, Inc | ||
8 | Feb 2012 | ||
9 | |||
10 | |||
11 | SPDY Protocol | ||
12 | draft-mbelshe-httpbis-spdy-00 | ||
13 | |||
14 | Abstract | ||
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 | |||
25 | Status 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 | |||
42 | Copyright 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 | |||
55 | Belshe & Peon Expires August 4, 2012 [Page 1] | ||
56 | |||
57 | Internet-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 | |||
66 | Table 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 | |||
111 | Belshe & Peon Expires August 4, 2012 [Page 2] | ||
112 | |||
113 | Internet-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 | |||
167 | Belshe & Peon Expires August 4, 2012 [Page 3] | ||
168 | |||
169 | Internet-Draft SPDY Feb 2012 | ||
170 | |||
171 | |||
172 | 1. 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 | |||
212 | 1.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 | |||
223 | Belshe & Peon Expires August 4, 2012 [Page 4] | ||
224 | |||
225 | Internet-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 | |||
231 | 1.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 | |||
279 | Belshe & Peon Expires August 4, 2012 [Page 5] | ||
280 | |||
281 | Internet-Draft SPDY Feb 2012 | ||
282 | |||
283 | |||
284 | 2. SPDY Framing Layer | ||
285 | |||
286 | 2.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 | |||
301 | 2.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 | |||
319 | 2.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 | |||
335 | Belshe & Peon Expires August 4, 2012 [Page 6] | ||
336 | |||
337 | Internet-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 | |||
363 | 2.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 | |||
391 | Belshe & Peon Expires August 4, 2012 [Page 7] | ||
392 | |||
393 | Internet-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 | |||
430 | 2.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 | |||
447 | Belshe & Peon Expires August 4, 2012 [Page 8] | ||
448 | |||
449 | Internet-Draft SPDY Feb 2012 | ||
450 | |||
451 | |||
452 | 2.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 | |||
462 | 2.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 | |||
494 | 2.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 | |||
503 | Belshe & Peon Expires August 4, 2012 [Page 9] | ||
504 | |||
505 | Internet-Draft SPDY Feb 2012 | ||
506 | |||
507 | |||
508 | (Section 2.3.6) state. | ||
509 | |||
510 | 2.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 | |||
516 | 2.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 | |||
525 | 2.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 | |||
534 | 2.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 | |||
544 | 2.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 | |||
559 | Belshe & Peon Expires August 4, 2012 [Page 10] | ||
560 | |||
561 | Internet-Draft SPDY Feb 2012 | ||
562 | |||
563 | |||
564 | 2.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 | |||
590 | 2.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 | |||
597 | 2.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 | |||
615 | Belshe & Peon Expires August 4, 2012 [Page 11] | ||
616 | |||
617 | Internet-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 | |||
628 | 2.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 | |||
649 | 2.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 | |||
655 | 2.6. Control frame types | ||
656 | |||
657 | 2.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 | |||
671 | Belshe & Peon Expires August 4, 2012 [Page 12] | ||
672 | |||
673 | Internet-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 | |||
727 | Belshe & Peon Expires August 4, 2012 [Page 13] | ||
728 | |||
729 | Internet-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 | |||
744 | 2.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 | |||
783 | Belshe & Peon Expires August 4, 2012 [Page 14] | ||
784 | |||
785 | Internet-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 | |||
800 | 2.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 | |||
839 | Belshe & Peon Expires August 4, 2012 [Page 15] | ||
840 | |||
841 | Internet-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 | |||
883 | 2.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 | |||
895 | Belshe & Peon Expires August 4, 2012 [Page 16] | ||
896 | |||
897 | Internet-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 | |||
951 | Belshe & Peon Expires August 4, 2012 [Page 17] | ||
952 | |||
953 | Internet-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 | |||
1007 | Belshe & Peon Expires August 4, 2012 [Page 18] | ||
1008 | |||
1009 | Internet-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 | |||
1036 | 2.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 | |||
1063 | Belshe & Peon Expires August 4, 2012 [Page 19] | ||
1064 | |||
1065 | Internet-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 | |||
1082 | 2.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 | |||
1119 | Belshe & Peon Expires August 4, 2012 [Page 20] | ||
1120 | |||
1121 | Internet-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 | |||
1157 | 2.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 | |||
1175 | Belshe & Peon Expires August 4, 2012 [Page 21] | ||
1176 | |||
1177 | Internet-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 | |||
1214 | 2.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 | |||
1231 | Belshe & Peon Expires August 4, 2012 [Page 22] | ||
1232 | |||
1233 | Internet-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 | |||
1287 | Belshe & Peon Expires August 4, 2012 [Page 23] | ||
1288 | |||
1289 | Internet-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 | |||
1333 | 2.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 | |||
1343 | Belshe & Peon Expires August 4, 2012 [Page 24] | ||
1344 | |||
1345 | Internet-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 | |||
1399 | Belshe & Peon Expires August 4, 2012 [Page 25] | ||
1400 | |||
1401 | Internet-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 | |||
1441 | 2.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 | |||
1455 | Belshe & Peon Expires August 4, 2012 [Page 26] | ||
1456 | |||
1457 | Internet-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 | |||
1511 | Belshe & Peon Expires August 4, 2012 [Page 27] | ||
1512 | |||
1513 | Internet-Draft SPDY Feb 2012 | ||
1514 | |||
1515 | |||
1516 | 2.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 | |||
1567 | Belshe & Peon Expires August 4, 2012 [Page 28] | ||
1568 | |||
1569 | Internet-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 | |||
1623 | Belshe & Peon Expires August 4, 2012 [Page 29] | ||
1624 | |||
1625 | Internet-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 | |||
1679 | Belshe & Peon Expires August 4, 2012 [Page 30] | ||
1680 | |||
1681 | Internet-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 | |||
1735 | Belshe & Peon Expires August 4, 2012 [Page 31] | ||
1736 | |||
1737 | Internet-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 | |||
1791 | Belshe & Peon Expires August 4, 2012 [Page 32] | ||
1792 | |||
1793 | Internet-Draft SPDY Feb 2012 | ||
1794 | |||
1795 | |||
1796 | 3. 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 | |||
1807 | 3.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 | |||
1816 | 3.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 | |||
1847 | Belshe & Peon Expires August 4, 2012 [Page 33] | ||
1848 | |||
1849 | Internet-Draft SPDY Feb 2012 | ||
1850 | |||
1851 | |||
1852 | SYN_STREAMs from the remote, the GOAWAY will contain a last-stream-id | ||
1853 | of 0. | ||
1854 | |||
1855 | 3.2. HTTP Request/Response | ||
1856 | |||
1857 | 3.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 | |||
1903 | Belshe & Peon Expires August 4, 2012 [Page 34] | ||
1904 | |||
1905 | Internet-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 | |||
1937 | 3.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 | |||
1959 | Belshe & Peon Expires August 4, 2012 [Page 35] | ||
1960 | |||
1961 | Internet-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 | |||
1980 | 3.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 | |||
1996 | 3.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 | |||
2004 | 3.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 | |||
2015 | Belshe & Peon Expires August 4, 2012 [Page 36] | ||
2016 | |||
2017 | Internet-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 | |||
2037 | 3.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 | |||
2071 | Belshe & Peon Expires August 4, 2012 [Page 37] | ||
2072 | |||
2073 | Internet-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 | |||
2083 | 3.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 | |||
2127 | Belshe & Peon Expires August 4, 2012 [Page 38] | ||
2128 | |||
2129 | Internet-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 | |||
2137 | 3.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 | |||
2183 | Belshe & Peon Expires August 4, 2012 [Page 39] | ||
2184 | |||
2185 | Internet-Draft SPDY Feb 2012 | ||
2186 | |||
2187 | |||
2188 | 4. 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 | |||
2197 | 4.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 | |||
2211 | 4.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 | |||
2225 | 4.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 | |||
2239 | Belshe & Peon Expires August 4, 2012 [Page 40] | ||
2240 | |||
2241 | Internet-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 | |||
2254 | 4.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 | |||
2274 | 4.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 | |||
2295 | Belshe & Peon Expires August 4, 2012 [Page 41] | ||
2296 | |||
2297 | Internet-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 | |||
2305 | 4.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 | |||
2318 | 4.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 | |||
2334 | 4.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 | |||
2351 | Belshe & Peon Expires August 4, 2012 [Page 42] | ||
2352 | |||
2353 | Internet-Draft SPDY Feb 2012 | ||
2354 | |||
2355 | |||
2356 | 5. Security Considerations | ||
2357 | |||
2358 | 5.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 | |||
2363 | 5.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 | |||
2373 | 5.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 | |||
2380 | 5.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 | |||
2407 | Belshe & Peon Expires August 4, 2012 [Page 43] | ||
2408 | |||
2409 | Internet-Draft SPDY Feb 2012 | ||
2410 | |||
2411 | |||
2412 | 6. Privacy Considerations | ||
2413 | |||
2414 | 6.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 | |||
2425 | 6.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 | |||
2463 | Belshe & Peon Expires August 4, 2012 [Page 44] | ||
2464 | |||
2465 | Internet-Draft SPDY Feb 2012 | ||
2466 | |||
2467 | |||
2468 | 7. 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 | |||
2519 | Belshe & Peon Expires August 4, 2012 [Page 45] | ||
2520 | |||
2521 | Internet-Draft SPDY Feb 2012 | ||
2522 | |||
2523 | |||
2524 | 8. 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 | |||
2575 | Belshe & Peon Expires August 4, 2012 [Page 46] | ||
2576 | |||
2577 | Internet-Draft SPDY Feb 2012 | ||
2578 | |||
2579 | |||
2580 | 9. 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 | |||
2631 | Belshe & Peon Expires August 4, 2012 [Page 47] | ||
2632 | |||
2633 | Internet-Draft SPDY Feb 2012 | ||
2634 | |||
2635 | |||
2636 | 10. 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 | |||
2687 | Belshe & Peon Expires August 4, 2012 [Page 48] | ||
2688 | |||
2689 | Internet-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 | |||
2743 | Belshe & Peon Expires August 4, 2012 [Page 49] | ||
2744 | |||
2745 | Internet-Draft SPDY Feb 2012 | ||
2746 | |||
2747 | |||
2748 | Appendix 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 | |||
2799 | Belshe & Peon Expires August 4, 2012 [Page 50] | ||
2800 | |||
2801 | Internet-Draft SPDY Feb 2012 | ||
2802 | |||
2803 | |||
2804 | Authors' 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 | |||
2855 | Belshe & 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 |
14 | endif | 14 | endif |
15 | 15 | ||
16 | if ENABLE_SPDY | ||
17 | spdyex = \ | ||
18 | spdy_event_loop \ | ||
19 | spdy_fileserver \ | ||
20 | spdy_response_with_callback | ||
21 | |||
22 | if HAVE_SPDYLAY | ||
23 | spdyex += mhd2spdy | ||
24 | endif | ||
25 | endif | ||
26 | |||
27 | 16 | ||
28 | # example programs | 17 | # example programs |
29 | noinst_PROGRAMS = \ | 18 | noinst_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 | ||
44 | if ENABLE_HTTPS | 32 | if 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 | ||
101 | mhd2spdy_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 | ||
106 | mhd2spdy_LDADD = \ | ||
107 | $(top_builddir)/src/microhttpd/libmicrohttpd.la \ | ||
108 | -lssl -lcrypto -lspdylay | ||
109 | |||
110 | benchmark_SOURCES = \ | 89 | benchmark_SOURCES = \ |
111 | benchmark.c | 90 | benchmark.c |
112 | benchmark_CPPFLAGS = \ | 91 | benchmark_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 | ||
182 | spdy_event_loop_SOURCES = \ | ||
183 | spdy_event_loop.c | ||
184 | spdy_event_loop_LDADD = \ | ||
185 | $(top_builddir)/src/microspdy/libmicrospdy.la \ | ||
186 | -lz | ||
187 | |||
188 | spdy_fileserver_SOURCES = \ | ||
189 | spdy_fileserver.c | ||
190 | spdy_fileserver_LDADD = \ | ||
191 | $(top_builddir)/src/microspdy/libmicrospdy.la \ | ||
192 | -lz | ||
193 | |||
194 | spdy_response_with_callback_SOURCES = \ | ||
195 | spdy_response_with_callback.c | ||
196 | spdy_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 | |||
36 | static int run = 1; | ||
37 | //static int spdy_close = 0; | ||
38 | |||
39 | |||
40 | static void | ||
41 | catch_signal(int signal) | ||
42 | { | ||
43 | (void)signal; | ||
44 | //spdy_close = 1; | ||
45 | run = 0; | ||
46 | } | ||
47 | |||
48 | |||
49 | void | ||
50 | print_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 | |||
65 | int | ||
66 | run_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 | |||
229 | void | ||
230 | display_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 | |||
254 | int | ||
255 | main (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 | |||
29 | void * | ||
30 | http_cb_log(void * cls, | ||
31 | const 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 | |||
47 | static int | ||
48 | http_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 | |||
78 | static ssize_t | ||
79 | http_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 | |||
148 | static void | ||
149 | http_cb_response_done(void *cls) | ||
150 | { | ||
151 | (void)cls; | ||
152 | //TODO remove | ||
153 | } | ||
154 | |||
155 | int | ||
156 | http_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 | |||
323 | void | ||
324 | http_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 | |||
371 | void | ||
372 | http_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 | |||
30 | int | ||
31 | http_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 | |||
41 | void * http_cb_log(void * cls, const char * uri); | ||
42 | |||
43 | |||
44 | void | ||
45 | http_create_response(struct Proxy* proxy, char **nv); | ||
46 | |||
47 | |||
48 | void | ||
49 | http_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 | */ | ||
43 | static void | ||
44 | spdy_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 | */ | ||
56 | void | ||
57 | spdy_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 | |||
66 | static ssize_t | ||
67 | spdy_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 | */ | ||
135 | static ssize_t | ||
136 | spdy_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 | */ | ||
218 | static ssize_t | ||
219 | spdy_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 | |||
285 | static void | ||
286 | spdy_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 | |||
320 | void | ||
321 | spdy_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 | */ | ||
385 | static void | ||
386 | spdy_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 | */ | ||
418 | static void | ||
419 | spdy_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 | |||
463 | static void | ||
464 | spdy_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 | */ | ||
489 | static void | ||
490 | spdy_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 | */ | ||
508 | static int | ||
509 | spdy_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 | */ | ||
538 | void | ||
539 | spdy_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 | |||
552 | static int | ||
553 | spdy_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 | */ | ||
574 | static int | ||
575 | spdy_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 | |||
613 | static void | ||
614 | spdy_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 | */ | ||
634 | static void | ||
635 | spdy_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 | /* | ||
649 | void | ||
650 | spdy_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 | */ | ||
670 | bool | ||
671 | spdy_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 | */ | ||
700 | int | ||
701 | spdy_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 | */ | ||
722 | struct SPDY_Connection * | ||
723 | spdy_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 | |||
812 | void | ||
813 | spdy_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 | |||
841 | int | ||
842 | spdy_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 | /* | ||
909 | void | ||
910 | spdy_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 | |||
982 | int | ||
983 | spdy_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 | /* | ||
1055 | void | ||
1056 | spdy_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 | |||
1107 | void | ||
1108 | spdy_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 | |||
30 | struct SPDY_Connection * | ||
31 | spdy_connect(const struct URI *uri, | ||
32 | uint16_t port, | ||
33 | bool is_tls); | ||
34 | |||
35 | |||
36 | void | ||
37 | spdy_ctl_poll(struct pollfd *pollfd, | ||
38 | struct SPDY_Connection *connection); | ||
39 | |||
40 | |||
41 | bool | ||
42 | spdy_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 | |||
48 | int | ||
49 | spdy_exec_io(struct SPDY_Connection *connection); | ||
50 | |||
51 | |||
52 | void | ||
53 | spdy_diec(const char *func, | ||
54 | int error_code); | ||
55 | |||
56 | |||
57 | int | ||
58 | spdy_request(const char **nv, | ||
59 | struct Proxy *proxy, | ||
60 | bool with_body); | ||
61 | |||
62 | |||
63 | void | ||
64 | spdy_ssl_init_ssl_ctx(SSL_CTX *ssl_ctx, | ||
65 | uint16_t *spdy_proto_version); | ||
66 | |||
67 | |||
68 | void | ||
69 | spdy_free_connection(struct SPDY_Connection * connection); | ||
70 | |||
71 | |||
72 | void | ||
73 | spdy_get_pollfdset(struct pollfd fds[], | ||
74 | struct SPDY_Connection *connections[], | ||
75 | unsigned int max_size, | ||
76 | nfds_t *real_size); | ||
77 | |||
78 | |||
79 | int | ||
80 | spdy_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 | |||
88 | void | ||
89 | spdy_run(struct pollfd fds[], | ||
90 | struct SPDY_Connection *connections[], | ||
91 | int size); | ||
92 | |||
93 | |||
94 | void | ||
95 | spdy_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 | |||
27 | void | ||
28 | free_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 | |||
46 | int | ||
47 | init_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 | |||
62 | void | ||
63 | deinit_parse_uri(regex_t * preg) | ||
64 | { | ||
65 | regfree(preg); | ||
66 | } | ||
67 | |||
68 | int | ||
69 | parse_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 | |||
117 | void | ||
118 | free_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 | |||
131 | void *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 | |||
145 | bool | ||
146 | copy_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. */ | ||
59 | enum | ||
60 | { | ||
61 | IO_NONE, | ||
62 | WANT_READ, | ||
63 | WANT_WRITE | ||
64 | }; | ||
65 | |||
66 | |||
67 | struct Proxy; | ||
68 | |||
69 | |||
70 | struct 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 | |||
86 | struct 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 | |||
100 | struct HTTP_URI; | ||
101 | |||
102 | |||
103 | struct 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 | |||
131 | struct HTTP_URI | ||
132 | { | ||
133 | char * uri; | ||
134 | struct Proxy * proxy; | ||
135 | }; | ||
136 | |||
137 | |||
138 | struct SPDY_Headers | ||
139 | { | ||
140 | const char **nv; | ||
141 | int num; | ||
142 | int cnt; | ||
143 | }; | ||
144 | |||
145 | |||
146 | struct 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 | } | ||
166 | glob_opt; | ||
167 | |||
168 | |||
169 | struct 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 | } | ||
177 | glob_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 | |||
267 | void | ||
268 | free_uri(struct URI * uri); | ||
269 | |||
270 | |||
271 | int | ||
272 | init_parse_uri(regex_t * preg); | ||
273 | |||
274 | |||
275 | void | ||
276 | deinit_parse_uri(regex_t * preg); | ||
277 | |||
278 | |||
279 | int | ||
280 | parse_uri(regex_t * preg, | ||
281 | char * full_uri, | ||
282 | struct URI ** uri); | ||
283 | |||
284 | |||
285 | void | ||
286 | free_proxy(struct Proxy *proxy); | ||
287 | |||
288 | |||
289 | void * | ||
290 | au_malloc(size_t size); | ||
291 | |||
292 | |||
293 | bool | ||
294 | copy_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 | |||
44 | static int run = 1; | ||
45 | |||
46 | static int run2 = 1; | ||
47 | |||
48 | |||
49 | static uint64_t loops; | ||
50 | |||
51 | static time_t start; | ||
52 | |||
53 | |||
54 | static void | ||
55 | new_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 | |||
96 | static void | ||
97 | session_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 | |||
120 | static void | ||
121 | response_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 | /* | ||
148 | static int | ||
149 | print_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 | /* | ||
160 | void | ||
161 | new_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 | |||
179 | static int | ||
180 | append_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 | |||
200 | static void | ||
201 | standard_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 | |||
272 | static int | ||
273 | new_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 | |||
290 | static void | ||
291 | sig_handler(int signo) | ||
292 | { | ||
293 | (void)signo; | ||
294 | |||
295 | printf("received signal\n"); | ||
296 | } | ||
297 | |||
298 | |||
299 | int | ||
300 | main (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 | |||
41 | int run = 1; | ||
42 | char* 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 | |||
73 | static const char *DAY_NAMES[] = | ||
74 | { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; | ||
75 | |||
76 | static 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 | ||
82 | char *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 | |||
102 | ssize_t | ||
103 | response_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 | |||
120 | void | ||
121 | response_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 | |||
141 | void | ||
142 | standard_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 | |||
268 | int | ||
269 | main (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 | |||
38 | static int run = 1; | ||
39 | |||
40 | |||
41 | static ssize_t | ||
42 | response_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 | |||
59 | static void | ||
60 | response_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 | |||
77 | static void | ||
78 | standard_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 | |||
154 | int | ||
155 | main (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 |
2 | SUBDIRS = . | 2 | SUBDIRS = . |
3 | 3 | ||
4 | if ENABLE_SPDY | 4 | include_HEADERS = microhttpd.h |
5 | microspdy = microspdy.h | ||
6 | endif | ||
7 | |||
8 | include_HEADERS = microhttpd.h $(microspdy) | ||
9 | 5 | ||
10 | EXTRA_DIST = platform.h platform_interface.h w32functions.h autoinit_funcs.h | 6 | EXTRA_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 | */ | ||
274 | struct SPDY_Daemon; | ||
275 | |||
276 | |||
277 | /** | ||
278 | * Handle for a SPDY session/connection. | ||
279 | */ | ||
280 | struct 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 | */ | ||
287 | struct 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 | */ | ||
295 | struct SPDY_Response; | ||
296 | |||
297 | |||
298 | /** | ||
299 | * Collection of tuples of an HTTP header and values used in requests | ||
300 | * and responses. | ||
301 | */ | ||
302 | struct SPDY_NameValue; | ||
303 | |||
304 | |||
305 | /** | ||
306 | * Collection of tuples of a SPDY setting ID, value | ||
307 | * and flags used to control the sessions. | ||
308 | */ | ||
309 | struct 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 | */ | ||
318 | enum 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 | */ | ||
346 | enum 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 | */ | ||
401 | enum 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 | */ | ||
427 | enum 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 | */ | ||
493 | enum 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 | */ | ||
520 | enum 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 | */ | ||
545 | enum 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 | */ | ||
561 | enum 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 | */ | ||
591 | typedef 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 | */ | ||
605 | typedef 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 | */ | ||
622 | typedef 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 | */ | ||
637 | typedef 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 | */ | ||
669 | typedef 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 | */ | ||
697 | typedef 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 | */ | ||
723 | typedef 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 | */ | ||
752 | typedef 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 | */ | ||
767 | typedef 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 | */ | ||
784 | typedef 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 | */ | ||
801 | typedef 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 | ||
835 | SPDY_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 | ||
854 | SPDY_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 * | ||
880 | SPDY_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 | ||
898 | SPDY_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 | ||
924 | SPDY_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 | ||
942 | SPDY_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 | ||
953 | SPDY_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 | ||
969 | SPDY_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 | ||
980 | SPDY_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 * | ||
992 | SPDY_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 | ||
1003 | SPDY_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 * | ||
1018 | SPDY_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 | ||
1032 | SPDY_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 * | ||
1047 | SPDY_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 | ||
1062 | SPDY_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 | ||
1074 | SPDY_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 * | ||
1088 | SPDY_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 | ||
1099 | SPDY_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 * | ||
1112 | SPDY_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 * | ||
1143 | SPDY_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 * | ||
1183 | SPDY_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 | ||
1221 | SPDY_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 | ||
1241 | SPDY_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 * | ||
1255 | SPDY_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 | ||
1272 | SPDY_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 | ||
1290 | SPDY_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 | ||
1306 | SPDY_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 | ||
1319 | SPDY_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 | ||
1341 | SPDY_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 | ||
1363 | SPDY_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 | ||
1376 | SPDY_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 | ||
2 | AM_CPPFLAGS = \ | ||
3 | -I$(top_srcdir)/src/include \ | ||
4 | -I$(top_srcdir)/src/microspdy | ||
5 | |||
6 | AM_CFLAGS = $(HIDDEN_VISIBILITY_CFLAGS) | ||
7 | |||
8 | |||
9 | lib_LTLIBRARIES = \ | ||
10 | libmicrospdy.la | ||
11 | |||
12 | libmicrospdy_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 | ||
24 | libmicrospdy_la_LIBADD = \ | ||
25 | $(SPDY_LIBDEPS) | ||
26 | |||
27 | libmicrospdy_la_LDFLAGS = \ | ||
28 | $(SPDY_LIB_LDFLAGS) | ||
29 | |||
30 | libmicrospdy_la_CPPFLAGS = \ | ||
31 | $(AM_CPPFLAGS) $(SPDY_LIB_CPPFLAGS) \ | ||
32 | -DBUILDING_MHD_LIB=1 | ||
33 | |||
34 | libmicrospdy_la_CFLAGS = -Wextra \ | ||
35 | $(AM_CFLAGS) $(SPDY_LIB_CFLAGS) | ||
36 | |||
37 | |||
38 | if USE_COVERAGE | ||
39 | AM_CFLAGS += --coverage | ||
40 | endif | ||
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 | |||
29 | void | ||
30 | SPDY_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 | */ | ||
34 | struct 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 | |||
34 | void | ||
35 | spdy_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 | */ | ||
58 | static int | ||
59 | spdy_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 | */ | ||
186 | static int | ||
187 | spdy_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 | */ | ||
211 | static void | ||
212 | spdy_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 | |||
231 | int | ||
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 | |||
259 | void | ||
260 | SPDY_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 | |||
274 | void | ||
275 | SPDY_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 | |||
287 | int | ||
288 | SPDY_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 | |||
301 | int | ||
302 | SPDY_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 | |||
324 | struct SPDY_Daemon * | ||
325 | SPDY_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 | |||
377 | void | ||
378 | SPDY_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 | |||
390 | struct SPDY_Response * | ||
391 | SPDY_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 | |||
479 | struct SPDY_Response * | ||
480 | SPDY_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 | |||
524 | int | ||
525 | SPDY_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 | |||
668 | socklen_t | ||
669 | SPDY_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 | |||
684 | struct SPDY_Session * | ||
685 | SPDY_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 | |||
697 | void * | ||
698 | SPDY_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 | |||
710 | void | ||
711 | SPDY_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 | |||
724 | void * | ||
725 | SPDY_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 | |||
737 | void | ||
738 | SPDY_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 */ | ||
31 | static const unsigned char | ||
32 | spdyf_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 | |||
214 | int | ||
215 | SPDYF_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 | |||
243 | void | ||
244 | SPDYF_zlib_deflate_end(z_stream *strm) | ||
245 | { | ||
246 | deflateEnd(strm); | ||
247 | } | ||
248 | |||
249 | int | ||
250 | SPDYF_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 | |||
319 | int | ||
320 | SPDYF_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 | |||
342 | void | ||
343 | SPDYF_zlib_inflate_end(z_stream *strm) | ||
344 | { | ||
345 | inflateEnd(strm); | ||
346 | } | ||
347 | |||
348 | |||
349 | int | ||
350 | SPDYF_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 | */ | ||
41 | int | ||
42 | SPDYF_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 | */ | ||
51 | void | ||
52 | SPDYF_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 | */ | ||
68 | int | ||
69 | SPDYF_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 | */ | ||
84 | int | ||
85 | SPDYF_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 | */ | ||
94 | void | ||
95 | SPDYF_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 | */ | ||
110 | int | ||
111 | SPDYF_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 | */ | ||
41 | static void | ||
42 | spdyf_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 | */ | ||
58 | SPDY_PanicCallback spdyf_panic = &spdyf_panic_std; | ||
59 | |||
60 | |||
61 | /** | ||
62 | * Global closure argument for "spdyf_panic". | ||
63 | */ | ||
64 | void *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 | */ | ||
73 | static void | ||
74 | spdyf_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 | */ | ||
94 | static void | ||
95 | spdyf_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 | */ | ||
119 | static int | ||
120 | spdyf_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 | |||
160 | void | ||
161 | SPDY_set_panic_func (SPDY_PanicCallback cb, | ||
162 | void *cls) | ||
163 | { | ||
164 | spdyf_panic = cb; | ||
165 | spdyf_panic_cls = cls; | ||
166 | } | ||
167 | |||
168 | |||
169 | struct SPDY_Daemon * | ||
170 | SPDYF_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 | |||
372 | void | ||
373 | SPDYF_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 | |||
391 | int | ||
392 | SPDYF_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 | |||
431 | int | ||
432 | SPDYF_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 | |||
473 | void | ||
474 | SPDYF_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 | */ | ||
34 | enum 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 | */ | ||
60 | struct SPDY_Daemon * | ||
61 | SPDYF_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 | */ | ||
82 | void | ||
83 | SPDYF_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 | */ | ||
96 | int | ||
97 | SPDYF_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 | */ | ||
114 | int | ||
115 | SPDYF_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 | */ | ||
127 | void | ||
128 | SPDYF_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 | |||
29 | unsigned long long | ||
30 | SPDYF_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 | */ | ||
56 | extern SPDY_PanicCallback spdyf_panic; | ||
57 | |||
58 | |||
59 | /** | ||
60 | * Closure argument for "mhd_panic". | ||
61 | */ | ||
62 | extern 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 | */ | ||
196 | unsigned long long | ||
197 | SPDYF_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 | |||
31 | int | ||
32 | SPDYF_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 | |||
57 | int | ||
58 | SPDYF_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 | */ | ||
36 | enum 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 | */ | ||
62 | typedef 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 | */ | ||
71 | typedef 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 | */ | ||
83 | typedef 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 | */ | ||
93 | typedef 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 | */ | ||
104 | typedef 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 | */ | ||
114 | typedef 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 | */ | ||
129 | typedef 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 | */ | ||
147 | typedef 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 | */ | ||
161 | typedef 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 | */ | ||
173 | typedef 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 | */ | ||
186 | typedef 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 | */ | ||
199 | int | ||
200 | SPDYF_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 | */ | ||
212 | int | ||
213 | SPDYF_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 | */ | ||
41 | static int | ||
42 | spdyf_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 | |||
55 | void | ||
56 | SPDYF_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 | |||
67 | void | ||
68 | SPDYF_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 | |||
77 | int | ||
78 | SPDYF_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 | |||
126 | void | ||
127 | SPDYF_openssl_deinit(struct SPDY_Daemon *daemon) | ||
128 | { | ||
129 | SSL_CTX_free(daemon->io_context); | ||
130 | } | ||
131 | |||
132 | |||
133 | int | ||
134 | SPDYF_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 | |||
169 | void | ||
170 | SPDYF_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 | |||
183 | int | ||
184 | SPDYF_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 | |||
218 | int | ||
219 | SPDYF_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 | |||
254 | int | ||
255 | SPDYF_openssl_is_pending(struct SPDY_Session *session) | ||
256 | { | ||
257 | /* From openssl docs: | ||
258 | * BUGS | ||
259 | SSL_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 | |||
265 | int | ||
266 | SPDYF_openssl_before_write(struct SPDY_Session *session) | ||
267 | { | ||
268 | (void)session; | ||
269 | |||
270 | return SPDY_YES; | ||
271 | } | ||
272 | |||
273 | |||
274 | int | ||
275 | SPDYF_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 | */ | ||
41 | void | ||
42 | SPDYF_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 | */ | ||
50 | void | ||
51 | SPDYF_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 | */ | ||
62 | int | ||
63 | SPDYF_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 | */ | ||
72 | void | ||
73 | SPDYF_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 | */ | ||
83 | int | ||
84 | SPDYF_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 | */ | ||
93 | void | ||
94 | SPDYF_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 | */ | ||
108 | int | ||
109 | SPDYF_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 | */ | ||
126 | int | ||
127 | SPDYF_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 | */ | ||
139 | int | ||
140 | SPDYF_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 | */ | ||
150 | int | ||
151 | SPDYF_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 | */ | ||
162 | int | ||
163 | SPDYF_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 | |||
33 | void | ||
34 | SPDYF_raw_global_init() | ||
35 | { | ||
36 | } | ||
37 | |||
38 | |||
39 | void | ||
40 | SPDYF_raw_global_deinit() | ||
41 | { | ||
42 | } | ||
43 | |||
44 | |||
45 | int | ||
46 | SPDYF_raw_init(struct SPDY_Daemon *daemon) | ||
47 | { | ||
48 | (void)daemon; | ||
49 | |||
50 | return SPDY_YES; | ||
51 | } | ||
52 | |||
53 | |||
54 | void | ||
55 | SPDYF_raw_deinit(struct SPDY_Daemon *daemon) | ||
56 | { | ||
57 | (void)daemon; | ||
58 | } | ||
59 | |||
60 | |||
61 | int | ||
62 | SPDYF_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 | |||
85 | void | ||
86 | SPDYF_raw_close_session(struct SPDY_Session *session) | ||
87 | { | ||
88 | (void)session; | ||
89 | } | ||
90 | |||
91 | |||
92 | int | ||
93 | SPDYF_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 | |||
121 | int | ||
122 | SPDYF_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 | |||
150 | int | ||
151 | SPDYF_raw_is_pending(struct SPDY_Session *session) | ||
152 | { | ||
153 | (void)session; | ||
154 | |||
155 | return SPDY_NO; | ||
156 | } | ||
157 | |||
158 | |||
159 | int | ||
160 | SPDYF_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 | |||
178 | int | ||
179 | SPDYF_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 | */ | ||
35 | void | ||
36 | SPDYF_raw_global_init(); | ||
37 | |||
38 | |||
39 | /** | ||
40 | * Should be called | ||
41 | * at the end of the program. | ||
42 | * | ||
43 | */ | ||
44 | void | ||
45 | SPDYF_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 | */ | ||
54 | int | ||
55 | SPDYF_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 | */ | ||
64 | void | ||
65 | SPDYF_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 | */ | ||
75 | int | ||
76 | SPDYF_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 | */ | ||
85 | void | ||
86 | SPDYF_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 | */ | ||
100 | int | ||
101 | SPDYF_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 | */ | ||
118 | int | ||
119 | SPDYF_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 | */ | ||
132 | int | ||
133 | SPDYF_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 | */ | ||
143 | int | ||
144 | SPDYF_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 | */ | ||
155 | int | ||
156 | SPDYF_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 | */ | ||
43 | static void | ||
44 | spdyf_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 | */ | ||
180 | static void | ||
181 | spdyf_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 | */ | ||
259 | static void | ||
260 | spdyf_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 | */ | ||
331 | static void | ||
332 | spdyf_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 | |||
421 | int | ||
422 | SPDYF_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 | |||
516 | int | ||
517 | SPDYF_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 | |||
569 | int | ||
570 | SPDYF_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 | |||
741 | int | ||
742 | SPDYF_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 | |||
786 | int | ||
787 | SPDYF_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 | |||
829 | void | ||
830 | SPDYF_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 | |||
865 | int | ||
866 | SPDYF_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 | |||
980 | int | ||
981 | SPDYF_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 | |||
1154 | int | ||
1155 | SPDYF_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 | |||
1359 | void | ||
1360 | SPDYF_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 | |||
1388 | int | ||
1389 | SPDYF_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 | |||
1493 | void | ||
1494 | SPDYF_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 | |||
1574 | void | ||
1575 | SPDYF_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 | |||
1616 | int | ||
1617 | SPDYF_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 | |||
1664 | int | ||
1665 | SPDYF_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 | |||
1720 | int | ||
1721 | SPDYF_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 | */ | ||
43 | int | ||
44 | SPDYF_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 | */ | ||
67 | int | ||
68 | SPDYF_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 | */ | ||
85 | int | ||
86 | SPDYF_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 | */ | ||
95 | void | ||
96 | SPDYF_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 | */ | ||
107 | int | ||
108 | SPDYF_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 | */ | ||
125 | void | ||
126 | SPDYF_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 | */ | ||
137 | void | ||
138 | SPDYF_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 | */ | ||
152 | int | ||
153 | SPDYF_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 | */ | ||
170 | int | ||
171 | SPDYF_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 | */ | ||
187 | int | ||
188 | SPDYF_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 | */ | ||
207 | int | ||
208 | SPDYF_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 | */ | ||
222 | int | ||
223 | SPDYF_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 | */ | ||
236 | int | ||
237 | SPDYF_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 | */ | ||
250 | int | ||
251 | SPDYF_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 | */ | ||
264 | int | ||
265 | SPDYF_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 | */ | ||
278 | void | ||
279 | SPDYF_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 | |||
31 | int | ||
32 | SPDYF_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 | |||
116 | void | ||
117 | SPDYF_stream_destroy(struct SPDYF_Stream *stream) | ||
118 | { | ||
119 | SPDY_name_value_destroy(stream->headers); | ||
120 | free(stream); | ||
121 | stream = NULL; | ||
122 | } | ||
123 | |||
124 | |||
125 | void | ||
126 | SPDYF_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 | |||
158 | struct SPDYF_Stream * | ||
159 | SPDYF_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 | */ | ||
43 | int | ||
44 | SPDYF_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 | */ | ||
52 | void | ||
53 | SPDYF_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 | */ | ||
62 | void | ||
63 | SPDYF_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 | */ | ||
73 | struct SPDYF_Stream * | ||
74 | SPDYF_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 | |||
34 | int | ||
35 | SPDYF_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 | |||
41 | struct SPDY_NameValue * | ||
42 | SPDY_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 | |||
55 | int | ||
56 | SPDY_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 | |||
189 | const char * const * | ||
190 | SPDY_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 | |||
217 | void | ||
218 | SPDY_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 | |||
236 | int | ||
237 | SPDY_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 | |||
278 | void | ||
279 | SPDY_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 | |||
289 | struct SPDYF_Response_Queue * | ||
290 | SPDYF_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 | |||
434 | void | ||
435 | SPDYF_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 | ||
457 | SPDYF_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 | ||
554 | SPDYF_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 | */ | ||
38 | enum 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 | */ | ||
129 | enum 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 | */ | ||
172 | enum 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 | */ | ||
189 | enum 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 | */ | ||
201 | enum 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 | */ | ||
219 | enum 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 | */ | ||
295 | enum 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 | |||
316 | struct SPDYF_Stream; | ||
317 | |||
318 | struct 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 | */ | ||
334 | typedef 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 | */ | ||
351 | typedef 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 | */ | ||
369 | typedef 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 | */ | ||
379 | struct __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 | */ | ||
392 | struct __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 | */ | ||
404 | struct 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 | */ | ||
486 | struct 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 | */ | ||
519 | struct 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 | */ | ||
602 | struct 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 | */ | ||
860 | struct 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 | */ | ||
1003 | struct 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 | */ | ||
1178 | struct SPDYF_Response_Queue * | ||
1179 | SPDYF_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 | */ | ||
1196 | void | ||
1197 | SPDYF_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 | */ | ||
1208 | int | ||
1209 | SPDYF_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 | */ | ||
1224 | int | ||
1225 | SPDYF_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 | */ | ||
1241 | ssize_t | ||
1242 | SPDYF_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 | ||
2 | SUBDIRS = . | ||
3 | |||
4 | AM_CFLAGS = | ||
5 | |||
6 | if USE_COVERAGE | ||
7 | AM_CFLAGS += -fprofile-arcs -ftest-coverage | ||
8 | endif | ||
9 | |||
10 | AM_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 | |||
17 | if !HAVE_W32 | ||
18 | PERF_GET_CONCURRENT=perf_get_concurrent | ||
19 | endif | ||
20 | |||
21 | bin_PROGRAMS = \ | ||
22 | microspdy2http | ||
23 | |||
24 | microspdy2http_SOURCES = \ | ||
25 | proxy.c | ||
26 | microspdy2http_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 | |||
56 | struct 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 | |||
75 | struct 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 | |||
145 | static int loop = 1; | ||
146 | |||
147 | static CURLM *multi_handle; | ||
148 | |||
149 | static int still_running = 0; /* keep number of running handles */ | ||
150 | |||
151 | static regex_t uri_preg; | ||
152 | |||
153 | static bool call_spdy_run; | ||
154 | static bool call_curl_run; | ||
155 | |||
156 | int debug_num_curls; | ||
157 | |||
158 | |||
159 | struct 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 | |||
188 | static void | ||
189 | free_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 | |||
208 | static int | ||
209 | init_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 | |||
225 | static void | ||
226 | deinit_parse_uri(regex_t * preg) | ||
227 | { | ||
228 | regfree(preg); | ||
229 | } | ||
230 | |||
231 | |||
232 | static int | ||
233 | parse_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 | |||
311 | static bool | ||
312 | store_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 | |||
331 | static ssize_t | ||
332 | get_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 | |||
358 | static void | ||
359 | catch_signal(int signal) | ||
360 | { | ||
361 | (void)signal; | ||
362 | |||
363 | loop = 0; | ||
364 | } | ||
365 | |||
366 | static void | ||
367 | new_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 | |||
385 | static void | ||
386 | session_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 | |||
403 | static int | ||
404 | spdy_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 | |||
440 | ssize_t | ||
441 | response_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 | |||
482 | static void | ||
483 | cleanup(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 | |||
505 | static void | ||
506 | response_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 | |||
527 | static size_t | ||
528 | curl_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 | |||
682 | static size_t | ||
683 | curl_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 | |||
726 | static size_t | ||
727 | curl_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 | |||
789 | static int | ||
790 | iterate_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 | |||
829 | static void | ||
830 | standard_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 | |||
971 | static int | ||
972 | run () | ||
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 | |||
1259 | static void | ||
1260 | display_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 | |||
1295 | int | ||
1296 | main (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 | ||
2 | SUBDIRS = . | ||
3 | |||
4 | AM_CFLAGS = -DDATA_DIR=\"$(top_srcdir)/src/datadir/\" | ||
5 | |||
6 | if USE_COVERAGE | ||
7 | AM_CFLAGS += -fprofile-arcs -ftest-coverage | ||
8 | endif | ||
9 | |||
10 | AM_CPPFLAGS = \ | ||
11 | -I$(top_srcdir) \ | ||
12 | -I$(top_srcdir)/src/include \ | ||
13 | -I$(top_srcdir)/src/applicationlayer \ | ||
14 | $(LIBCURL_CPPFLAGS) | ||
15 | |||
16 | if !HAVE_W32 | ||
17 | PERF_GET_CONCURRENT=perf_get_concurrent | ||
18 | endif | ||
19 | |||
20 | if ENABLE_SPDY | ||
21 | if HAVE_OPENSSL | ||
22 | check_PROGRAMS = \ | ||
23 | test_daemon_start_stop \ | ||
24 | test_daemon_start_stop_many \ | ||
25 | test_struct_namevalue | ||
26 | |||
27 | if HAVE_SPDYLAY | ||
28 | check_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 | ||
36 | if HAVE_CURL_BINARY | ||
37 | check_PROGRAMS += \ | ||
38 | test_proxies | ||
39 | endif | ||
40 | endif | ||
41 | endif | ||
42 | endif | ||
43 | |||
44 | |||
45 | TESTS = $(check_PROGRAMS) | ||
46 | |||
47 | |||
48 | SPDY_SOURCES= \ | ||
49 | common.h common.c | ||
50 | |||
51 | SPDY_LDADD= \ | ||
52 | $(top_builddir)/src/microspdy/libmicrospdy.la \ | ||
53 | -lz | ||
54 | |||
55 | test_daemon_start_stop_SOURCES = \ | ||
56 | test_daemon_start_stop.c \ | ||
57 | $(SPDY_SOURCES) | ||
58 | test_daemon_start_stop_LDADD = $(SPDY_LDADD) | ||
59 | |||
60 | test_daemon_start_stop_many_SOURCES = \ | ||
61 | test_daemon_start_stop_many.c \ | ||
62 | $(SPDY_SOURCES) | ||
63 | test_daemon_start_stop_many_LDADD = $(SPDY_LDADD) | ||
64 | |||
65 | test_struct_namevalue_SOURCES = \ | ||
66 | test_struct_namevalue.c \ | ||
67 | $(SPDY_SOURCES) | ||
68 | test_struct_namevalue_LDADD = $(SPDY_LDADD) | ||
69 | |||
70 | if HAVE_SPDYLAY | ||
71 | test_new_connection_SOURCES = \ | ||
72 | test_new_connection.c \ | ||
73 | $(SPDY_SOURCES) | ||
74 | test_new_connection_LDADD = $(SPDY_LDADD) \ | ||
75 | -lspdylay | ||
76 | |||
77 | test_request_response_SOURCES = \ | ||
78 | test_request_response.c \ | ||
79 | $(SPDY_SOURCES) | ||
80 | test_request_response_LDADD = $(SPDY_LDADD) \ | ||
81 | -lspdylay | ||
82 | |||
83 | test_notls_SOURCES = \ | ||
84 | test_notls.c \ | ||
85 | $(SPDY_SOURCES) | ||
86 | test_notls_LDADD = $(SPDY_LDADD) \ | ||
87 | -lspdylay | ||
88 | |||
89 | test_request_response_with_callback_SOURCES = \ | ||
90 | test_request_response_with_callback.c \ | ||
91 | $(SPDY_SOURCES) | ||
92 | test_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 | |||
99 | test_misc_SOURCES = \ | ||
100 | test_misc.c \ | ||
101 | $(SPDY_SOURCES) | ||
102 | test_misc_LDADD = $(SPDY_LDADD) | ||
103 | |||
104 | test_session_timeout_SOURCES = \ | ||
105 | test_session_timeout.c \ | ||
106 | $(SPDY_SOURCES) | ||
107 | test_session_timeout_LDADD = $(SPDY_LDADD) | ||
108 | |||
109 | |||
110 | test_proxies_SOURCES = \ | ||
111 | test_proxies.c \ | ||
112 | $(SPDY_SOURCES) | ||
113 | test_proxies_LDADD = $(SPDY_LDADD) | ||
114 | |||
115 | endif | ||
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 | |||
37 | FUNC_CONSTRUCTOR (constructor)() | ||
38 | { | ||
39 | printf("\nTEST START -------------------------------------------------------\n"); | ||
40 | } | ||
41 | |||
42 | FUNC_DESTRUCTOR (destructor)() | ||
43 | { | ||
44 | printf("------------------------------------------------------- TEST END\n"); | ||
45 | } | ||
46 | |||
47 | uint16_t | ||
48 | get_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 | |||
37 | uint16_t | ||
38 | get_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 | |||
29 | int | ||
30 | main() | ||
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 | |||
29 | int | ||
30 | main() | ||
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 | |||
31 | int 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 | |||
43 | pid_t parent; | ||
44 | pid_t child; | ||
45 | |||
46 | struct SPDY_Session *session1; | ||
47 | struct SPDY_Session *session2; | ||
48 | |||
49 | void | ||
50 | killchild() | ||
51 | { | ||
52 | kill(child, SIGKILL); | ||
53 | exit(1); | ||
54 | } | ||
55 | |||
56 | void | ||
57 | killparent() | ||
58 | { | ||
59 | kill(parent, SIGKILL); | ||
60 | _exit(1); | ||
61 | } | ||
62 | |||
63 | |||
64 | void | ||
65 | create_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 | |||
96 | void | ||
97 | response_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 | |||
152 | void | ||
153 | standard_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 | |||
200 | int | ||
201 | parentproc() | ||
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 | |||
278 | int | ||
279 | main() | ||
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 | |||
38 | int port; | ||
39 | int loop = 1; | ||
40 | |||
41 | pid_t parent; | ||
42 | pid_t child; | ||
43 | |||
44 | int | ||
45 | spdylay_printf(const char *format, ...) | ||
46 | { | ||
47 | (void)format; | ||
48 | |||
49 | return 0; | ||
50 | } | ||
51 | |||
52 | int | ||
53 | spdylay_fprintf(FILE *stream, const char *format, ...) | ||
54 | { | ||
55 | (void)stream; | ||
56 | (void)format; | ||
57 | |||
58 | return 0; | ||
59 | } | ||
60 | |||
61 | void | ||
62 | killchild(int pid, char *message) | ||
63 | { | ||
64 | printf("%s\n",message); | ||
65 | kill(pid, SIGKILL); | ||
66 | exit(1); | ||
67 | } | ||
68 | |||
69 | void | ||
70 | killparent(int pid, char *message) | ||
71 | { | ||
72 | printf("%s\n",message); | ||
73 | kill(pid, SIGKILL); | ||
74 | _exit(2); | ||
75 | } | ||
76 | |||
77 | |||
78 | /***** | ||
79 | * start of code needed to utilize spdylay | ||
80 | */ | ||
81 | |||
82 | #include <stdint.h> | ||
83 | #include <stdlib.h> | ||
84 | #include <unistd.h> | ||
85 | #include <fcntl.h> | ||
86 | #include <sys/types.h> | ||
87 | #include <sys/socket.h> | ||
88 | #include <netdb.h> | ||
89 | #include <netinet/in.h> | ||
90 | #include <netinet/tcp.h> | ||
91 | #include <poll.h> | ||
92 | #include <signal.h> | ||
93 | #include <stdio.h> | ||
94 | #include <assert.h> | ||
95 | |||
96 | #include <spdylay/spdylay.h> | ||
97 | |||
98 | #include <openssl/ssl.h> | ||
99 | #include <openssl/err.h> | ||
100 | |||
101 | enum { | ||
102 | IO_NONE, | ||
103 | WANT_READ, | ||
104 | WANT_WRITE | ||
105 | }; | ||
106 | |||
107 | struct Connection { | ||
108 | SSL *ssl; | ||
109 | spdylay_session *session; | ||
110 | /* WANT_READ if SSL connection needs more input; or WANT_WRITE if it | ||
111 | needs more output; or IO_NONE. This is necessary because SSL/TLS | ||
112 | re-negotiation is possible at any time. Spdylay API offers | ||
113 | similar functions like spdylay_session_want_read() and | ||
114 | spdylay_session_want_write() but they do not take into account | ||
115 | SSL connection. */ | ||
116 | int want_io; | ||
117 | }; | ||
118 | |||
119 | struct Request { | ||
120 | char *host; | ||
121 | uint16_t port; | ||
122 | /* In this program, path contains query component as well. */ | ||
123 | char *path; | ||
124 | /* This is the concatenation of host and port with ":" in | ||
125 | between. */ | ||
126 | char *hostport; | ||
127 | /* Stream ID for this request. */ | ||
128 | int32_t stream_id; | ||
129 | /* The gzip stream inflater for the compressed response. */ | ||
130 | spdylay_gzip *inflater; | ||
131 | }; | ||
132 | |||
133 | struct URI { | ||
134 | const char *host; | ||
135 | size_t hostlen; | ||
136 | uint16_t port; | ||
137 | /* In this program, path contains query component as well. */ | ||
138 | const char *path; | ||
139 | size_t pathlen; | ||
140 | const char *hostport; | ||
141 | size_t hostportlen; | ||
142 | }; | ||
143 | |||
144 | /* | ||
145 | * Returns copy of string |s| with the length |len|. The returned | ||
146 | * string is NULL-terminated. | ||
147 | */ | ||
148 | static char* strcopy(const char *s, size_t len) | ||
149 | { | ||
150 | char *dst; | ||
151 | dst = malloc(len+1); | ||
152 | if (NULL == dst) | ||
153 | abort (); | ||
154 | memcpy(dst, s, len); | ||
155 | dst[len] = '\0'; | ||
156 | return dst; | ||
157 | } | ||
158 | |||
159 | /* | ||
160 | * Prints error message |msg| and exit. | ||
161 | */ | ||
162 | static void die(const char *msg) | ||
163 | { | ||
164 | fprintf(stderr, "FATAL: %s\n", msg); | ||
165 | exit(EXIT_FAILURE); | ||
166 | } | ||
167 | |||
168 | /* | ||
169 | * Prints error containing the function name |func| and message |msg| | ||
170 | * and exit. | ||
171 | */ | ||
172 | static void dief(const char *func, const char *msg) | ||
173 | { | ||
174 | fprintf(stderr, "FATAL: %s: %s\n", func, msg); | ||
175 | exit(EXIT_FAILURE); | ||
176 | } | ||
177 | |||
178 | /* | ||
179 | * Prints error containing the function name |func| and error code | ||
180 | * |error_code| and exit. | ||
181 | */ | ||
182 | static void diec(const char *func, int error_code) | ||
183 | { | ||
184 | fprintf(stderr, "FATAL: %s: error_code=%d, msg=%s\n", func, error_code, | ||
185 | spdylay_strerror(error_code)); | ||
186 | exit(EXIT_FAILURE); | ||
187 | } | ||
188 | |||
189 | /* | ||
190 | * Check response is content-encoding: gzip. We need this because SPDY | ||
191 | * client is required to support gzip. | ||
192 | */ | ||
193 | static void check_gzip(struct Request *req, char **nv) | ||
194 | { | ||
195 | int gzip = 0; | ||
196 | size_t i; | ||
197 | for(i = 0; nv[i]; i += 2) { | ||
198 | if(strcmp("content-encoding", nv[i]) == 0) { | ||
199 | gzip = strcmp("gzip", nv[i+1]) == 0; | ||
200 | break; | ||
201 | } | ||
202 | } | ||
203 | if(gzip) { | ||
204 | int rv; | ||
205 | if(req->inflater) { | ||
206 | return; | ||
207 | } | ||
208 | rv = spdylay_gzip_inflate_new(&req->inflater); | ||
209 | if(rv != 0) { | ||
210 | die("Can't allocate inflate stream."); | ||
211 | } | ||
212 | } | ||
213 | } | ||
214 | |||
215 | /* | ||
216 | * The implementation of spdylay_send_callback type. Here we write | ||
217 | * |data| with size |length| to the network and return the number of | ||
218 | * bytes actually written. See the documentation of | ||
219 | * spdylay_send_callback for the details. | ||
220 | */ | ||
221 | static ssize_t send_callback(spdylay_session *session, | ||
222 | const uint8_t *data, size_t length, int flags, | ||
223 | void *user_data) | ||
224 | { | ||
225 | (void)session; | ||
226 | (void)flags; | ||
227 | |||
228 | struct Connection *connection; | ||
229 | ssize_t rv; | ||
230 | connection = (struct Connection*)user_data; | ||
231 | connection->want_io = IO_NONE; | ||
232 | ERR_clear_error(); | ||
233 | rv = SSL_write(connection->ssl, data, length); | ||
234 | if(rv < 0) { | ||
235 | int err = SSL_get_error(connection->ssl, rv); | ||
236 | if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) { | ||
237 | connection->want_io = (err == SSL_ERROR_WANT_READ ? | ||
238 | WANT_READ : WANT_WRITE); | ||
239 | rv = SPDYLAY_ERR_WOULDBLOCK; | ||
240 | } else { | ||
241 | rv = SPDYLAY_ERR_CALLBACK_FAILURE; | ||
242 | } | ||
243 | } | ||
244 | return rv; | ||
245 | } | ||
246 | |||
247 | /* | ||
248 | * The implementation of spdylay_recv_callback type. Here we read data | ||
249 | * from the network and write them in |buf|. The capacity of |buf| is | ||
250 | * |length| bytes. Returns the number of bytes stored in |buf|. See | ||
251 | * the documentation of spdylay_recv_callback for the details. | ||
252 | */ | ||
253 | static ssize_t recv_callback(spdylay_session *session, | ||
254 | uint8_t *buf, size_t length, int flags, | ||
255 | void *user_data) | ||
256 | { | ||
257 | (void)session; | ||
258 | (void)flags; | ||
259 | |||
260 | struct Connection *connection; | ||
261 | ssize_t rv; | ||
262 | connection = (struct Connection*)user_data; | ||
263 | connection->want_io = IO_NONE; | ||
264 | ERR_clear_error(); | ||
265 | rv = SSL_read(connection->ssl, buf, length); | ||
266 | if(rv < 0) { | ||
267 | int err = SSL_get_error(connection->ssl, rv); | ||
268 | if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) { | ||
269 | connection->want_io = (err == SSL_ERROR_WANT_READ ? | ||
270 | WANT_READ : WANT_WRITE); | ||
271 | rv = SPDYLAY_ERR_WOULDBLOCK; | ||
272 | } else { | ||
273 | rv = SPDYLAY_ERR_CALLBACK_FAILURE; | ||
274 | } | ||
275 | } else if(rv == 0) { | ||
276 | rv = SPDYLAY_ERR_EOF; | ||
277 | } | ||
278 | return rv; | ||
279 | } | ||
280 | |||
281 | /* | ||
282 | * The implementation of spdylay_before_ctrl_send_callback type. We | ||
283 | * use this function to get stream ID of the request. This is because | ||
284 | * stream ID is not known when we submit the request | ||
285 | * (spdylay_submit_request). | ||
286 | */ | ||
287 | static void before_ctrl_send_callback(spdylay_session *session, | ||
288 | spdylay_frame_type type, | ||
289 | spdylay_frame *frame, | ||
290 | void *user_data) | ||
291 | { | ||
292 | (void)user_data; | ||
293 | |||
294 | if(type == SPDYLAY_SYN_STREAM) { | ||
295 | struct Request *req; | ||
296 | int stream_id = frame->syn_stream.stream_id; | ||
297 | req = spdylay_session_get_stream_user_data(session, stream_id); | ||
298 | if(req && req->stream_id == -1) { | ||
299 | req->stream_id = stream_id; | ||
300 | spdylay_printf("[INFO] Stream ID = %d\n", stream_id); | ||
301 | } | ||
302 | } | ||
303 | } | ||
304 | |||
305 | static void on_ctrl_send_callback(spdylay_session *session, | ||
306 | spdylay_frame_type type, | ||
307 | spdylay_frame *frame, void *user_data) | ||
308 | { | ||
309 | (void)user_data; | ||
310 | |||
311 | char **nv; | ||
312 | const char *name = NULL; | ||
313 | int32_t stream_id; | ||
314 | size_t i; | ||
315 | switch(type) { | ||
316 | case SPDYLAY_SYN_STREAM: | ||
317 | nv = frame->syn_stream.nv; | ||
318 | name = "SYN_STREAM"; | ||
319 | stream_id = frame->syn_stream.stream_id; | ||
320 | break; | ||
321 | default: | ||
322 | break; | ||
323 | } | ||
324 | if(name && spdylay_session_get_stream_user_data(session, stream_id)) { | ||
325 | spdylay_printf("[INFO] C ----------------------------> S (%s)\n", name); | ||
326 | for(i = 0; nv[i]; i += 2) { | ||
327 | spdylay_printf(" %s: %s\n", nv[i], nv[i+1]); | ||
328 | } | ||
329 | } | ||
330 | } | ||
331 | |||
332 | static void on_ctrl_recv_callback(spdylay_session *session, | ||
333 | spdylay_frame_type type, | ||
334 | spdylay_frame *frame, void *user_data) | ||
335 | { | ||
336 | (void)user_data; | ||
337 | |||
338 | struct Request *req; | ||
339 | char **nv; | ||
340 | const char *name = NULL; | ||
341 | int32_t stream_id; | ||
342 | size_t i; | ||
343 | switch(type) { | ||
344 | case SPDYLAY_SYN_REPLY: | ||
345 | nv = frame->syn_reply.nv; | ||
346 | name = "SYN_REPLY"; | ||
347 | stream_id = frame->syn_reply.stream_id; | ||
348 | break; | ||
349 | case SPDYLAY_HEADERS: | ||
350 | nv = frame->headers.nv; | ||
351 | name = "HEADERS"; | ||
352 | stream_id = frame->headers.stream_id; | ||
353 | break; | ||
354 | default: | ||
355 | break; | ||
356 | } | ||
357 | if(!name) { | ||
358 | return; | ||
359 | } | ||
360 | req = spdylay_session_get_stream_user_data(session, stream_id); | ||
361 | if(req) { | ||
362 | check_gzip(req, nv); | ||
363 | spdylay_printf("[INFO] C <---------------------------- S (%s)\n", name); | ||
364 | for(i = 0; nv[i]; i += 2) { | ||
365 | spdylay_printf(" %s: %s\n", nv[i], nv[i+1]); | ||
366 | } | ||
367 | } | ||
368 | } | ||
369 | |||
370 | /* | ||
371 | * The implementation of spdylay_on_stream_close_callback type. We use | ||
372 | * this function to know the response is fully received. Since we just | ||
373 | * fetch 1 resource in this program, after reception of the response, | ||
374 | * we submit GOAWAY and close the session. | ||
375 | */ | ||
376 | static void on_stream_close_callback(spdylay_session *session, | ||
377 | int32_t stream_id, | ||
378 | spdylay_status_code status_code, | ||
379 | void *user_data) | ||
380 | { | ||
381 | (void)user_data; | ||
382 | (void)status_code; | ||
383 | struct Request *req; | ||
384 | req = spdylay_session_get_stream_user_data(session, stream_id); | ||
385 | if(req) { | ||
386 | int rv; | ||
387 | rv = spdylay_submit_goaway(session, SPDYLAY_GOAWAY_OK); | ||
388 | if(rv != 0) { | ||
389 | diec("spdylay_submit_goaway", rv); | ||
390 | } | ||
391 | } | ||
392 | } | ||
393 | |||
394 | #define MAX_OUTLEN 4096 | ||
395 | |||
396 | /* | ||
397 | * The implementation of spdylay_on_data_chunk_recv_callback type. We | ||
398 | * use this function to print the received response body. | ||
399 | */ | ||
400 | static void on_data_chunk_recv_callback(spdylay_session *session, uint8_t flags, | ||
401 | int32_t stream_id, | ||
402 | const uint8_t *data, size_t len, | ||
403 | void *user_data) | ||
404 | { | ||
405 | (void)user_data; | ||
406 | (void)flags; | ||
407 | |||
408 | struct Request *req; | ||
409 | req = spdylay_session_get_stream_user_data(session, stream_id); | ||
410 | if(req) { | ||
411 | spdylay_printf("[INFO] C <---------------------------- S (DATA)\n"); | ||
412 | spdylay_printf(" %lu bytes\n", (unsigned long int)len); | ||
413 | if(req->inflater) { | ||
414 | while(len > 0) { | ||
415 | uint8_t out[MAX_OUTLEN]; | ||
416 | size_t outlen = MAX_OUTLEN; | ||
417 | size_t tlen = len; | ||
418 | int rv; | ||
419 | rv = spdylay_gzip_inflate(req->inflater, out, &outlen, data, &tlen); | ||
420 | if(rv == -1) { | ||
421 | spdylay_submit_rst_stream(session, stream_id, SPDYLAY_INTERNAL_ERROR); | ||
422 | break; | ||
423 | } | ||
424 | fwrite(out, 1, outlen, stdout); | ||
425 | data += tlen; | ||
426 | len -= tlen; | ||
427 | } | ||
428 | } else { | ||
429 | /* TODO add support gzip */ | ||
430 | fwrite(data, 1, len, stdout); | ||
431 | |||
432 | //check if the data is correct | ||
433 | if(strcmp(RESPONSE_BODY, (char *)data) != 0) | ||
434 | killparent(parent, "\nreceived data is not the same"); | ||
435 | } | ||
436 | spdylay_printf("\n"); | ||
437 | } | ||
438 | } | ||
439 | |||
440 | /* | ||
441 | * Setup callback functions. Spdylay API offers many callback | ||
442 | * functions, but most of them are optional. The send_callback is | ||
443 | * always required. Since we use spdylay_session_recv(), the | ||
444 | * recv_callback is also required. | ||
445 | */ | ||
446 | static void setup_spdylay_callbacks(spdylay_session_callbacks *callbacks) | ||
447 | { | ||
448 | memset(callbacks, 0, sizeof(spdylay_session_callbacks)); | ||
449 | callbacks->send_callback = send_callback; | ||
450 | callbacks->recv_callback = recv_callback; | ||
451 | callbacks->before_ctrl_send_callback = before_ctrl_send_callback; | ||
452 | callbacks->on_ctrl_send_callback = on_ctrl_send_callback; | ||
453 | callbacks->on_ctrl_recv_callback = on_ctrl_recv_callback; | ||
454 | callbacks->on_stream_close_callback = on_stream_close_callback; | ||
455 | callbacks->on_data_chunk_recv_callback = on_data_chunk_recv_callback; | ||
456 | } | ||
457 | |||
458 | /* | ||
459 | * Callback function for SSL/TLS NPN. Since this program only supports | ||
460 | * SPDY protocol, if server does not offer SPDY protocol the Spdylay | ||
461 | * library supports, we terminate program. | ||
462 | */ | ||
463 | static int select_next_proto_cb(SSL* ssl, | ||
464 | unsigned char **out, unsigned char *outlen, | ||
465 | const unsigned char *in, unsigned int inlen, | ||
466 | void *arg) | ||
467 | { | ||
468 | (void)ssl; | ||
469 | |||
470 | int rv; | ||
471 | uint16_t *spdy_proto_version; | ||
472 | /* spdylay_select_next_protocol() selects SPDY protocol version the | ||
473 | Spdylay library supports. */ | ||
474 | rv = spdylay_select_next_protocol(out, outlen, in, inlen); | ||
475 | if(rv <= 0) { | ||
476 | die("Server did not advertise spdy/2 or spdy/3 protocol."); | ||
477 | } | ||
478 | spdy_proto_version = (uint16_t*)arg; | ||
479 | *spdy_proto_version = rv; | ||
480 | return SSL_TLSEXT_ERR_OK; | ||
481 | } | ||
482 | |||
483 | /* | ||
484 | * Setup SSL context. We pass |spdy_proto_version| to get negotiated | ||
485 | * SPDY protocol version in NPN callback. | ||
486 | */ | ||
487 | static void init_ssl_ctx(SSL_CTX *ssl_ctx, uint16_t *spdy_proto_version) | ||
488 | { | ||
489 | /* Disable SSLv2 and enable all workarounds for buggy servers */ | ||
490 | SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2); | ||
491 | SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); | ||
492 | SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); | ||
493 | /* Set NPN callback */ | ||
494 | SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, | ||
495 | spdy_proto_version); | ||
496 | } | ||
497 | |||
498 | static void ssl_handshake(SSL *ssl, int fd) | ||
499 | { | ||
500 | int rv; | ||
501 | if(SSL_set_fd(ssl, fd) == 0) { | ||
502 | dief("SSL_set_fd", ERR_error_string(ERR_get_error(), NULL)); | ||
503 | } | ||
504 | ERR_clear_error(); | ||
505 | rv = SSL_connect(ssl); | ||
506 | if(rv <= 0) { | ||
507 | dief("SSL_connect", ERR_error_string(ERR_get_error(), NULL)); | ||
508 | } | ||
509 | } | ||
510 | |||
511 | /* | ||
512 | * Connects to the host |host| and port |port|. This function returns | ||
513 | * the file descriptor of the client socket. | ||
514 | */ | ||
515 | static int connect_to(const char *host, uint16_t port) | ||
516 | { | ||
517 | struct addrinfo hints; | ||
518 | int fd = -1; | ||
519 | int rv; | ||
520 | char service[NI_MAXSERV]; | ||
521 | struct addrinfo *res, *rp; | ||
522 | snprintf(service, sizeof(service), "%u", port); | ||
523 | memset(&hints, 0, sizeof(struct addrinfo)); | ||
524 | hints.ai_family = AF_UNSPEC; | ||
525 | hints.ai_socktype = SOCK_STREAM; | ||
526 | rv = getaddrinfo(host, service, &hints, &res); | ||
527 | if(rv != 0) { | ||
528 | dief("getaddrinfo", gai_strerror(rv)); | ||
529 | } | ||
530 | for(rp = res; rp; rp = rp->ai_next) { | ||
531 | fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); | ||
532 | if(fd == -1) { | ||
533 | continue; | ||
534 | } | ||
535 | while((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 && | ||
536 | errno == EINTR); | ||
537 | if(rv == 0) { | ||
538 | break; | ||
539 | } | ||
540 | MHD_socket_close_(fd); | ||
541 | fd = -1; | ||
542 | } | ||
543 | freeaddrinfo(res); | ||
544 | return fd; | ||
545 | } | ||
546 | |||
547 | static void make_non_block(int fd) | ||
548 | { | ||
549 | int flags, rv; | ||
550 | while((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR); | ||
551 | if(flags == -1) { | ||
552 | dief("fcntl", strerror(errno)); | ||
553 | } | ||
554 | while((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR); | ||
555 | if(rv == -1) { | ||
556 | dief("fcntl", strerror(errno)); | ||
557 | } | ||
558 | } | ||
559 | |||
560 | /* | ||
561 | * Setting TCP_NODELAY is not mandatory for the SPDY protocol. | ||
562 | */ | ||
563 | static void set_tcp_nodelay(int fd) | ||
564 | { | ||
565 | int val = 1; | ||
566 | int rv; | ||
567 | rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val)); | ||
568 | if(rv == -1) { | ||
569 | dief("setsockopt", strerror(errno)); | ||
570 | } | ||
571 | } | ||
572 | |||
573 | /* | ||
574 | * Update |pollfd| based on the state of |connection|. | ||
575 | */ | ||
576 | static void ctl_poll(struct pollfd *pollfd, struct Connection *connection) | ||
577 | { | ||
578 | pollfd->events = 0; | ||
579 | if(spdylay_session_want_read(connection->session) || | ||
580 | connection->want_io == WANT_READ) { | ||
581 | pollfd->events |= POLLIN; | ||
582 | } | ||
583 | if(spdylay_session_want_write(connection->session) || | ||
584 | connection->want_io == WANT_WRITE) { | ||
585 | pollfd->events |= POLLOUT; | ||
586 | } | ||
587 | } | ||
588 | |||
589 | /* | ||
590 | * Submits the request |req| to the connection |connection|. This | ||
591 | * function does not send packets; just append the request to the | ||
592 | * internal queue in |connection->session|. | ||
593 | */ | ||
594 | static void submit_request(struct Connection *connection, struct Request *req) | ||
595 | { | ||
596 | int pri = 0; | ||
597 | int rv; | ||
598 | const char *nv[15]; | ||
599 | /* We always use SPDY/3 style header even if the negotiated protocol | ||
600 | version is SPDY/2. The library translates the header name as | ||
601 | necessary. Make sure that the last item is NULL! */ | ||
602 | nv[0] = ":method"; nv[1] = "GET"; | ||
603 | nv[2] = ":path"; nv[3] = req->path; | ||
604 | nv[4] = ":version"; nv[5] = "HTTP/1.1"; | ||
605 | nv[6] = ":scheme"; nv[7] = "https"; | ||
606 | nv[8] = ":host"; nv[9] = req->hostport; | ||
607 | nv[10] = "accept"; nv[11] = "*/*"; | ||
608 | nv[12] = "user-agent"; nv[13] = "spdylay/"SPDYLAY_VERSION; | ||
609 | nv[14] = NULL; | ||
610 | rv = spdylay_submit_request(connection->session, pri, nv, NULL, req); | ||
611 | if(rv != 0) { | ||
612 | diec("spdylay_submit_request", rv); | ||
613 | } | ||
614 | } | ||
615 | |||
616 | /* | ||
617 | * Performs the network I/O. | ||
618 | */ | ||
619 | static void exec_io(struct Connection *connection) | ||
620 | { | ||
621 | int rv; | ||
622 | rv = spdylay_session_recv(connection->session); | ||
623 | if(rv != 0) { | ||
624 | diec("spdylay_session_recv", rv); | ||
625 | } | ||
626 | rv = spdylay_session_send(connection->session); | ||
627 | if(rv != 0) { | ||
628 | diec("spdylay_session_send", rv); | ||
629 | } | ||
630 | } | ||
631 | |||
632 | static void request_init(struct Request *req, const struct URI *uri) | ||
633 | { | ||
634 | req->host = strcopy(uri->host, uri->hostlen); | ||
635 | req->port = uri->port; | ||
636 | req->path = strcopy(uri->path, uri->pathlen); | ||
637 | req->hostport = strcopy(uri->hostport, uri->hostportlen); | ||
638 | req->stream_id = -1; | ||
639 | req->inflater = NULL; | ||
640 | } | ||
641 | |||
642 | static void request_free(struct Request *req) | ||
643 | { | ||
644 | free(req->host); | ||
645 | free(req->path); | ||
646 | free(req->hostport); | ||
647 | spdylay_gzip_inflate_del(req->inflater); | ||
648 | } | ||
649 | |||
650 | /* | ||
651 | * Fetches the resource denoted by |uri|. | ||
652 | */ | ||
653 | static void | ||
654 | fetch_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 | |||
735 | static int | ||
736 | parse_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 | |||
827 | void | ||
828 | new_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 | ||
882 | void | ||
883 | childproc(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 | ||
907 | int | ||
908 | parentproc(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 | |||
989 | int 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 | |||
36 | pid_t parent; | ||
37 | pid_t child; | ||
38 | char *rcvbuf; | ||
39 | int rcvbuf_c = 0; | ||
40 | |||
41 | int session_closed_called = 0; | ||
42 | |||
43 | void | ||
44 | killchild(int pid, char *message) | ||
45 | { | ||
46 | printf("%s\n",message); | ||
47 | kill(pid, SIGKILL); | ||
48 | exit(1); | ||
49 | } | ||
50 | |||
51 | void | ||
52 | killparent(int pid, char *message) | ||
53 | { | ||
54 | printf("%s\n",message); | ||
55 | kill(pid, SIGKILL); | ||
56 | _exit(1); | ||
57 | } | ||
58 | |||
59 | |||
60 | /***** | ||
61 | * start of code needed to utilize spdylay | ||
62 | */ | ||
63 | |||
64 | #include <stdint.h> | ||
65 | #include <stdlib.h> | ||
66 | #include <unistd.h> | ||
67 | #include <fcntl.h> | ||
68 | #include <sys/types.h> | ||
69 | #include <sys/socket.h> | ||
70 | #include <netdb.h> | ||
71 | #include <netinet/in.h> | ||
72 | #include <netinet/tcp.h> | ||
73 | #include <poll.h> | ||
74 | #include <signal.h> | ||
75 | #include <stdio.h> | ||
76 | #include <assert.h> | ||
77 | |||
78 | #include <spdylay/spdylay.h> | ||
79 | |||
80 | enum { | ||
81 | IO_NONE, | ||
82 | WANT_READ, | ||
83 | WANT_WRITE | ||
84 | }; | ||
85 | |||
86 | struct Connection { | ||
87 | spdylay_session *session; | ||
88 | /* WANT_READ if SSL connection needs more input; or WANT_WRITE if it | ||
89 | needs more output; or IO_NONE. This is necessary because SSL/TLS | ||
90 | re-negotiation is possible at any time. Spdylay API offers | ||
91 | similar functions like spdylay_session_want_read() and | ||
92 | spdylay_session_want_write() but they do not take into account | ||
93 | SSL connection. */ | ||
94 | int want_io; | ||
95 | int fd; | ||
96 | }; | ||
97 | |||
98 | struct Request { | ||
99 | char *host; | ||
100 | uint16_t port; | ||
101 | /* In this program, path contains query component as well. */ | ||
102 | char *path; | ||
103 | /* This is the concatenation of host and port with ":" in | ||
104 | between. */ | ||
105 | char *hostport; | ||
106 | /* Stream ID for this request. */ | ||
107 | int32_t stream_id; | ||
108 | /* The gzip stream inflater for the compressed response. */ | ||
109 | spdylay_gzip *inflater; | ||
110 | }; | ||
111 | |||
112 | struct URI { | ||
113 | const char *host; | ||
114 | size_t hostlen; | ||
115 | uint16_t port; | ||
116 | /* In this program, path contains query component as well. */ | ||
117 | const char *path; | ||
118 | size_t pathlen; | ||
119 | const char *hostport; | ||
120 | size_t hostportlen; | ||
121 | }; | ||
122 | |||
123 | /* | ||
124 | * Returns copy of string |s| with the length |len|. The returned | ||
125 | * string is NULL-terminated. | ||
126 | */ | ||
127 | static char* strcopy(const char *s, size_t len) | ||
128 | { | ||
129 | char *dst; | ||
130 | dst = malloc(len+1); | ||
131 | if (NULL == dst) | ||
132 | abort (); | ||
133 | memcpy(dst, s, len); | ||
134 | dst[len] = '\0'; | ||
135 | return dst; | ||
136 | } | ||
137 | |||
138 | /* | ||
139 | * Prints error message |msg| and exit. | ||
140 | */ | ||
141 | static void die(const char *msg) | ||
142 | { | ||
143 | fprintf(stderr, "FATAL: %s\n", msg); | ||
144 | exit(EXIT_FAILURE); | ||
145 | } | ||
146 | |||
147 | /* | ||
148 | * Prints error containing the function name |func| and message |msg| | ||
149 | * and exit. | ||
150 | */ | ||
151 | static void dief(const char *func, const char *msg) | ||
152 | { | ||
153 | fprintf(stderr, "FATAL: %s: %s\n", func, msg); | ||
154 | exit(EXIT_FAILURE); | ||
155 | } | ||
156 | |||
157 | /* | ||
158 | * Prints error containing the function name |func| and error code | ||
159 | * |error_code| and exit. | ||
160 | */ | ||
161 | static void diec(const char *func, int error_code) | ||
162 | { | ||
163 | fprintf(stderr, "FATAL: %s: error_code=%d, msg=%s\n", func, error_code, | ||
164 | spdylay_strerror(error_code)); | ||
165 | exit(EXIT_FAILURE); | ||
166 | } | ||
167 | |||
168 | /* | ||
169 | * Check response is content-encoding: gzip. We need this because SPDY | ||
170 | * client is required to support gzip. | ||
171 | */ | ||
172 | static void check_gzip(struct Request *req, char **nv) | ||
173 | { | ||
174 | int gzip = 0; | ||
175 | size_t i; | ||
176 | for(i = 0; nv[i]; i += 2) { | ||
177 | if(strcmp("content-encoding", nv[i]) == 0) { | ||
178 | gzip = strcmp("gzip", nv[i+1]) == 0; | ||
179 | break; | ||
180 | } | ||
181 | } | ||
182 | if(gzip) { | ||
183 | int rv; | ||
184 | if(req->inflater) { | ||
185 | return; | ||
186 | } | ||
187 | rv = spdylay_gzip_inflate_new(&req->inflater); | ||
188 | if(rv != 0) { | ||
189 | die("Can't allocate inflate stream."); | ||
190 | } | ||
191 | } | ||
192 | } | ||
193 | |||
194 | /* | ||
195 | * The implementation of spdylay_send_callback type. Here we write | ||
196 | * |data| with size |length| to the network and return the number of | ||
197 | * bytes actually written. See the documentation of | ||
198 | * spdylay_send_callback for the details. | ||
199 | */ | ||
200 | static ssize_t send_callback(spdylay_session *session, | ||
201 | const uint8_t *data, size_t length, int flags, | ||
202 | void *user_data) | ||
203 | { | ||
204 | (void)session; | ||
205 | (void)flags; | ||
206 | |||
207 | struct Connection *connection; | ||
208 | ssize_t rv; | ||
209 | connection = (struct Connection*)user_data; | ||
210 | connection->want_io = IO_NONE; | ||
211 | |||
212 | rv = write(connection->fd, | ||
213 | data, | ||
214 | length); | ||
215 | |||
216 | if (rv < 0) | ||
217 | { | ||
218 | switch(errno) | ||
219 | { | ||
220 | case EAGAIN: | ||
221 | #if EAGAIN != EWOULDBLOCK | ||
222 | case EWOULDBLOCK: | ||
223 | #endif | ||
224 | connection->want_io = WANT_WRITE; | ||
225 | rv = SPDYLAY_ERR_WOULDBLOCK; | ||
226 | break; | ||
227 | |||
228 | default: | ||
229 | rv = SPDYLAY_ERR_CALLBACK_FAILURE; | ||
230 | } | ||
231 | } | ||
232 | return rv; | ||
233 | } | ||
234 | |||
235 | /* | ||
236 | * The implementation of spdylay_recv_callback type. Here we read data | ||
237 | * from the network and write them in |buf|. The capacity of |buf| is | ||
238 | * |length| bytes. Returns the number of bytes stored in |buf|. See | ||
239 | * the documentation of spdylay_recv_callback for the details. | ||
240 | */ | ||
241 | static ssize_t recv_callback(spdylay_session *session, | ||
242 | uint8_t *buf, size_t length, int flags, | ||
243 | void *user_data) | ||
244 | { | ||
245 | (void)session; | ||
246 | (void)flags; | ||
247 | |||
248 | struct Connection *connection; | ||
249 | ssize_t rv; | ||
250 | connection = (struct Connection*)user_data; | ||
251 | connection->want_io = IO_NONE; | ||
252 | |||
253 | rv = read(connection->fd, | ||
254 | buf, | ||
255 | length); | ||
256 | |||
257 | if (rv < 0) | ||
258 | { | ||
259 | switch(errno) | ||
260 | { | ||
261 | case EAGAIN: | ||
262 | #if EAGAIN != EWOULDBLOCK | ||
263 | case EWOULDBLOCK: | ||
264 | #endif | ||
265 | connection->want_io = WANT_READ; | ||
266 | rv = SPDYLAY_ERR_WOULDBLOCK; | ||
267 | break; | ||
268 | |||
269 | default: | ||
270 | rv = SPDYLAY_ERR_CALLBACK_FAILURE; | ||
271 | } | ||
272 | } | ||
273 | else if(rv == 0) | ||
274 | rv = SPDYLAY_ERR_EOF; | ||
275 | return rv; | ||
276 | } | ||
277 | |||
278 | /* | ||
279 | * The implementation of spdylay_before_ctrl_send_callback type. We | ||
280 | * use this function to get stream ID of the request. This is because | ||
281 | * stream ID is not known when we submit the request | ||
282 | * (spdylay_submit_request). | ||
283 | */ | ||
284 | static void before_ctrl_send_callback(spdylay_session *session, | ||
285 | spdylay_frame_type type, | ||
286 | spdylay_frame *frame, | ||
287 | void *user_data) | ||
288 | { | ||
289 | (void)user_data; | ||
290 | |||
291 | if(type == SPDYLAY_SYN_STREAM) { | ||
292 | struct Request *req; | ||
293 | int stream_id = frame->syn_stream.stream_id; | ||
294 | req = spdylay_session_get_stream_user_data(session, stream_id); | ||
295 | if(req && req->stream_id == -1) { | ||
296 | req->stream_id = stream_id; | ||
297 | printf("[INFO] Stream ID = %d\n", stream_id); | ||
298 | } | ||
299 | } | ||
300 | } | ||
301 | |||
302 | static void on_ctrl_send_callback(spdylay_session *session, | ||
303 | spdylay_frame_type type, | ||
304 | spdylay_frame *frame, void *user_data) | ||
305 | { | ||
306 | (void)user_data; | ||
307 | |||
308 | char **nv; | ||
309 | const char *name = NULL; | ||
310 | int32_t stream_id; | ||
311 | size_t i; | ||
312 | switch(type) { | ||
313 | case SPDYLAY_SYN_STREAM: | ||
314 | nv = frame->syn_stream.nv; | ||
315 | name = "SYN_STREAM"; | ||
316 | stream_id = frame->syn_stream.stream_id; | ||
317 | break; | ||
318 | default: | ||
319 | break; | ||
320 | } | ||
321 | if(name && spdylay_session_get_stream_user_data(session, stream_id)) { | ||
322 | printf("[INFO] C ----------------------------> S (%s)\n", name); | ||
323 | for(i = 0; nv[i]; i += 2) { | ||
324 | printf(" %s: %s\n", nv[i], nv[i+1]); | ||
325 | } | ||
326 | } | ||
327 | } | ||
328 | |||
329 | static void on_ctrl_recv_callback(spdylay_session *session, | ||
330 | spdylay_frame_type type, | ||
331 | spdylay_frame *frame, void *user_data) | ||
332 | { | ||
333 | (void)user_data; | ||
334 | |||
335 | struct Request *req; | ||
336 | char **nv; | ||
337 | const char *name = NULL; | ||
338 | int32_t stream_id; | ||
339 | size_t i; | ||
340 | switch(type) { | ||
341 | case SPDYLAY_SYN_REPLY: | ||
342 | nv = frame->syn_reply.nv; | ||
343 | name = "SYN_REPLY"; | ||
344 | stream_id = frame->syn_reply.stream_id; | ||
345 | break; | ||
346 | case SPDYLAY_HEADERS: | ||
347 | nv = frame->headers.nv; | ||
348 | name = "HEADERS"; | ||
349 | stream_id = frame->headers.stream_id; | ||
350 | break; | ||
351 | default: | ||
352 | break; | ||
353 | } | ||
354 | if(!name) { | ||
355 | return; | ||
356 | } | ||
357 | req = spdylay_session_get_stream_user_data(session, stream_id); | ||
358 | if(req) { | ||
359 | check_gzip(req, nv); | ||
360 | printf("[INFO] C <---------------------------- S (%s)\n", name); | ||
361 | for(i = 0; nv[i]; i += 2) { | ||
362 | printf(" %s: %s\n", nv[i], nv[i+1]); | ||
363 | } | ||
364 | } | ||
365 | } | ||
366 | |||
367 | /* | ||
368 | * The implementation of spdylay_on_stream_close_callback type. We use | ||
369 | * this function to know the response is fully received. Since we just | ||
370 | * fetch 1 resource in this program, after reception of the response, | ||
371 | * we submit GOAWAY and close the session. | ||
372 | */ | ||
373 | static void on_stream_close_callback(spdylay_session *session, | ||
374 | int32_t stream_id, | ||
375 | spdylay_status_code status_code, | ||
376 | void *user_data) | ||
377 | { | ||
378 | (void)status_code; | ||
379 | (void)user_data; | ||
380 | |||
381 | struct Request *req; | ||
382 | req = spdylay_session_get_stream_user_data(session, stream_id); | ||
383 | if(req) { | ||
384 | int rv; | ||
385 | rv = spdylay_submit_goaway(session, SPDYLAY_GOAWAY_OK); | ||
386 | if(rv != 0) { | ||
387 | diec("spdylay_submit_goaway", rv); | ||
388 | } | ||
389 | } | ||
390 | } | ||
391 | |||
392 | #define MAX_OUTLEN 4096 | ||
393 | |||
394 | /* | ||
395 | * The implementation of spdylay_on_data_chunk_recv_callback type. We | ||
396 | * use this function to print the received response body. | ||
397 | */ | ||
398 | static void on_data_chunk_recv_callback(spdylay_session *session, uint8_t flags, | ||
399 | int32_t stream_id, | ||
400 | const uint8_t *data, size_t len, | ||
401 | void *user_data) | ||
402 | { | ||
403 | (void)flags; | ||
404 | (void)user_data; | ||
405 | |||
406 | struct Request *req; | ||
407 | req = spdylay_session_get_stream_user_data(session, stream_id); | ||
408 | if(req) { | ||
409 | printf("[INFO] C <---------------------------- S (DATA)\n"); | ||
410 | printf(" %lu bytes\n", (unsigned long int)len); | ||
411 | if(req->inflater) { | ||
412 | while(len > 0) { | ||
413 | uint8_t out[MAX_OUTLEN]; | ||
414 | size_t outlen = MAX_OUTLEN; | ||
415 | size_t tlen = len; | ||
416 | int rv; | ||
417 | rv = spdylay_gzip_inflate(req->inflater, out, &outlen, data, &tlen); | ||
418 | if(rv == -1) { | ||
419 | spdylay_submit_rst_stream(session, stream_id, SPDYLAY_INTERNAL_ERROR); | ||
420 | break; | ||
421 | } | ||
422 | fwrite(out, 1, outlen, stdout); | ||
423 | data += tlen; | ||
424 | len -= tlen; | ||
425 | } | ||
426 | } else { | ||
427 | /* TODO add support gzip */ | ||
428 | fwrite(data, 1, len, stdout); | ||
429 | |||
430 | //check if the data is correct | ||
431 | //if(strcmp(RESPONSE_BODY, data) != 0) | ||
432 | //killparent(parent, "\nreceived data is not the same"); | ||
433 | if(len + rcvbuf_c > strlen(RESPONSE_BODY)) | ||
434 | killparent(parent, "\nreceived data is not the same"); | ||
435 | |||
436 | strcpy(rcvbuf + rcvbuf_c,(char*)data); | ||
437 | rcvbuf_c+=len; | ||
438 | } | ||
439 | printf("\n"); | ||
440 | } | ||
441 | } | ||
442 | |||
443 | /* | ||
444 | * Setup callback functions. Spdylay API offers many callback | ||
445 | * functions, but most of them are optional. The send_callback is | ||
446 | * always required. Since we use spdylay_session_recv(), the | ||
447 | * recv_callback is also required. | ||
448 | */ | ||
449 | static void setup_spdylay_callbacks(spdylay_session_callbacks *callbacks) | ||
450 | { | ||
451 | memset(callbacks, 0, sizeof(spdylay_session_callbacks)); | ||
452 | callbacks->send_callback = send_callback; | ||
453 | callbacks->recv_callback = recv_callback; | ||
454 | callbacks->before_ctrl_send_callback = before_ctrl_send_callback; | ||
455 | callbacks->on_ctrl_send_callback = on_ctrl_send_callback; | ||
456 | callbacks->on_ctrl_recv_callback = on_ctrl_recv_callback; | ||
457 | callbacks->on_stream_close_callback = on_stream_close_callback; | ||
458 | callbacks->on_data_chunk_recv_callback = on_data_chunk_recv_callback; | ||
459 | } | ||
460 | |||
461 | |||
462 | /* | ||
463 | * Connects to the host |host| and port |port|. This function returns | ||
464 | * the file descriptor of the client socket. | ||
465 | */ | ||
466 | static int connect_to(const char *host, uint16_t port) | ||
467 | { | ||
468 | struct addrinfo hints; | ||
469 | int fd = -1; | ||
470 | int rv; | ||
471 | char service[NI_MAXSERV]; | ||
472 | struct addrinfo *res, *rp; | ||
473 | snprintf(service, sizeof(service), "%u", port); | ||
474 | memset(&hints, 0, sizeof(struct addrinfo)); | ||
475 | hints.ai_family = AF_UNSPEC; | ||
476 | hints.ai_socktype = SOCK_STREAM; | ||
477 | rv = getaddrinfo(host, service, &hints, &res); | ||
478 | if(rv != 0) { | ||
479 | dief("getaddrinfo", gai_strerror(rv)); | ||
480 | } | ||
481 | for(rp = res; rp; rp = rp->ai_next) { | ||
482 | fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); | ||
483 | if(fd == -1) { | ||
484 | continue; | ||
485 | } | ||
486 | while((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 && | ||
487 | errno == EINTR); | ||
488 | if(rv == 0) { | ||
489 | break; | ||
490 | } | ||
491 | MHD_socket_close_(fd); | ||
492 | fd = -1; | ||
493 | dief("connect", strerror(errno)); | ||
494 | } | ||
495 | freeaddrinfo(res); | ||
496 | return fd; | ||
497 | } | ||
498 | |||
499 | static void make_non_block(int fd) | ||
500 | { | ||
501 | int flags, rv; | ||
502 | while((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR); | ||
503 | if(flags == -1) { | ||
504 | dief("fcntl1", strerror(errno)); | ||
505 | } | ||
506 | while((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR); | ||
507 | if(rv == -1) { | ||
508 | dief("fcntl2", strerror(errno)); | ||
509 | } | ||
510 | } | ||
511 | |||
512 | /* | ||
513 | * Setting TCP_NODELAY is not mandatory for the SPDY protocol. | ||
514 | */ | ||
515 | static void set_tcp_nodelay(int fd) | ||
516 | { | ||
517 | int val = 1; | ||
518 | int rv; | ||
519 | rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val)); | ||
520 | if(rv == -1) { | ||
521 | dief("setsockopt", strerror(errno)); | ||
522 | } | ||
523 | } | ||
524 | |||
525 | /* | ||
526 | * Update |pollfd| based on the state of |connection|. | ||
527 | */ | ||
528 | static void ctl_poll(struct pollfd *pollfd, struct Connection *connection) | ||
529 | { | ||
530 | pollfd->events = 0; | ||
531 | if(spdylay_session_want_read(connection->session) || | ||
532 | connection->want_io == WANT_READ) { | ||
533 | pollfd->events |= POLLIN; | ||
534 | } | ||
535 | if(spdylay_session_want_write(connection->session) || | ||
536 | connection->want_io == WANT_WRITE) { | ||
537 | pollfd->events |= POLLOUT; | ||
538 | } | ||
539 | } | ||
540 | |||
541 | /* | ||
542 | * Submits the request |req| to the connection |connection|. This | ||
543 | * function does not send packets; just append the request to the | ||
544 | * internal queue in |connection->session|. | ||
545 | */ | ||
546 | static void submit_request(struct Connection *connection, struct Request *req) | ||
547 | { | ||
548 | int pri = 0; | ||
549 | int rv; | ||
550 | const char *nv[15]; | ||
551 | /* We always use SPDY/3 style header even if the negotiated protocol | ||
552 | version is SPDY/2. The library translates the header name as | ||
553 | necessary. Make sure that the last item is NULL! */ | ||
554 | nv[0] = ":method"; nv[1] = "GET"; | ||
555 | nv[2] = ":path"; nv[3] = req->path; | ||
556 | nv[4] = ":version"; nv[5] = "HTTP/1.1"; | ||
557 | nv[6] = ":scheme"; nv[7] = "https"; | ||
558 | nv[8] = ":host"; nv[9] = req->hostport; | ||
559 | nv[10] = "accept"; nv[11] = "*/*"; | ||
560 | nv[12] = "user-agent"; nv[13] = "spdylay/"SPDYLAY_VERSION; | ||
561 | nv[14] = NULL; | ||
562 | rv = spdylay_submit_request(connection->session, pri, nv, NULL, req); | ||
563 | if(rv != 0) { | ||
564 | diec("spdylay_submit_request", rv); | ||
565 | } | ||
566 | } | ||
567 | |||
568 | /* | ||
569 | * Performs the network I/O. | ||
570 | */ | ||
571 | static void exec_io(struct Connection *connection) | ||
572 | { | ||
573 | int rv; | ||
574 | rv = spdylay_session_recv(connection->session); | ||
575 | if(rv != 0) { | ||
576 | diec("spdylay_session_recv", rv); | ||
577 | } | ||
578 | rv = spdylay_session_send(connection->session); | ||
579 | if(rv != 0) { | ||
580 | diec("spdylay_session_send", rv); | ||
581 | } | ||
582 | } | ||
583 | |||
584 | static void request_init(struct Request *req, const struct URI *uri) | ||
585 | { | ||
586 | req->host = strcopy(uri->host, uri->hostlen); | ||
587 | req->port = uri->port; | ||
588 | req->path = strcopy(uri->path, uri->pathlen); | ||
589 | req->hostport = strcopy(uri->hostport, uri->hostportlen); | ||
590 | req->stream_id = -1; | ||
591 | req->inflater = NULL; | ||
592 | } | ||
593 | |||
594 | static void request_free(struct Request *req) | ||
595 | { | ||
596 | free(req->host); | ||
597 | free(req->path); | ||
598 | free(req->hostport); | ||
599 | spdylay_gzip_inflate_del(req->inflater); | ||
600 | } | ||
601 | |||
602 | /* | ||
603 | * Fetches the resource denoted by |uri|. | ||
604 | */ | ||
605 | static void fetch_uri(const struct URI *uri) | ||
606 | { | ||
607 | spdylay_session_callbacks callbacks; | ||
608 | int fd; | ||
609 | struct Request req; | ||
610 | struct Connection connection; | ||
611 | int rv; | ||
612 | nfds_t npollfds = 1; | ||
613 | struct pollfd pollfds[1]; | ||
614 | uint16_t spdy_proto_version = 3; | ||
615 | |||
616 | request_init(&req, uri); | ||
617 | |||
618 | setup_spdylay_callbacks(&callbacks); | ||
619 | |||
620 | /* Establish connection and setup SSL */ | ||
621 | fd = connect_to(req.host, req.port); | ||
622 | if (-1 == fd) | ||
623 | abort (); | ||
624 | |||
625 | connection.fd = fd; | ||
626 | connection.want_io = IO_NONE; | ||
627 | |||
628 | /* Here make file descriptor non-block */ | ||
629 | make_non_block(fd); | ||
630 | set_tcp_nodelay(fd); | ||
631 | |||
632 | printf("[INFO] SPDY protocol version = %d\n", spdy_proto_version); | ||
633 | rv = spdylay_session_client_new(&connection.session, spdy_proto_version, | ||
634 | &callbacks, &connection); | ||
635 | if(rv != 0) { | ||
636 | diec("spdylay_session_client_new", rv); | ||
637 | } | ||
638 | |||
639 | /* Submit the HTTP request to the outbound queue. */ | ||
640 | submit_request(&connection, &req); | ||
641 | |||
642 | pollfds[0].fd = fd; | ||
643 | ctl_poll(pollfds, &connection); | ||
644 | |||
645 | /* Event loop */ | ||
646 | while(spdylay_session_want_read(connection.session) || | ||
647 | spdylay_session_want_write(connection.session)) { | ||
648 | int nfds = poll(pollfds, npollfds, -1); | ||
649 | if(nfds == -1) { | ||
650 | dief("poll", strerror(errno)); | ||
651 | } | ||
652 | if(pollfds[0].revents & (POLLIN | POLLOUT)) { | ||
653 | exec_io(&connection); | ||
654 | } | ||
655 | if((pollfds[0].revents & POLLHUP) || (pollfds[0].revents & POLLERR)) { | ||
656 | die("Connection error"); | ||
657 | } | ||
658 | ctl_poll(pollfds, &connection); | ||
659 | } | ||
660 | |||
661 | /* Resource cleanup */ | ||
662 | spdylay_session_del(connection.session); | ||
663 | shutdown(fd, SHUT_WR); | ||
664 | MHD_socket_close_(fd); | ||
665 | request_free(&req); | ||
666 | } | ||
667 | |||
668 | static int parse_uri(struct URI *res, const char *uri) | ||
669 | { | ||
670 | /* We only interested in https */ | ||
671 | size_t len, i, offset; | ||
672 | memset(res, 0, sizeof(struct URI)); | ||
673 | len = strlen(uri); | ||
674 | if(len < 9 || memcmp("https://", uri, 8) != 0) { | ||
675 | return -1; | ||
676 | } | ||
677 | offset = 8; | ||
678 | res->host = res->hostport = &uri[offset]; | ||
679 | res->hostlen = 0; | ||
680 | if(uri[offset] == '[') { | ||
681 | /* IPv6 literal address */ | ||
682 | ++offset; | ||
683 | ++res->host; | ||
684 | for(i = offset; i < len; ++i) { | ||
685 | if(uri[i] == ']') { | ||
686 | res->hostlen = i-offset; | ||
687 | offset = i+1; | ||
688 | break; | ||
689 | } | ||
690 | } | ||
691 | } else { | ||
692 | const char delims[] = ":/?#"; | ||
693 | for(i = offset; i < len; ++i) { | ||
694 | if(strchr(delims, uri[i]) != NULL) { | ||
695 | break; | ||
696 | } | ||
697 | } | ||
698 | res->hostlen = i-offset; | ||
699 | offset = i; | ||
700 | } | ||
701 | if(res->hostlen == 0) { | ||
702 | return -1; | ||
703 | } | ||
704 | /* Assuming https */ | ||
705 | res->port = 443; | ||
706 | if(offset < len) { | ||
707 | if(uri[offset] == ':') { | ||
708 | /* port */ | ||
709 | const char delims[] = "/?#"; | ||
710 | int port = 0; | ||
711 | ++offset; | ||
712 | for(i = offset; i < len; ++i) { | ||
713 | if(strchr(delims, uri[i]) != NULL) { | ||
714 | break; | ||
715 | } | ||
716 | if('0' <= uri[i] && uri[i] <= '9') { | ||
717 | port *= 10; | ||
718 | port += uri[i]-'0'; | ||
719 | if(port > 65535) { | ||
720 | return -1; | ||
721 | } | ||
722 | } else { | ||
723 | return -1; | ||
724 | } | ||
725 | } | ||
726 | if(port == 0) { | ||
727 | return -1; | ||
728 | } | ||
729 | offset = i; | ||
730 | res->port = port; | ||
731 | } | ||
732 | } | ||
733 | res->hostportlen = uri+offset-res->host; | ||
734 | for(i = offset; i < len; ++i) { | ||
735 | if(uri[i] == '#') { | ||
736 | break; | ||
737 | } | ||
738 | } | ||
739 | if(i-offset == 0) { | ||
740 | res->path = "/"; | ||
741 | res->pathlen = 1; | ||
742 | } else { | ||
743 | res->path = &uri[offset]; | ||
744 | res->pathlen = i-offset; | ||
745 | } | ||
746 | return 0; | ||
747 | } | ||
748 | |||
749 | |||
750 | /***** | ||
751 | * end of code needed to utilize spdylay | ||
752 | */ | ||
753 | |||
754 | |||
755 | /***** | ||
756 | * start of code needed to utilize microspdy | ||
757 | */ | ||
758 | |||
759 | |||
760 | void | ||
761 | standard_request_handler(void *cls, | ||
762 | struct SPDY_Request * request, | ||
763 | uint8_t priority, | ||
764 | const char *method, | ||
765 | const char *path, | ||
766 | const char *version, | ||
767 | const char *host, | ||
768 | const char *scheme, | ||
769 | struct SPDY_NameValue * headers, | ||
770 | bool more) | ||
771 | { | ||
772 | (void)cls; | ||
773 | (void)request; | ||
774 | (void)priority; | ||
775 | (void)host; | ||
776 | (void)scheme; | ||
777 | (void)headers; | ||
778 | (void)method; | ||
779 | (void)version; | ||
780 | (void)more; | ||
781 | |||
782 | struct SPDY_Response *response=NULL; | ||
783 | |||
784 | if(strcmp(CLS,cls)!=0) | ||
785 | { | ||
786 | killchild(child,"wrong cls"); | ||
787 | } | ||
788 | |||
789 | response = SPDY_build_response(200,NULL,SPDY_HTTP_VERSION_1_1,NULL,RESPONSE_BODY,strlen(RESPONSE_BODY)); | ||
790 | |||
791 | if(NULL==response){ | ||
792 | fprintf(stdout,"no response obj\n"); | ||
793 | exit(3); | ||
794 | } | ||
795 | |||
796 | if(SPDY_queue_response(request,response,true,false,NULL,(void*)strdup(path))!=SPDY_YES) | ||
797 | { | ||
798 | fprintf(stdout,"queue\n"); | ||
799 | exit(4); | ||
800 | } | ||
801 | } | ||
802 | |||
803 | void | ||
804 | session_closed_handler (void *cls, | ||
805 | struct SPDY_Session * session, | ||
806 | int by_client) | ||
807 | { | ||
808 | printf("session_closed_handler called\n"); | ||
809 | |||
810 | if(strcmp(CLS,cls)!=0) | ||
811 | { | ||
812 | killchild(child,"wrong cls"); | ||
813 | } | ||
814 | |||
815 | if(SPDY_YES != by_client) | ||
816 | { | ||
817 | //killchild(child,"wrong by_client"); | ||
818 | printf("session closed by server\n"); | ||
819 | } | ||
820 | else | ||
821 | { | ||
822 | printf("session closed by client\n"); | ||
823 | } | ||
824 | |||
825 | if(NULL == session) | ||
826 | { | ||
827 | killchild(child,"session is NULL"); | ||
828 | } | ||
829 | |||
830 | session_closed_called = 1; | ||
831 | } | ||
832 | |||
833 | |||
834 | /***** | ||
835 | * end of code needed to utilize microspdy | ||
836 | */ | ||
837 | |||
838 | //child process | ||
839 | void | ||
840 | childproc(int port) | ||
841 | { | ||
842 | struct URI uri; | ||
843 | struct sigaction act; | ||
844 | int rv; | ||
845 | char *uristr; | ||
846 | |||
847 | memset(&act, 0, sizeof(struct sigaction)); | ||
848 | act.sa_handler = SIG_IGN; | ||
849 | sigaction(SIGPIPE, &act, 0); | ||
850 | |||
851 | usleep(10000); | ||
852 | asprintf(&uristr, "https://127.0.0.1:%i/",port); | ||
853 | if(NULL == (rcvbuf = malloc(strlen(RESPONSE_BODY)+1))) | ||
854 | killparent(parent,"no memory"); | ||
855 | |||
856 | rv = parse_uri(&uri, uristr); | ||
857 | if(rv != 0) { | ||
858 | killparent(parent,"parse_uri failed"); | ||
859 | } | ||
860 | fetch_uri(&uri); | ||
861 | |||
862 | if(strcmp(rcvbuf, RESPONSE_BODY)) | ||
863 | killparent(parent,"received data is different"); | ||
864 | } | ||
865 | |||
866 | //parent proc | ||
867 | int | ||
868 | parentproc( int port) | ||
869 | { | ||
870 | int childstatus; | ||
871 | unsigned long long timeoutlong=0; | ||
872 | struct timeval timeout; | ||
873 | int ret; | ||
874 | fd_set read_fd_set; | ||
875 | fd_set write_fd_set; | ||
876 | fd_set except_fd_set; | ||
877 | int maxfd = -1; | ||
878 | struct SPDY_Daemon *daemon; | ||
879 | |||
880 | SPDY_init(); | ||
881 | |||
882 | daemon = SPDY_start_daemon(port, | ||
883 | NULL, | ||
884 | NULL, | ||
885 | NULL,&session_closed_handler,&standard_request_handler,NULL,CLS, | ||
886 | SPDY_DAEMON_OPTION_IO_SUBSYSTEM, SPDY_IO_SUBSYSTEM_RAW, | ||
887 | SPDY_DAEMON_OPTION_FLAGS, SPDY_DAEMON_FLAG_NO_DELAY, | ||
888 | SPDY_DAEMON_OPTION_END); | ||
889 | |||
890 | if(NULL==daemon){ | ||
891 | printf("no daemon\n"); | ||
892 | return 1; | ||
893 | } | ||
894 | |||
895 | do | ||
896 | { | ||
897 | FD_ZERO(&read_fd_set); | ||
898 | FD_ZERO(&write_fd_set); | ||
899 | FD_ZERO(&except_fd_set); | ||
900 | |||
901 | ret = SPDY_get_timeout(daemon, &timeoutlong); | ||
902 | if(SPDY_NO == ret || timeoutlong > 1000) | ||
903 | { | ||
904 | timeout.tv_sec = 1; | ||
905 | timeout.tv_usec = 0; | ||
906 | } | ||
907 | else | ||
908 | { | ||
909 | timeout.tv_sec = timeoutlong / 1000; | ||
910 | timeout.tv_usec = (timeoutlong % 1000) * 1000; | ||
911 | } | ||
912 | |||
913 | maxfd = SPDY_get_fdset (daemon, | ||
914 | &read_fd_set, | ||
915 | &write_fd_set, | ||
916 | &except_fd_set); | ||
917 | |||
918 | ret = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout); | ||
919 | |||
920 | switch(ret) { | ||
921 | case -1: | ||
922 | printf("select error: %i\n", errno); | ||
923 | killchild(child, "select error"); | ||
924 | break; | ||
925 | case 0: | ||
926 | |||
927 | break; | ||
928 | default: | ||
929 | SPDY_run(daemon); | ||
930 | |||
931 | break; | ||
932 | } | ||
933 | } | ||
934 | while(waitpid(child,&childstatus,WNOHANG) != child); | ||
935 | |||
936 | //give chance to the client to close socket and handle this in run | ||
937 | usleep(100000); | ||
938 | SPDY_run(daemon); | ||
939 | |||
940 | SPDY_stop_daemon(daemon); | ||
941 | |||
942 | SPDY_deinit(); | ||
943 | |||
944 | return WEXITSTATUS(childstatus); | ||
945 | } | ||
946 | |||
947 | int main() | ||
948 | { | ||
949 | int port = get_port(12123); | ||
950 | parent = getpid(); | ||
951 | |||
952 | child = fork(); | ||
953 | if (child == -1) | ||
954 | { | ||
955 | fprintf(stderr, "can't fork, error %d\n", errno); | ||
956 | exit(EXIT_FAILURE); | ||
957 | } | ||
958 | |||
959 | if (child == 0) | ||
960 | { | ||
961 | childproc(port); | ||
962 | _exit(0); | ||
963 | } | ||
964 | else | ||
965 | { | ||
966 | int ret = parentproc(port); | ||
967 | if(1 == session_closed_called && 0 == ret) | ||
968 | exit(0); | ||
969 | else | ||
970 | exit(ret ? ret : 21); | ||
971 | } | ||
972 | return 1; | ||
973 | } | ||
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 | |||
47 | pid_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 | |||
57 | void | ||
58 | killproc(int pid, const char *message) | ||
59 | { | ||
60 | printf("%s\nkilling %i\n",message,pid); | ||
61 | kill(pid, SIGKILL); | ||
62 | } | ||
63 | |||
64 | |||
65 | void 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 | |||
77 | pid_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 | |||
91 | int 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 | |||
36 | pid_t parent; | ||
37 | pid_t child; | ||
38 | char *rcvbuf; | ||
39 | int rcvbuf_c = 0; | ||
40 | |||
41 | int session_closed_called = 0; | ||
42 | |||
43 | void | ||
44 | killchild(int pid, char *message) | ||
45 | { | ||
46 | printf("%s\n",message); | ||
47 | kill(pid, SIGKILL); | ||
48 | exit(1); | ||
49 | } | ||
50 | |||
51 | void | ||
52 | killparent(int pid, char *message) | ||
53 | { | ||
54 | printf("%s\n",message); | ||
55 | kill(pid, SIGKILL); | ||
56 | _exit(1); | ||
57 | } | ||
58 | |||
59 | |||
60 | /***** | ||
61 | * start of code needed to utilize spdylay | ||
62 | */ | ||
63 | |||
64 | #include <stdint.h> | ||
65 | #include <stdlib.h> | ||
66 | #include <unistd.h> | ||
67 | #include <fcntl.h> | ||
68 | #include <sys/types.h> | ||
69 | #include <sys/socket.h> | ||
70 | #include <netdb.h> | ||
71 | #include <netinet/in.h> | ||
72 | #include <netinet/tcp.h> | ||
73 | #include <poll.h> | ||
74 | #include <signal.h> | ||
75 | #include <stdio.h> | ||
76 | #include <assert.h> | ||
77 | |||
78 | #include <spdylay/spdylay.h> | ||
79 | |||
80 | #include <openssl/ssl.h> | ||
81 | #include <openssl/err.h> | ||
82 | |||
83 | enum { | ||
84 | IO_NONE, | ||
85 | WANT_READ, | ||
86 | WANT_WRITE | ||
87 | }; | ||
88 | |||
89 | struct 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 | |||
101 | struct 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 | |||
115 | struct 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 | */ | ||
130 | static 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 | */ | ||
144 | static 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 | */ | ||
154 | static 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 | */ | ||
164 | static 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 | */ | ||
175 | static 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 | */ | ||
203 | static 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 | */ | ||
235 | static 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 | */ | ||
269 | static 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 | |||
287 | static 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 | |||
314 | static 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 | */ | ||
358 | static 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 | */ | ||
383 | static 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 | */ | ||
434 | static 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 | */ | ||
451 | static 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 | */ | ||
475 | static 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 | |||
486 | static 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 | */ | ||
503 | static 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 | |||
535 | static 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 | */ | ||
551 | static 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 | */ | ||
564 | static 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 | */ | ||
582 | static 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 | */ | ||
607 | static 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 | |||
620 | static 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 | |||
630 | static 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 | */ | ||
641 | static 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 | |||
721 | static 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 | |||
813 | void | ||
814 | standard_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 | |||
860 | void | ||
861 | session_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 | ||
896 | void | ||
897 | childproc(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 | ||
926 | int | ||
927 | parentproc( 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 | |||
1003 | int 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 | |||
34 | int port; | ||
35 | |||
36 | pid_t parent; | ||
37 | pid_t child; | ||
38 | |||
39 | int run = 1; | ||
40 | int chunk_size=1; | ||
41 | |||
42 | void | ||
43 | killchild() | ||
44 | { | ||
45 | kill(child, SIGKILL); | ||
46 | exit(1); | ||
47 | } | ||
48 | |||
49 | void | ||
50 | killparent() | ||
51 | { | ||
52 | kill(parent, SIGKILL); | ||
53 | _exit(1); | ||
54 | } | ||
55 | |||
56 | ssize_t | ||
57 | response_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 | |||
86 | void | ||
87 | response_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 | |||
105 | void | ||
106 | standard_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 | |||
163 | int | ||
164 | parentproc() | ||
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 | |||
244 | int | ||
245 | md5(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 | |||
260 | int | ||
261 | childproc() | ||
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 | |||
296 | int | ||
297 | main() | ||
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() {\ | ||
54 | var 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';\ | ||
56 | var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);\ | ||
57 | })();" | ||
58 | |||
59 | int port; | ||
60 | |||
61 | #define NUM_CLIENTS 50 | ||
62 | |||
63 | pid_t parent; | ||
64 | int html_req_count; | ||
65 | int html_resp_count; | ||
66 | |||
67 | int child_c; | ||
68 | int children_pid[NUM_CLIENTS]; | ||
69 | int children_status[NUM_CLIENTS]; | ||
70 | |||
71 | int session_closed_called; | ||
72 | |||
73 | void | ||
74 | new_child(pid_t pid) | ||
75 | { | ||
76 | //todo ids overflow | ||
77 | children_pid[child_c] = pid; | ||
78 | children_status[child_c++] = 1; | ||
79 | } | ||
80 | |||
81 | int | ||
82 | alive_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 | |||
111 | void | ||
112 | killchild(int pid, char *message) | ||
113 | { | ||
114 | printf("%s\nkilling %i\n",message,pid); | ||
115 | kill(pid, SIGKILL); | ||
116 | exit(1); | ||
117 | } | ||
118 | |||
119 | void | ||
120 | killparent(int pid, const char *message) | ||
121 | { | ||
122 | printf("%s\nkilling %i\n",message,pid); | ||
123 | kill(pid, SIGKILL); | ||
124 | _exit(1); | ||
125 | } | ||
126 | |||
127 | void | ||
128 | standard_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 | |||
179 | void | ||
180 | run_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 | |||
212 | int | ||
213 | parentproc() | ||
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 | |||
293 | int 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 | |||
39 | int port; | ||
40 | |||
41 | pid_t parent; | ||
42 | pid_t child; | ||
43 | |||
44 | int run = 1; | ||
45 | int chunk_size=1; | ||
46 | int new_session; | ||
47 | int closed_session; | ||
48 | int do_sleep; | ||
49 | |||
50 | |||
51 | |||
52 | static unsigned long long | ||
53 | monotonic_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 | |||
66 | static void | ||
67 | killchild(char *msg) | ||
68 | { | ||
69 | printf("%s\n",msg); | ||
70 | kill(child, SIGKILL); | ||
71 | exit(1); | ||
72 | } | ||
73 | |||
74 | |||
75 | static void | ||
76 | killparent(char *msg) | ||
77 | { | ||
78 | printf("%s\n",msg); | ||
79 | kill(parent, SIGKILL); | ||
80 | _exit(1); | ||
81 | } | ||
82 | |||
83 | |||
84 | static void | ||
85 | new_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 | |||
97 | static void | ||
98 | closed_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 | |||
120 | static int | ||
121 | parentproc() | ||
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 | |||
269 | static int | ||
270 | childproc() | ||
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 | |||
314 | int | ||
315 | main() | ||
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 | |||
31 | char *pairs[] = {"one","1","two","2","three","3","four","4","five","5"}; | ||
32 | char *pairs_with_dups[] = {"one","1","two","2","one","11","two","22","three","3","two","222","two","2222","four","","five","5"};//82 | ||
33 | char *pairs_with_empty[] = {"name","","name","value"}; | ||
34 | char *pairs_different[] = {"30","thirty","40","fouthy"}; | ||
35 | int size; | ||
36 | int size2; | ||
37 | int brake_at = 3; | ||
38 | bool flag; | ||
39 | |||
40 | |||
41 | int | ||
42 | iterate_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 | |||
69 | int | ||
70 | iterate_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 | |||
90 | int | ||
91 | main() | ||
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 | } | ||