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.
- checksums.yaml +4 -4
- data/.rspec +1 -0
- data/.rubocop.yml +18 -0
- data/.rubocop_todo.yml +46 -0
- data/.travis.yml +10 -2
- data/Gemfile +7 -5
- data/README.md +35 -37
- data/Rakefile +9 -10
- data/example/README.md +0 -13
- data/example/client.rb +12 -15
- data/example/helper.rb +2 -2
- data/example/server.rb +19 -19
- data/example/upgrade_server.rb +191 -0
- data/http-2.gemspec +10 -9
- data/lib/http/2.rb +13 -13
- data/lib/http/2/buffer.rb +6 -10
- data/lib/http/2/client.rb +5 -10
- data/lib/http/2/compressor.rb +134 -146
- data/lib/http/2/connection.rb +104 -100
- data/lib/http/2/emitter.rb +2 -4
- data/lib/http/2/error.rb +7 -7
- data/lib/http/2/flow_buffer.rb +11 -10
- data/lib/http/2/framer.rb +78 -87
- data/lib/http/2/huffman.rb +265 -274
- data/lib/http/2/huffman_statemachine.rb +257 -257
- data/lib/http/2/server.rb +81 -6
- data/lib/http/2/stream.rb +195 -130
- data/lib/http/2/version.rb +1 -1
- data/lib/tasks/generate_huffman_table.rb +30 -24
- data/spec/buffer_spec.rb +11 -13
- data/spec/client_spec.rb +41 -42
- data/spec/compressor_spec.rb +243 -242
- data/spec/connection_spec.rb +252 -248
- data/spec/emitter_spec.rb +12 -12
- data/spec/framer_spec.rb +177 -179
- data/spec/helper.rb +56 -57
- data/spec/huffman_spec.rb +33 -33
- data/spec/server_spec.rb +15 -15
- data/spec/stream_spec.rb +356 -265
- metadata +7 -6
- data/spec/hpack_test_spec.rb +0 -83
data/lib/http/2/server.rb
CHANGED
@@ -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
|
data/lib/http/2/stream.rb
CHANGED
@@ -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
|
-
|
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
|
76
|
-
@connection = connection
|
77
|
-
@id = 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(
|
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
|
-
|
101
|
-
emit(:data, frame[:payload])
|
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])
|
103
|
+
emit(:headers, frame[:payload]) unless frame[:ignore]
|
104
104
|
when :priority
|
105
105
|
process_priority(frame)
|
106
106
|
when :window_update
|
107
|
-
|
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
|
-
|
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
|
-
|
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(
|
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
|
-
|
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(
|
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
|
179
|
+
while payload.bytesize > max_size
|
179
180
|
chunk = payload.slice!(0, max_size)
|
180
|
-
send(
|
181
|
+
send(type: :data, flags: [], payload: chunk)
|
181
182
|
end
|
182
183
|
|
183
|
-
|
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(
|
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(
|
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(
|
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-
|
211
|
+
# - http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-5.1
|
209
212
|
#
|
210
|
-
#
|
211
|
-
# PP
|
212
|
-
#
|
213
|
-
#
|
214
|
-
#
|
215
|
-
#
|
216
|
-
#
|
217
|
-
#
|
218
|
-
# |
|
219
|
-
# |
|
220
|
-
# |
|
221
|
-
# |
|
222
|
-
# |
|
223
|
-
# |
|
224
|
-
# |
|
225
|
-
# |
|
226
|
-
# | |
|
227
|
-
# |
|
228
|
-
# |
|
229
|
-
# |
|
230
|
-
# |
|
231
|
-
# |
|
232
|
-
# |
|
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
|
-
#
|
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 (
|
247
|
-
#
|
248
|
-
#
|
249
|
-
#
|
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
|
-
|
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
|
-
|
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
|
282
|
-
# An endpoint MUST NOT send any
|
283
|
-
#
|
284
|
-
#
|
285
|
-
#
|
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
|
310
|
+
else stream_error
|
311
|
+
end
|
292
312
|
else
|
293
313
|
@state = case frame[:type]
|
294
|
-
when :rst_stream
|
295
|
-
when :priority
|
296
|
-
else stream_error
|
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
|
305
|
-
#
|
306
|
-
#
|
307
|
-
#
|
308
|
-
#
|
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
|
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
|
318
|
-
when :rst_stream
|
319
|
-
else stream_error
|
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
|
-
#
|
323
|
-
# In this state, sending peers observe
|
324
|
-
# control limits (Section 5.2).
|
325
|
-
#
|
326
|
-
#
|
327
|
-
#
|
328
|
-
#
|
329
|
-
#
|
330
|
-
#
|
331
|
-
#
|
332
|
-
#
|
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
|
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
|
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
|
354
|
-
#
|
355
|
-
#
|
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
|
-
|
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
|
384
|
-
#
|
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
|
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
|
-
|
437
|
+
when :window_update
|
438
|
+
# nop
|
439
|
+
else
|
440
|
+
stream_error(:stream_closed)
|
441
|
+
end
|
402
442
|
end
|
403
443
|
|
404
|
-
#
|
405
|
-
#
|
406
|
-
#
|
407
|
-
#
|
408
|
-
#
|
409
|
-
#
|
410
|
-
#
|
411
|
-
#
|
412
|
-
#
|
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.
|
418
|
-
# closed streams after it has sent a RST_STREAM frame.
|
419
|
-
#
|
420
|
-
#
|
421
|
-
#
|
422
|
-
#
|
423
|
-
#
|
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)
|
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
|
-
|
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(
|
486
|
-
|
487
|
-
|
488
|
-
|
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-
|
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
|
564
|
+
else false
|
565
|
+
end
|
501
566
|
end
|
502
567
|
|
503
|
-
def stream_error(error = :
|
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
|
-
|
573
|
+
fail Error.const_get(klass), msg
|
509
574
|
end
|
510
575
|
end
|
511
576
|
end
|