http-2 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,4 @@
1
1
  module HTTP2
2
-
3
2
  # HTTP 2.0 server connection class that implements appropriate header
4
3
  # compression / decompression algorithms and stream management logic.
5
4
  #
@@ -20,7 +19,6 @@ module HTTP2
20
19
  # end
21
20
  #
22
21
  class Server < Connection
23
-
24
22
  # Initialize new HTTP 2.0 server object.
25
23
  def initialize(**settings)
26
24
  @stream_id = 2
@@ -32,6 +30,84 @@ module HTTP2
32
30
  super
33
31
  end
34
32
 
33
+ # GET / HTTP/1.1
34
+ # Host: server.example.com
35
+ # Connection: Upgrade, HTTP2-Settings
36
+ # Upgrade: h2c
37
+ # HTTP2-Settings: <base64url encoding of HTTP/2 SETTINGS payload>
38
+ #
39
+ # Requests that contain a payload body MUST be sent in their entirety
40
+ # before the client can send HTTP/2 frames. This means that a large
41
+ # request can block the use of the connection until it is completely sent.
42
+ #
43
+ # If concurrency of an initial request with subsequent requests is
44
+ # important, an OPTIONS request can be used to perform the upgrade to
45
+ # HTTP/2, at the cost of an additional round trip.
46
+ #
47
+ # HTTP/1.1 101 Switching Protocols
48
+ # Connection: Upgrade
49
+ # Upgrade: h2c
50
+ #
51
+ # [ HTTP/2 connection ...
52
+ #
53
+ # - The first HTTP/2 frame sent by the server MUST be a server
54
+ # connection preface (Section 3.5) consisting of a SETTINGS frame.
55
+ # - Upon receiving the 101 response, the client MUST send a connection
56
+ # preface (Section 3.5), which includes a SETTINGS frame.
57
+ #
58
+ # The HTTP/1.1 request that is sent prior to upgrade is assigned a stream
59
+ # identifier of 1 (see Section 5.1.1) with default priority values
60
+ # (Section 5.3.5). Stream 1 is implicitly "half-closed" from the client
61
+ # toward the server (see Section 5.1), since the request is completed as
62
+ # an HTTP/1.1 request. After commencing the HTTP/2 connection, stream 1
63
+ # is used for the response.
64
+ #
65
+ def upgrade(settings, headers, body)
66
+ @h2c_upgrade = :start
67
+
68
+ # Pretend that we've received the preface
69
+ # - puts us into :waiting_connection_preface state
70
+ # - emits a SETTINGS frame to the client
71
+ receive(CONNECTION_PREFACE_MAGIC)
72
+
73
+ # Process received HTTP2-Settings payload
74
+ buf = HTTP2::Buffer.new Base64.urlsafe_decode64(settings)
75
+ buf.prepend(@framer.common_header(
76
+ length: buf.bytesize,
77
+ type: :settings,
78
+ stream: 0,
79
+ flags: []
80
+ ))
81
+ receive(buf)
82
+
83
+ # Activate stream (id: 1) with on HTTP/1.1 request parameters
84
+ stream = activate_stream(id: 1)
85
+ emit(:stream, stream)
86
+
87
+ headers_frame = {
88
+ type: :headers,
89
+ stream: 1,
90
+ weight: DEFAULT_WEIGHT,
91
+ dependency: 0,
92
+ exclusive: false,
93
+ payload: headers,
94
+ }
95
+
96
+ if body.empty?
97
+ headers_frame.merge!(flags: [:end_stream])
98
+ stream << headers_frame
99
+ else
100
+ stream << headers_frame
101
+ stream << {type: :data, stream: 1, payload: body, flags: [:end_stream]}
102
+ end
103
+
104
+ # Mark h2c upgrade as finished
105
+ @h2c_upgrade = :finished
106
+
107
+ # Transition back to :waiting_magic and wait for client's preface
108
+ @state = :waiting_magic
109
+ end
110
+
35
111
  private
36
112
 
37
113
  # Handle locally initiated server-push event emitted by the stream.
@@ -41,16 +117,15 @@ module HTTP2
41
117
  def promise(*args, &callback)
42
118
  parent, headers, flags = *args
43
119
  promise = new_stream(parent: parent)
44
- promise.send({
120
+ promise.send(
45
121
  type: :push_promise,
46
122
  flags: flags,
47
123
  stream: parent.id,
48
124
  promise_stream: promise.id,
49
- payload: headers.to_a
50
- })
125
+ payload: headers.to_a,
126
+ )
51
127
 
