http-2 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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