http-2 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,474 @@
1
+ module HTTP2
2
+
3
+ # A single HTTP 2.0 connection can multiplex multiple streams in parallel:
4
+ # multiple requests and responses can be in flight simultaneously and stream
5
+ # data can be interleaved and prioritized.
6
+ #
7
+ # This class encapsulates all of the state, transition, flow-control, and
8
+ # error management as defined by the HTTP 2.0 specification. All you have
9
+ # to do is subscribe to appropriate events (marked with ":" prefix in
10
+ # diagram below) and provide your application logic to handle request
11
+ # and response processing.
12
+ #
13
+ # +--------+
14
+ # PP | | PP
15
+ # ,--------| idle |--------.
16
+ # / | | \
17
+ # v +--------+ v
18
+ # +----------+ | +----------+
19
+ # | | | H | |
20
+ # ,---|:reserved | | |:reserved |---.
21
+ # | | (local) | v | (remote) | |
22
+ # | +----------+ +--------+ +----------+ |
23
+ # | | :active | | :active | |
24
+ # | | ,-------|:active |-------. | |
25
+ # | | H / ES | | ES \ H | |
26
+ # | v v +--------+ v v |
27
+ # | +-----------+ | +-_---------+ |
28
+ # | |:half_close| | |:half_close| |
29
+ # | | (remote) | | | (local) | |
30
+ # | +-----------+ | +-----------+ |
31
+ # | | v | |
32
+ # | | ES/R +--------+ ES/R | |
33
+ # | `----------->| |<-----------' |
34
+ # | R | :close | R |
35
+ # `-------------------->| |<--------------------'
36
+ # +--------+
37
+ class Stream
38
+ include FlowBuffer
39
+ include Emitter
40
+ include Error
41
+
42
+ # Stream ID (odd for client initiated streams, even otherwise).
43
+ attr_reader :id
44
+
45
+ # Stream state as defined by HTTP 2.0.
46
+ attr_reader :state
47
+
48
+ # Request parent stream of push stream.
49
+ attr_reader :parent
50
+
51
+ # Stream priority as set by initiator.
52
+ attr_reader :priority
53
+
54
+ # Size of current stream flow control window.
55
+ attr_reader :window
56
+
57
+ # Reason why connection was closed.
58
+ attr_reader :closed
59
+
60
+ # Initializes new stream.
61
+ #
62
+ # Note that you should never have to call this directly. To create a new
63
+ # client initiated stream, use Connection#new_stream. Similarly, Connection
64
+ # will emit new stream objects, when new stream frames are received.
65
+ #
66
+ # @param id [Integer]
67
+ # @param priority [Integer]
68
+ # @param window [Integer]
69
+ # @param parent [Stream]
70
+ def initialize(id, priority, window, parent = nil)
71
+ @id = id
72
+ @priority = priority
73
+ @window = window
74
+ @parent = parent
75
+ @state = :idle
76
+ @error = false
77
+ @closed = false
78
+ @send_buffer = []
79
+
80
+ on(:window) { |v| @window = v }
81
+ end
82
+
83
+ # Processes incoming HTTP 2.0 frames. The frames must be decoded upstream.
84
+ #
85
+ # @param frame [Hash]
86
+ def receive(frame)
87
+ transition(frame, false)
88
+
89
+ case frame[:type]
90
+ when :data
91
+ emit(:data, frame[:payload]) if !frame[:ignore]
92
+ when :headers, :push_promise
93
+ if frame[:payload].is_a? Array
94
+ emit(:headers, Hash[*frame[:payload].flatten]) if !frame[:ignore]
95
+ else
96
+ emit(:headers, frame[:payload]) if !frame[:ignore]
97
+ end
98
+ when :priority
99
+ @priority = frame[:priority]
100
+ emit(:priority, @priority)
101
+ when :window_update
102
+ @window += frame[:increment]
103
+ send_data
104
+ end
105
+
106
+ complete_transition(frame)
107
+ end
108
+ alias :<< :receive
109
+
110
+ # Processes outgoing HTTP 2.0 frames. Data frames may be automatically
111
+ # split and buffered based on maximum frame size and current stream flow
112
+ # control window size.
113
+ #
114
+ # @param frame [Hash]
115
+ def send(frame)
116
+ transition(frame, true)
117
+ frame[:stream] ||= @id
118
+
119
+ @priority = frame[:priority] if frame[:type] == :priority
120
+
121
+ if frame[:type] == :data
122
+ send_data(frame)
123
+ else
124
+ emit(:frame, frame)
125
+ end
126
+
127
+ complete_transition(frame)
128
+ end
129
+
130
+ # Sends a HEADERS frame containing HTTP response headers.
131
+ #
132
+ # @param headers [Hash]
133
+ # @param end_headers [Boolean] indicates that no more headers will be sent
134
+ # @param end_stream [Boolean] indicates that no payload will be sent
135
+ def headers(headers, end_headers: true, end_stream: false)
136
+ flags = []
137
+ flags << :end_headers if end_headers
138
+ flags << :end_stream if end_stream
139
+
140
+ send({type: :headers, flags: flags, payload: headers.to_a})
141
+ end
142
+
143
+ def promise(headers, end_push_promise: true, &block)
144
+ raise Exception.new("must provide callback") if !block_given?
145
+
146
+ flags = end_push_promise ? [:end_push_promise] : []
147
+ emit(:promise, self, headers, flags, &block)
148
+ end
149
+
150
+ # Sends a PRIORITY frame with new stream priority value (can only be
151
+ # performed by the client).
152
+ #
153
+ # @param p [Integer] new stream priority value
154
+ def reprioritize(p)
155
+ stream_error if @id.even?
156
+ send({type: :priority, priority: p})
157
+ end
158
+
159
+ # Sends DATA frame containing response payload.
160
+ #
161
+ # @param payload [String]
162
+ # @param end_stream [Boolean] indicates last response DATA frame
163
+ def data(payload, end_stream: true)
164
+ flags = []
165
+ flags << :end_stream if end_stream
166
+
167
+ while payload.bytesize > MAX_FRAME_SIZE do
168
+ chunk = payload.slice!(0, MAX_FRAME_SIZE)
169
+ send({type: :data, payload: chunk})
170
+ end
171
+
172
+ send({type: :data, flags: flags, payload: payload})
173
+ end
174
+
175
+ # Sends a RST_STREAM frame which closes current stream - this does not
176
+ # close the underlying connection.
177
+ #
178
+ # @param error [:Symbol] optional reason why stream was closed
179
+ def close(error = :stream_closed)
180
+ send({type: :rst_stream, error: error})
181
+ end
182
+
183
+ # Sends a RST_STREAM indicating that the stream is no longer needed.
184
+ def cancel
185
+ send({type: :rst_stream, error: :cancel})
186
+ end
187
+
188
+ # Sends a RST_STREAM indicating that the stream has been refused prior
189
+ # to performing any application processing.
190
+ def refuse
191
+ send({type: :rst_stream, error: :refused_stream})
192
+ end
193
+
194
+ private
195
+
196
+ # HTTP 2.0 Stream States
197
+ # - http://tools.ietf.org/html/draft-ietf-httpbis-http2-05#section-5
198
+ #
199
+ # +--------+
200
+ # PP | | PP
201
+ # ,--------| idle |--------.
202
+ # / | | \
203
+ # v +--------+ v
204
+ # +----------+ | +----------+
205
+ # | | | H | |
206
+ # ,---| reserved | | | reserved |---.
207
+ # | | (local) | v | (remote) | |
208
+ # | +----------+ +--------+ +----------+ |
209
+ # | | ES | | ES | |
210
+ # | | H ,-------| open |-------. | H |
211
+ # | | / | | \ | |
212
+ # | v v +--------+ v v |
213
+ # | +----------+ | +----------+ |
214
+ # | | half | | | half | |
215
+ # | | closed | | R | closed | |
216
+ # | | (remote) | | | (local) | |
217
+ # | +----------+ | +----------+ |
218
+ # | | v | |
219
+ # | | ES / R +--------+ ES / R | |
220
+ # | `----------->| |<-----------' |
221
+ # | R | closed | R |
222
+ # `-------------------->| |<--------------------'
223
+ # +--------+
224
+ #
225
+ def transition(frame, sending)
226
+ case @state
227
+
228
+ # All streams start in the "idle" state. In this state, no frames
229
+ # have been exchanged.
230
+ # * Sending or receiving a HEADERS frame causes the stream to
231
+ # become "open". The stream identifier is selected as described
232
+ # in Section 5.1.1.
233
+ # * Sending a PUSH_PROMISE frame marks the associated stream for
234
+ # later use. The stream state for the reserved stream
235
+ # transitions to "reserved (local)".
236
+ # * Receiving a PUSH_PROMISE frame marks the associated stream as
237
+ # reserved by the remote peer. The state of the stream becomes
238
+ # "reserved (remote)".
239
+ when :idle
240
+ if sending
241
+ case frame[:type]
242
+ when :push_promise then event(:reserved_local)
243
+ when :headers
244
+ if end_stream?(frame)
245
+ event(:half_closed_local)
246
+ else
247
+ event(:open)
248
+ end
249
+ when :rst_stream then event(:local_rst)
250
+ else stream_error; end
251
+ else
252
+ case frame[:type]
253
+ when :push_promise then event(:reserved_remote)
254
+ when :headers
255
+ if end_stream?(frame)
256
+ event(:half_closed_remote)
257
+ else
258
+ event(:open)
259
+ end
260
+ else stream_error(:protocol_error); end
261
+ end
262
+
263
+ # A stream in the "reserved (local)" state is one that has been
264
+ # promised by sending a PUSH_PROMISE frame. A PUSH_PROMISE frame
265
+ # reserves an idle stream by associating the stream with an open
266
+ # stream that was initiated by the remote peer (see Section 8.2).
267
+ # * The endpoint can send a HEADERS frame. This causes the stream
268
+ # to open in a "half closed (remote)" state.
269
+ # * Either endpoint can send a RST_STREAM frame to cause the stream
270
+ # to become "closed". This also releases the stream reservation.
271
+ # An endpoint MUST NOT send any other type of frame in this state.
272
+ # Receiving any frame other than RST_STREAM or PRIORITY MUST be
273
+ # treated as a connection error (Section 5.4.1) of type
274
+ # PROTOCOL_ERROR.
275
+ when :reserved_local
276
+ if sending
277
+ @state = case frame[:type]
278
+ when :headers then event(:half_closed_remote)
279
+ when :rst_stream then event(:local_rst)
280
+ else stream_error; end
281
+ else
282
+ @state = case frame[:type]
283
+ when :rst_stream then event(:remote_rst)
284
+ when :priority then @state
285
+ else stream_error; end
286
+ end
287
+
288
+ # A stream in the "reserved (remote)" state has been reserved by a
289
+ # remote peer.
290
+ # * Receiving a HEADERS frame causes the stream to transition to
291
+ # "half closed (local)".
292
+ # * Either endpoint can send a RST_STREAM frame to cause the stream
293
+ # to become "closed". This also releases the stream reservation.
294
+ # Receiving any other type of frame MUST be treated as a stream
295
+ # error (Section 5.4.2) of type PROTOCOL_ERROR. An endpoint MAY
296
+ # send RST_STREAM or PRIORITY frames in this state to cancel or
297
+ # reprioritize the reserved stream.
298
+ when :reserved_remote
299
+ if sending
300
+ @state = case frame[:type]
301
+ when :rst_stream then event(:local_rst)
302
+ when :priority then @state
303
+ else stream_error; end
304
+ else
305
+ @state = case frame[:type]
306
+ when :headers then event(:half_closed_local)
307
+ when :rst_stream then event(:remote_rst)
308
+ else stream_error; end
309
+ end
310
+
311
+ # The "open" state is where both peers can send frames of any type.
312
+ # In this state, sending peers observe advertised stream level flow
313
+ # control limits (Section 5.2).
314
+ # * From this state either endpoint can send a frame with a END_STREAM
315
+ # flag set, which causes the stream to transition into one of the
316
+ # "half closed" states: an endpoint sending a END_STREAM flag causes
317
+ # the stream state to become "half closed (local)"; an endpoint
318
+ # receiving a END_STREAM flag causes the stream state to become
319
+ # "half closed (remote)".
320
+ # * Either endpoint can send a RST_STREAM frame from this state,
321
+ # causing it to transition immediately to "closed".
322
+ when :open
323
+ if sending
324
+ case frame[:type]
325
+ when :data, :headers, :continuation
326
+ event(:half_closed_local) if end_stream?(frame)
327
+ when :rst_stream then event(:local_rst)
328
+ end
329
+ else
330
+ case frame[:type]
331
+ when :data, :headers, :continuation
332
+ event(:half_closed_remote) if end_stream?(frame)
333
+ when :rst_stream then event(:remote_rst)
334
+ end
335
+ end
336
+
337
+ # A stream that is "half closed (local)" cannot be used for sending
338
+ # frames.
339
+ # A stream transitions from this state to "closed" when a frame that
340
+ # contains a END_STREAM flag is received, or when either peer sends
341
+ # a RST_STREAM frame.
342
+ # A receiver can ignore WINDOW_UPDATE or PRIORITY frames in this
343
+ # state. These frame types might arrive for a short period after a
344
+ # frame bearing the END_STREAM flag is sent.
345
+ when :half_closed_local
346
+ if sending
347
+ if frame[:type] == :rst_stream
348
+ event(:local_rst)
349
+ else
350
+ stream_error
351
+ end
352
+ else
353
+ case frame[:type]
354
+ when :data, :headers, :continuation
355
+ event(:remote_closed) if end_stream?(frame)
356
+ when :rst_stream then event(:remote_rst)
357
+ when :window_update, :priority
358
+ frame[:igore] = true
359
+ end
360
+ end
361
+
362
+ # A stream that is "half closed (remote)" is no longer being used by
363
+ # the peer to send frames. In this state, an endpoint is no longer
364
+ # obligated to maintain a receiver flow control window if it
365
+ # performs flow control.
366
+ # If an endpoint receives additional frames for a stream that is in
367
+ # this state it MUST respond with a stream error (Section 5.4.2) of
368
+ # type STREAM_CLOSED.
369
+ # A stream can transition from this state to "closed" by sending a
370
+ # frame that contains a END_STREAM flag, or when either peer sends a
371
+ # RST_STREAM frame.
372
+ when :half_closed_remote
373
+ if sending
374
+ case frame[:type]
375
+ when :data, :headers, :continuation
376
+ event(:local_closed) if end_stream?(frame)
377
+ when :rst_stream then event(:local_rst)
378
+ end
379
+ else
380
+ case frame[:type]
381
+ when :rst_stream then event(:remote_rst)
382
+ when :window_update then frame[:ignore] = true
383
+ else stream_error(:stream_closed); end
384
+ end
385
+
386
+ # An endpoint MUST NOT send frames on a closed stream. An endpoint
387
+ # that receives a frame after receiving a RST_STREAM or a frame
388
+ # containing a END_STREAM flag on that stream MUST treat that as a
389
+ # stream error (Section 5.4.2) of type STREAM_CLOSED.
390
+ #
391
+ # WINDOW_UPDATE or PRIORITY frames can be received in this state for
392
+ # a short period after a a frame containing an END_STREAM flag is
393
+ # sent. Until the remote peer receives and processes the frame
394
+ # bearing the END_STREAM flag, it might send either frame type.
395
+ #
396
+ # If this state is reached as a result of sending a RST_STREAM
397
+ # frame, the peer that receives the RST_STREAM might have already
398
+ # sent - or enqueued for sending - frames on the stream that cannot
399
+ # be withdrawn. An endpoint MUST ignore frames that it receives on
400
+ # closed streams after it has sent a RST_STREAM frame.
401
+ #
402
+ # An endpoint might receive a PUSH_PROMISE or a CONTINUATION frame
403
+ # after it sends RST_STREAM. PUSH_PROMISE causes a stream to become
404
+ # "reserved". If promised streams are not desired, a RST_STREAM can
405
+ # be used to close any of those streams.
406
+ when :closed
407
+ if sending
408
+ case frame[:type]
409
+ when :rst_stream then # ignore
410
+ else
411
+ stream_error(:stream_closed) if !(frame[:type] == :rst_stream)
412
+ end
413
+ else
414
+ case @closed
415
+ when :remote_rst, :remote_closed
416
+ stream_error(:stream_closed) if !(frame[:type] == :rst_stream)
417
+ when :local_rst, :local_closed
418
+ frame[:ignore] = true
419
+ end
420
+ end
421
+ end
422
+ end
423
+
424
+ def event(newstate)
425
+ case newstate
426
+ when :open
427
+ @state = newstate
428
+ emit(:active)
429
+
430
+ when :reserved_local, :reserved_remote
431
+ @state = newstate
432
+ emit(:reserved)
433
+
434
+ when :half_closed_local, :half_closed_remote
435
+ @closed = newstate
436
+ emit(:active) unless @state == :open
437
+ @state = :half_closing
438
+
439
+ when :local_closed, :remote_closed, :local_rst, :remote_rst
440
+ @closed = newstate
441
+ @state = :closing
442
+ end
443
+
444
+ @state
445
+ end
446
+
447
+ def complete_transition(frame)
448
+ case @state
449
+ when :closing
450
+ @state = :closed
451
+ emit(:close, frame[:error])
452
+ when :half_closing
453
+ @state = @closed
454
+ emit(:half_close)
455
+ end
456
+ end
457
+
458
+ def end_stream?(frame)
459
+ case frame[:type]
460
+ when :data, :headers, :continuation
461
+ frame[:flags].include?(:end_stream)
462
+ else false; end
463
+ end
464
+
465
+ def stream_error(error = :stream_error, msg: nil)
466
+ @error = error
467
+ close(error) if @state != :closed
468
+
469
+ klass = error.to_s.split('_').map(&:capitalize).join
470
+ raise Kernel.const_get(klass).new(msg)
471
+ end
472
+
473
+ end
474
+ end
@@ -0,0 +1,3 @@
1
+ module HTTP2
2
+ VERSION = "0.6.1"
3
+ end
data/lib/http/2.rb ADDED
@@ -0,0 +1,9 @@
1
+ require "http/2/version"
2
+ require "http/2/error"
3
+ require "http/2/emitter"
4
+ require "http/2/buffer"
5
+ require "http/2/flow_buffer"
6
+ require "http/2/compressor"
7
+ require "http/2/framer"
8
+ require "http/2/connection"
9
+ require "http/2/stream"