52
128
  callback.call(promise)
53
129
  end
54
130
  end
55
-
56
131
  end
@@ -1,5 +1,4 @@
1
1
  module HTTP2
2
-
3
2
  # A single HTTP 2.0 connection can multiplex multiple streams in parallel:
4
3
  # multiple requests and responses can be in flight simultaneously and stream
5
4
  # data can be interleaved and prioritized.
@@ -55,7 +54,7 @@ module HTTP2
55
54
  # Size of current stream flow control window.
56
55
  attr_reader :local_window
57
56
  attr_reader :remote_window
58
- alias :window :local_window
57
+ alias_method :window, :local_window
59
58
 
60
59
  # Reason why connection was closed.
61
60
  attr_reader :closed
@@ -72,12 +71,13 @@ module HTTP2
72
71
  # @param exclusive [Boolean]
73
72
  # @param window [Integer]
74
73
  # @param parent [Stream]
75
- def initialize(connection: nil, id: nil, weight: 16, dependency: 0, exclusive: false, parent: nil)
76
- @connection = connection or raise ArgumentError.new("missing mandatory argument connection")
77
- @id = id or raise ArgumentError.new("missing mandatory argument id")
74
+ def initialize(connection:, id:, weight: 16, dependency: 0, exclusive: false, parent: nil)
75
+ @connection = connection
76
+ @id = id
78
77
  @weight = weight
79
78
  @dependency = dependency
80
- process_priority({weight: weight, stream_dependency: dependency, exclusive: exclusive})
79
+ process_priority(weight: weight, stream_dependency: dependency, exclusive: exclusive)
80
+ @local_window = connection.local_settings[:settings_initial_window_size]
81
81
  @remote_window = connection.remote_settings[:settings_initial_window_size]
82
82
  @parent = parent
83
83
  @state = :idle
@@ -97,22 +97,21 @@ module HTTP2
97
97
 
98
98
  case frame[:type]
99
99
  when :data
100
- # TODO: when receiving DATA, keep track of local_window.
101
- emit(:data, frame[:payload]) if !frame[:ignore]
100
+ @local_window -= frame[:payload].size
101
+ emit(:data, frame[:payload]) unless frame[:ignore]
102
102
  when :headers, :push_promise
103
- emit(:headers, frame[:payload]) if !frame[:ignore]
103
+ emit(:headers, frame[:payload]) unless frame[:ignore]
104
104
  when :priority
105
105
  process_priority(frame)
106
106
  when :window_update
107
- @remote_window += frame[:increment]
108
- send_data
107
+ process_window_update(frame)
109
108
  when :altsvc, :blocked
110
109
  emit(frame[:type], frame)
111
110
  end
112
111
 
113
112
  complete_transition(frame)
114
113
  end
115
- alias :<< :receive
114
+ alias_method :<<, :receive
116
115
 
117
116
  # Processes outgoing HTTP 2.0 frames. Data frames may be automatically
118
117
  # split and buffered based on maximum frame size and current stream flow
@@ -125,8 +124,13 @@ module HTTP2
125
124
 
126
125
  process_priority(frame) if frame[:type] == :priority
127
126
 
128
- if frame[:type] == :data
127
+ case frame[:type]
128
+ when :data
129
+ # @remote_window is maintained in send_data
129
130
  send_data(frame)
131
+ when :window_update
132
+ @local_window += frame[:increment]
133
+ emit(:frame, frame)
130
134
  else
131
135
  emit(:frame, frame)
132
136
  end
@@ -144,11 +148,11 @@ module HTTP2
144
148
  flags << :end_headers if end_headers
145
149
  flags << :end_stream if end_stream
146
150
 
147
- send({type: :headers, flags: flags, payload: headers.to_a})
151
+ send(type: :headers, flags: flags, payload: headers.to_a)
148
152
  end
149
153
 
150
154
  def promise(headers, end_headers: true, &block)
151
- raise Exception.new("must provide callback") if !block_given?
155
+ fail ArgumentError, 'must provide callback' unless block_given?
152
156
 
153
157
  flags = end_headers ? [:end_headers] : []
154
158
  emit(:promise, self, headers, flags, &block)
@@ -161,7 +165,7 @@ module HTTP2
161
165
  # @param dependency [Integer] new stream dependency stream
162
166
  def reprioritize(weight: 16, dependency: 0, exclusive: false)
163
167
  stream_error if @id.even?
164
- send({type: :priority, weight: weight, stream_dependency: dependency, exclusive: exclusive})
168
+ send(type: :priority, weight: weight, stream_dependency: dependency, exclusive: exclusive)
165
169
  end
166
170
 
167
171
  # Sends DATA frame containing response payload.
@@ -169,18 +173,17 @@ module HTTP2
169
173
  # @param payload [String]
170
174
  # @param end_stream [Boolean] indicates last response DATA frame
171
175
  def data(payload, end_stream: true)
172
- flags = []
173
- flags << :end_stream if end_stream
174
-
175
176
  # Split data according to each frame is smaller enough
176
177
  # TODO: consider padding?
177
178
  max_size = @connection.remote_settings[:settings_max_frame_size]
178
- while payload.bytesize > max_size do
179
+ while payload.bytesize > max_size
179
180
  chunk = payload.slice!(0, max_size)
180
- send({type: :data, payload: chunk})
181
+ send(type: :data, flags: [], payload: chunk)
181
182
  end
182
183
 
183
- send({type: :data, flags: flags, payload: payload})
184
+ flags = []
185
+ flags << :end_stream if end_stream
186
+ send(type: :data, flags: flags, payload: payload)
184
187
  end
185
188
 
186
189
  # Sends a RST_STREAM frame which closes current stream - this does not
@@ -188,65 +191,74 @@ module HTTP2
188
191
  #
189
192
  # @param error [:Symbol] optional reason why stream was closed
190
193
  def close(error = :stream_closed)
191
- send({type: :rst_stream, error: error})
194
+ send(type: :rst_stream, error: error)
192
195
  end
193
196
 
194
197
  # Sends a RST_STREAM indicating that the stream is no longer needed.
195
198
  def cancel
196
- send({type: :rst_stream, error: :cancel})
199
+ send(type: :rst_stream, error: :cancel)
197
200
  end
198
201
 
199
202
  # Sends a RST_STREAM indicating that the stream has been refused prior
200
203
  # to performing any application processing.
201
204
  def refuse
202
- send({type: :rst_stream, error: :refused_stream})
205
+ send(type: :rst_stream, error: :refused_stream)
203
206
  end
204
207
 
205
208
  private
206
209
 
207
210
  # HTTP 2.0 Stream States
208
- # - http://tools.ietf.org/html/draft-ietf-httpbis-http2-05#section-5
211
+ # - http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-5.1
209
212
  #
210
- # +--------+
211
- # PP | | PP
212
- # ,--------| idle |--------.
213
- # / | | \
214
- # v +--------+ v
215
- # +----------+ | +----------+
216
- # | | | H | |
217
- # ,---| reserved | | | reserved |---.
218
- # | | (local) | v | (remote) | |
219
- # | +----------+ +--------+ +----------+ |
220
- # | | ES | | ES | |
221
- # | | H ,-------| open |-------. | H |
222
- # | | / | | \ | |
223
- # | v v +--------+ v v |
224
- # | +----------+ | +----------+ |
225
- # | | half | | | half | |
226
- # | | closed | | R | closed | |
227
- # | | (remote) | | | (local) | |
228
- # | +----------+ | +----------+ |
229
- # | | v | |
230
- # | | ES / R +--------+ ES / R | |
231
- # | `----------->| |<-----------' |
232
- # | R | closed | R |
233
- # `-------------------->| |<--------------------'
234
- # +--------+
213
+ # +--------+
214
+ # send PP | | recv PP
215
+ # ,--------| idle |--------.
216
+ # / | | \
217
+ # v +--------+ v
218
+ # +----------+ | +----------+
219
+ # | | | send H/ | |
220
+ # ,-----| reserved | | recv H | reserved |-----.
221
+ # | | (local) | | | (remote) | |
222
+ # | +----------+ v +----------+ |
223
+ # | | +--------+ | |
224
+ # | | recv ES | | send ES | |
225
+ # | send H | ,-------| open |-------. | recv H |
226
+ # | | / | | \ | |
227
+ # | v v +--------+ v v |
228
+ # | +----------+ | +----------+ |
229
+ # | | half | | | half | |
230
+ # | | closed | | send R/ | closed | |
231
+ # | | (remote) | | recv R | (local) | |
232
+ # | +----------+ | +----------+ |
233
+ # | | | | |
234
+ # | | send ES/ | recv ES/ | |
235
+ # | | send R/ v send R/ | |
236
+ # | | recv R +--------+ recv R | |
237
+ # | send R/ `----------->| |<-----------' send R/ |
238
+ # | recv R | closed | recv R |
239
+ # `---------------------->| |<----------------------'
240
+ # +--------+
235
241
  #
236
242
  def transition(frame, sending)
237
243
  case @state
238
244
 
239
245
  # All streams start in the "idle" state. In this state, no frames
240
246
  # have been exchanged.
247
+ # The following transitions are valid from this state:
241
248
  # * Sending or receiving a HEADERS frame causes the stream to
242
249
  # become "open". The stream identifier is selected as described
243
- # in Section 5.1.1.
244
- # * Sending a PUSH_PROMISE frame marks the associated stream for
250
+ # in Section 5.1.1. The same HEADERS frame can also cause a
251
+ # stream to immediately become "half closed".
252
+ # * Sending a PUSH_PROMISE frame reserves an idle stream for later
253
+ # use. The stream state for the reserved stream transitions to
254
+ # "reserved (local)".
255
+ # * Receiving a PUSH_PROMISE frame reserves an idle stream for
245
256
  # later use. The stream state for the reserved stream
246
- # transitions to "reserved (local)".
247
- # * Receiving a PUSH_PROMISE frame marks the associated stream as
248
- # reserved by the remote peer. The state of the stream becomes
249
- # "reserved (remote)".
257
+ # transitions to "reserved (remote)".
258
+ # Receiving any frames other than HEADERS, PUSH_PROMISE or PRIORITY
259
+ # on a stream in this state MUST be treated as a connection error
260
+ # (Section 5.4.1) of type PROTOCOL_ERROR.
261
+
250
262
  when :idle
251
263
  if sending
252
264
  case frame[:type]
@@ -258,7 +270,9 @@ module HTTP2
258
270
  event(:open)
259
271
  end
260
272
  when :rst_stream then event(:local_rst)
261
- else stream_error; end
273
+ when :priority then process_priority(frame)
274
+ else stream_error
275
+ end
262
276
  else
263
277
  case frame[:type]
264
278
  when :push_promise then event(:reserved_remote)
@@ -268,68 +282,81 @@ module HTTP2
268
282
  else
269
283
  event(:open)
270
284
  end
271
- else stream_error(:protocol_error); end
285
+ when :priority then process_priority(frame)
286
+ else stream_error(:protocol_error)
287
+ end
272
288
  end
273
289
 
274
290
  # A stream in the "reserved (local)" state is one that has been
275
291
  # promised by sending a PUSH_PROMISE frame. A PUSH_PROMISE frame
276
292
  # reserves an idle stream by associating the stream with an open
277
293
  # stream that was initiated by the remote peer (see Section 8.2).
294
+ # In this state, only the following transitions are possible:
278
295
  # * The endpoint can send a HEADERS frame. This causes the stream
279
296
  # to open in a "half closed (remote)" state.
280
297
  # * Either endpoint can send a RST_STREAM frame to cause the stream
281
- # to become "closed". This also releases the stream reservation.
282
- # An endpoint MUST NOT send any other type of frame in this state.
283
- # Receiving any frame other than RST_STREAM or PRIORITY MUST be
284
- # treated as a connection error (Section 5.4.1) of type
285
- # PROTOCOL_ERROR.
298
+ # to become "closed". This releases the stream reservation.
299
+ # An endpoint MUST NOT send any type of frame other than HEADERS,
300
+ # RST_STREAM, or PRIORITY in this state.
301
+ # A PRIORITY or WINDOW_UPDATE frame MAY be received in this state.
302
+ # Receiving any type of frame other than RST_STREAM, PRIORITY or
303
+ # WINDOW_UPDATE on a stream in this state MUST be treated as a
304
+ # connection error (Section 5.4.1) of type PROTOCOL_ERROR.
286
305
  when :reserved_local
287
306
  if sending
288
307
  @state = case frame[:type]
289
308
  when :headers then event(:half_closed_remote)
290
309
  when :rst_stream then event(:local_rst)
291
- else stream_error; end
310
+ else stream_error
311
+ end
292
312
  else
293
313
  @state = case frame[:type]
294
- when :rst_stream then event(:remote_rst)
295
- when :priority then @state
296
- else stream_error; end
314
+ when :rst_stream then event(:remote_rst)
315
+ when :priority, :window_update then @state
316
+ else stream_error
317
+ end
297
318
  end
298
319
 
299
320
  # A stream in the "reserved (remote)" state has been reserved by a
300
321
  # remote peer.
322
+ # In this state, only the following transitions are possible:
301
323
  # * Receiving a HEADERS frame causes the stream to transition to
302
324
  # "half closed (local)".
303
325
  # * Either endpoint can send a RST_STREAM frame to cause the stream
304
- # to become "closed". This also releases the stream reservation.
305
- # Receiving any other type of frame MUST be treated as a stream
306
- # error (Section 5.4.2) of type PROTOCOL_ERROR. An endpoint MAY
307
- # send RST_STREAM or PRIORITY frames in this state to cancel or
308
- # reprioritize the reserved stream.
326
+ # to become "closed". This releases the stream reservation.
327
+ # An endpoint MAY send a PRIORITY frame in this state to
328
+ # reprioritize the reserved stream. An endpoint MUST NOT send any
329
+ # type of frame other than RST_STREAM, WINDOW_UPDATE, or PRIORITY in
330
+ # this state.
331
+ # Receiving any type of frame other than HEADERS, RST_STREAM or
332
+ # PRIORITY on a stream in this state MUST be treated as a connection
333
+ # error (Section 5.4.1) of type PROTOCOL_ERROR.
309
334
  when :reserved_remote
310
335
  if sending
311
336
  @state = case frame[:type]
312
337
  when :rst_stream then event(:local_rst)
313
- when :priority then @state
314
- else stream_error; end
338
+ when :priority, :window_update then @state
339
+ else stream_error
340
+ end
315
341
  else
316
342
  @state = case frame[:type]
317
- when :headers then event(:half_closed_local)
318
- when :rst_stream then event(:remote_rst)
319
- else stream_error; end
343
+ when :headers then event(:half_closed_local)
344
+ when :rst_stream then event(:remote_rst)
345
+ else stream_error
346
+ end
320
347
  end
321
348
 
322
- # The "open" state is where both peers can send frames of any type.
323
- # In this state, sending peers observe advertised stream level flow
324
- # control limits (Section 5.2).
325
- # * From this state either endpoint can send a frame with a END_STREAM
326
- # flag set, which causes the stream to transition into one of the
327
- # "half closed" states: an endpoint sending a END_STREAM flag causes
328
- # the stream state to become "half closed (local)"; an endpoint
329
- # receiving a END_STREAM flag causes the stream state to become
330
- # "half closed (remote)".
331
- # * Either endpoint can send a RST_STREAM frame from this state,
332
- # causing it to transition immediately to "closed".
349
+ # A stream in the "open" state may be used by both peers to send
350
+ # frames of any type. In this state, sending peers observe
351
+ # advertised stream level flow control limits (Section 5.2).
352
+ # From this state either endpoint can send a frame with an
353
+ # END_STREAM flag set, which causes the stream to transition into
354
+ # one of the "half closed" states: an endpoint sending an END_STREAM
355
+ # flag causes the stream state to become "half closed (local)"; an
356
+ # endpoint receiving an END_STREAM flag causes the stream state to
357
+ # become "half closed (remote)".
358
+ # Either endpoint can send a RST_STREAM frame from this state,
359
+ # causing it to transition immediately to "closed".
333
360
  when :open
334
361
  if sending
335
362
  case frame[:type]
@@ -345,14 +372,17 @@ module HTTP2
345
372
  end
346
373
  end
347
374
 
348
- # A stream that is "half closed (local)" cannot be used for sending
349
- # frames.
375
+ # A stream that is in the "half closed (local)" state cannot be used
376
+ # for sending frames. Only WINDOW_UPDATE, PRIORITY and RST_STREAM
377
+ # frames can be sent in this state.
350
378
  # A stream transitions from this state to "closed" when a frame that
351
- # contains a END_STREAM flag is received, or when either peer sends
379
+ # contains an END_STREAM flag is received, or when either peer sends
352
380
  # a RST_STREAM frame.
353
- # A receiver can ignore WINDOW_UPDATE or PRIORITY frames in this
354
- # state. These frame types might arrive for a short period after a
355
- # frame bearing the END_STREAM flag is sent.
381
+ # A receiver can ignore WINDOW_UPDATE frames in this state, which
382
+ # might arrive for a short period after a frame bearing the
383
+ # END_STREAM flag is sent.
384
+ # PRIORITY frames received in this state are used to reprioritize
385
+ # streams that depend on the current stream.
356
386
  when :half_closed_local
357
387
  if sending
358
388
  case frame[:type]
@@ -360,6 +390,8 @@ module HTTP2
360
390
  event(:local_rst)
361
391
  when :priority
362
392
  process_priority(frame)
393
+ when :window_update
394
+ # nop here
363
395
  else
364
396
  stream_error
365
397
  end
@@ -371,7 +403,7 @@ module HTTP2
371
403
  when :priority
372
404
  process_priority(frame)
373
405
  when :window_update
374
- frame[:ignore] = true
406
+ # nop here
375
407
  end
376
408
  end
377
409
 
@@ -380,11 +412,16 @@ module HTTP2
380
412
  # obligated to maintain a receiver flow control window if it
381
413
  # performs flow control.
382
414
  # If an endpoint receives additional frames for a stream that is in
383
- # this state it MUST respond with a stream error (Section 5.4.2) of
384
- # type STREAM_CLOSED.
415
+ # this state, other than WINDOW_UPDATE, PRIORITY or RST_STREAM, it
416
+ # MUST respond with a stream error (Section 5.4.2) of type
417
+ # STREAM_CLOSED.
418
+ # A stream that is "half closed (remote)" can be used by the
419
+ # endpoint to send frames of any type. In this state, the endpoint
420
+ # continues to observe advertised stream level flow control limits
421
+ # (Section 5.2).
385
422
  # A stream can transition from this state to "closed" by sending a
386
- # frame that contains a END_STREAM flag, or when either peer sends a
387
- # RST_STREAM frame.
423
+ # frame that contains an END_STREAM flag, or when either peer sends
424
+ # a RST_STREAM frame.
388
425
  when :half_closed_remote
389
426
  if sending
390
427
  case frame[:type]
@@ -395,32 +432,53 @@ module HTTP2
395
432
  else
396
433
  case frame[:type]
397
434
  when :rst_stream then event(:remote_rst)
398
- when :window_update then frame[:ignore] = true
399
435
  when :priority
400
436
  process_priority(frame)
401
- else stream_error(:stream_closed); end
437
+ when :window_update
438
+ # nop
439
+ else
440
+ stream_error(:stream_closed)
441
+ end
402
442
  end
403
443
 
404
- # An endpoint MUST NOT send frames on a closed stream. An endpoint
405
- # that receives a frame after receiving a RST_STREAM or a frame
406
- # containing a END_STREAM flag on that stream MUST treat that as a
407
- # stream error (Section 5.4.2) of type STREAM_CLOSED.
408
- #
409
- # WINDOW_UPDATE or PRIORITY frames can be received in this state for
410
- # a short period after a a frame containing an END_STREAM flag is
411
- # sent. Until the remote peer receives and processes the frame
412
- # bearing the END_STREAM flag, it might send either frame type.
413
- #
444
+ # The "closed" state is the terminal state.
445
+ # An endpoint MUST NOT send frames other than PRIORITY on a closed
446
+ # stream. An endpoint that receives any frame other than PRIORITY
447
+ # after receiving a RST_STREAM MUST treat that as a stream error
448
+ # (Section 5.4.2) of type STREAM_CLOSED. Similarly, an endpoint
449
+ # that receives any frames after receiving a frame with the
450
+ # END_STREAM flag set MUST treat that as a connection error
451
+ # (Section 5.4.1) of type STREAM_CLOSED, unless the frame is
452
+ # permitted as described below.
453
+ # WINDOW_UPDATE or RST_STREAM frames can be received in this state
454
+ # for a short period after a DATA or HEADERS frame containing an
455
+ # END_STREAM flag is sent. Until the remote peer receives and
456
+ # processes RST_STREAM or the frame bearing the END_STREAM flag, it
457
+ # might send frames of these types. Endpoints MUST ignore
458
+ # WINDOW_UPDATE or RST_STREAM frames received in this state, though
459
+ # endpoints MAY choose to treat frames that arrive a significant
460
+ # time after sending END_STREAM as a connection error
461
+ # (Section 5.4.1) of type PROTOCOL_ERROR.
462
+ # PRIORITY frames can be sent on closed streams to prioritize
463
+ # streams that are dependent on the closed stream. Endpoints SHOULD
464
+ # process PRIORITY frames, though they can be ignored if the stream
465
+ # has been removed from the dependency tree (see Section 5.3.4).
414
466
  # If this state is reached as a result of sending a RST_STREAM
415
467
  # frame, the peer that receives the RST_STREAM might have already
416
468
  # sent - or enqueued for sending - frames on the stream that cannot
417
- # be withdrawn. An endpoint MUST ignore frames that it receives on
418
- # closed streams after it has sent a RST_STREAM frame.
419
- #
420
- # An endpoint might receive a PUSH_PROMISE or a CONTINUATION frame
421
- # after it sends RST_STREAM. PUSH_PROMISE causes a stream to become
422
- # "reserved". If promised streams are not desired, a RST_STREAM can
423
- # be used to close any of those streams.
469
+ # be withdrawn. An endpoint MUST ignore frames that it receives on
470
+ # closed streams after it has sent a RST_STREAM frame. An endpoint
471
+ # MAY choose to limit the period over which it ignores frames and
472
+ # treat frames that arrive after this time as being in error.
473
+ # Flow controlled frames (i.e., DATA) received after sending
474
+ # RST_STREAM are counted toward the connection flow control window.
475
+ # Even though these frames might be ignored, because they are sent
476
+ # before the sender receives the RST_STREAM, the sender will
477
+ # consider the frames to count against the flow control window.
478
+ # An endpoint might receive a PUSH_PROMISE frame after it sends
479
+ # RST_STREAM. PUSH_PROMISE causes a stream to become "reserved"
480
+ # even if the associated stream has been reset. Therefore, a
481
+ # RST_STREAM is needed to close an unwanted promised stream.
424
482
  when :closed
425
483
  if sending
426
484
  case frame[:type]
@@ -428,7 +486,7 @@ module HTTP2
428
486
  when :priority then
429
487
  process_priority(frame)
430
488
  else
431
- stream_error(:stream_closed) if !(frame[:type] == :rst_stream)
489
+ stream_error(:stream_closed) unless (frame[:type] == :rst_stream)
432
490
  end
433
491
  else
434
492
  if frame[:type] == :priority
@@ -436,9 +494,13 @@ module HTTP2
436
494
  else
437
495
  case @closed
438
496
  when :remote_rst, :remote_closed
439
- stream_error(:stream_closed) if !(frame[:type] == :rst_stream)
497
+ case frame[:type]
498
+ when :rst_stream, :window_update # nop here
499
+ else
500
+ stream_error(:stream_closed)
501
+ end
440
502
  when :local_rst, :local_closed
441
- frame[:ignore] = true
503
+ frame[:ignore] = true if frame[:type] != :window_update
442
504
  end
443
505
  end
444
506
  end
@@ -482,13 +544,15 @@ module HTTP2
482
544
  def process_priority(frame)
483
545
  @weight = frame[:weight]
484
546
  @dependency = frame[:stream_dependency]
485
- emit(:priority,
486
- weight: frame[:weight],
487
- dependency: frame[:stream_dependency],
488
- exclusive: frame[:exclusive])
547
+ emit(
548
+ :priority,
549
+ weight: frame[:weight],
550
+ dependency: frame[:stream_dependency],
551
+ exclusive: frame[:exclusive],
552
+ )
489
553
  # TODO: implement dependency tree housekeeping
490
554
  # Latest draft defines a fairly complex priority control.
491
- # See https://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-5.3
555
+ # See https://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-5.3
492
556
  # We currently have no prioritization among streams.
493
557
  # We should add code here.
494
558
  end
@@ -497,15 +561,16 @@ module HTTP2
497
561
  case frame[:type]
498
562
  when :data, :headers, :continuation
499
563
  frame[:flags].include?(:end_stream)
500
- else false; end
564
+ else false
565
+ end
501
566
  end
502
567
 
503
- def stream_error(error = :stream_error, msg: nil)
568
+ def stream_error(error = :internal_error, msg: nil)
504
569
  @error = error
505
570
  close(error) if @state != :closed
506
571
 
507
572
  klass = error.to_s.split('_').map(&:capitalize).join
508
- raise Error.const_get(klass).new(msg)
573
+ fail Error.const_get(klass), msg
509
574
  end
510
575
  end
511
576
  end