h2 0.5.0 → 0.6.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/CHANGELOG.md +11 -1
- data/README.md +4 -4
- data/examples/server/hello_world.rb +1 -1
- data/examples/server/https_hello_world.rb +2 -2
- data/examples/server/push_promise.rb +9 -5
- data/exe/h2 +5 -2
- data/h2.gemspec +1 -1
- data/lib/h2.rb +27 -5
- data/lib/h2/client.rb +105 -7
- data/lib/h2/server/stream.rb +8 -38
- data/lib/h2/server/stream/request.rb +2 -2
- data/lib/h2/stream.rb +33 -0
- data/lib/h2/version.rb +1 -1
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e62e384940a1e408102e4e518942c1245ce374f82007c858d9de5a0b286c2301
|
4
|
+
data.tar.gz: d3fc7f3d111b3786cb50c58f862b32dad713557ae69cf18233cd1eda7ca0d5be
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 907a28fb440862cbc3a6a9dbb80db39408f3635f4aa4983358bba508617ddbb8fcb9da0a30b3601a614f4ea7a79ff4cceece1b7d1c687b6afc3e663ad5d4459d
|
7
|
+
data.tar.gz: 317f75c090f2f54526a2bff4b2e8b7d298ab7f6bf8f4653e9bd5ac2916f333133e26a7b47d357c447a7a0a6741468b99733e1212c71ee018f4698c51b0ea5eaf
|
data/CHANGELOG.md
CHANGED
@@ -1,7 +1,17 @@
|
|
1
1
|
h2 changelog
|
2
2
|
============
|
3
3
|
|
4
|
-
### 0.
|
4
|
+
### 0.6.0 25 jul 2018
|
5
|
+
|
6
|
+
* update server API - kwargs
|
7
|
+
* update client API - addr: -> host:
|
8
|
+
* add rubydoc, update readme
|
9
|
+
|
10
|
+
### 0.5.0 21 jul 2018
|
11
|
+
|
12
|
+
* add server
|
13
|
+
|
14
|
+
### 0.4.1 17 jul 2018
|
5
15
|
|
6
16
|
* update .travis.yml for latest supported versions
|
7
17
|
* add CLI flags for threading model
|
data/README.md
CHANGED
@@ -20,7 +20,7 @@ require 'h2/server'
|
|
20
20
|
|
21
21
|
server = H2::Server::HTTP.new host: addr, port: port do |connection|
|
22
22
|
connection.each_stream do |stream|
|
23
|
-
stream.respond :
|
23
|
+
stream.respond status: 200, body: "hello, world!\n"
|
24
24
|
stream.connection.goaway
|
25
25
|
end
|
26
26
|
end
|
@@ -29,7 +29,7 @@ stream = H2.get url: "http://#{addr}:#{port}", tls: false
|
|
29
29
|
stream.body #=> "hello, world!\n"
|
30
30
|
```
|
31
31
|
|
32
|
-
See [examples](https://github.com/kenichi/h2/tree/master/examples/server/)
|
32
|
+
See [examples](https://github.com/kenichi/h2/tree/master/examples/server/).
|
33
33
|
|
34
34
|
## Client Usage
|
35
35
|
|
@@ -55,7 +55,7 @@ client.closed? #=> true
|
|
55
55
|
# --- normal connection
|
56
56
|
#
|
57
57
|
|
58
|
-
client = H2::Client.new
|
58
|
+
client = H2::Client.new host: 'example.com', port: 443
|
59
59
|
|
60
60
|
stream = client.get path: '/'
|
61
61
|
|
@@ -103,7 +103,7 @@ If you're running on macOS and using Homebrew's openssl package, you may need to
|
|
103
103
|
specify the CA file in the TLS options:
|
104
104
|
|
105
105
|
```ruby
|
106
|
-
client = H2::Client.new
|
106
|
+
client = H2::Client.new host: 'example.com', port: 443, tls: { ca_file: '/usr/local/etc/openssl/cert.pem' }
|
107
107
|
```
|
108
108
|
|
109
109
|
or when using the CLI:
|
@@ -12,7 +12,7 @@ addr, port = '127.0.0.1', 1234
|
|
12
12
|
puts "*** Starting server on http://#{addr}:#{port}"
|
13
13
|
s = H2::Server::HTTP.new host: addr, port: port do |connection|
|
14
14
|
connection.each_stream do |stream|
|
15
|
-
stream.respond :
|
15
|
+
stream.respond status: 200, body: "hello, world!\n"
|
16
16
|
stream.connection.goaway
|
17
17
|
end
|
18
18
|
end
|
@@ -21,9 +21,9 @@ s = H2::Server::HTTPS.new host: addr, port: port, **tls do |connection|
|
|
21
21
|
stream.goaway_on_complete
|
22
22
|
|
23
23
|
if stream.request.path == '/favicon.ico'
|
24
|
-
stream.respond :
|
24
|
+
stream.respond status: 404
|
25
25
|
else
|
26
|
-
stream.respond :
|
26
|
+
stream.respond status: 200, body: "hello, world!\n"
|
27
27
|
end
|
28
28
|
end
|
29
29
|
end
|
@@ -11,7 +11,7 @@ port = 1234
|
|
11
11
|
addr = Socket.getaddrinfo('localhost', port).first[3]
|
12
12
|
certs_dir = File.expand_path '../../../tmp/certs', __FILE__
|
13
13
|
dog_png = File.read File.expand_path '../dog.png', __FILE__
|
14
|
-
push_promise = '<html>wait for it...<img src="/dog.png"/><script src="/pushed.js"></script></html>'
|
14
|
+
push_promise = '<html><body>wait for it...<img src="/dog.png"/><script src="/pushed.js"></script></body></html>'
|
15
15
|
pushed_js = '(()=>{ alert("hello h2 push promise!"); })();'
|
16
16
|
|
17
17
|
sni = {
|
@@ -27,17 +27,21 @@ s = H2::Server::HTTPS.new host: addr, port: port, sni: sni do |connection|
|
|
27
27
|
connection.each_stream do |stream|
|
28
28
|
|
29
29
|
if stream.request.path == '/favicon.ico'
|
30
|
-
stream.respond :
|
30
|
+
stream.respond status: 404
|
31
31
|
|
32
32
|
else
|
33
33
|
stream.goaway_on_complete
|
34
34
|
|
35
|
-
|
35
|
+
# one-line convenience with async "keep" handler
|
36
|
+
stream.push_promise path: '/dog.png', headers: { 'content-type' => 'image/png' }, body: dog_png
|
36
37
|
|
37
|
-
|
38
|
+
# more control over when promises are "kept"...
|
39
|
+
js_promise = stream.push_promise_for path: '/pushed.js',
|
40
|
+
headers: { 'content-type' => 'application/javascript' },
|
41
|
+
body: pushed_js
|
38
42
|
js_promise.make_on stream
|
39
43
|
|
40
|
-
stream.respond :
|
44
|
+
stream.respond status: 200, body: push_promise
|
41
45
|
|
42
46
|
js_promise.keep
|
43
47
|
end
|
data/exe/h2
CHANGED
@@ -8,9 +8,8 @@
|
|
8
8
|
require 'colored'
|
9
9
|
require 'optparse'
|
10
10
|
|
11
|
-
$: << File.expand_path('../../../http-2/lib', __FILE__)
|
12
|
-
|
13
11
|
begin # {{{
|
12
|
+
raise LoadError if ARGV.include?('--dev')
|
14
13
|
require 'h2'
|
15
14
|
rescue LoadError
|
16
15
|
$: << File.expand_path('../../lib', __FILE__)
|
@@ -60,6 +59,10 @@ OptionParser.new do |o|
|
|
60
59
|
options[:debug] = true
|
61
60
|
end
|
62
61
|
|
62
|
+
o.on '--dev', 'load lib from source tree' do
|
63
|
+
# handled above for reasons
|
64
|
+
end
|
65
|
+
|
63
66
|
o.on '-g', '--goaway', 'send GOAWAY frame when stream is complete' do
|
64
67
|
options[:goaway] = true
|
65
68
|
end
|
data/h2.gemspec
CHANGED
@@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
|
|
17
17
|
|
18
18
|
spec.required_ruby_version = '>= 2.2'
|
19
19
|
|
20
|
-
spec.add_dependency 'http-2', '~> 0.
|
20
|
+
spec.add_dependency 'http-2', '~> 0.10', '>= 0.10.0'
|
21
21
|
spec.add_dependency 'colored', '1.2'
|
22
22
|
|
23
23
|
spec.add_development_dependency "bundler", "~> 1.15"
|
data/lib/h2.rb
CHANGED
@@ -30,6 +30,10 @@ module H2
|
|
30
30
|
|
31
31
|
class << self
|
32
32
|
|
33
|
+
# convenience wrappers to make requests with HTTP methods
|
34
|
+
#
|
35
|
+
# @see H2.request
|
36
|
+
#
|
33
37
|
REQUEST_METHODS.each do |m|
|
34
38
|
define_method m do |**args, &block|
|
35
39
|
request method: m, **args, &block
|
@@ -38,25 +42,43 @@ module H2
|
|
38
42
|
|
39
43
|
private
|
40
44
|
|
41
|
-
|
45
|
+
# creates a +H2::Client+ and initiates a +H2::Stream+ by making a request
|
46
|
+
# with the given HTTP method
|
47
|
+
#
|
48
|
+
# @param [String] host IP address or hostname
|
49
|
+
# @param [Integer] port TCP port
|
50
|
+
# @param [String,URI] url full URL to parse (optional: existing +URI+ instance)
|
51
|
+
# @param [Symbol] method HTTP request method
|
52
|
+
# @param [String] path request path
|
53
|
+
# @param [Hash] headers request headers
|
54
|
+
# @param [Hash] params request query string parameters
|
55
|
+
# @param [String] body request body
|
56
|
+
# @param [Hash,FalseClass] tls TLS options (optional: +false+ do not use TLS)
|
57
|
+
# @option tls [String] :cafile path to CA file
|
58
|
+
#
|
59
|
+
# @yield [H2::Stream]
|
60
|
+
#
|
61
|
+
# @return [H2::Stream]
|
62
|
+
#
|
63
|
+
def request host: nil,
|
42
64
|
port: nil,
|
65
|
+
url: nil,
|
43
66
|
method:,
|
44
67
|
path: '/',
|
45
68
|
headers: {},
|
46
69
|
params: {},
|
47
70
|
body: nil,
|
48
|
-
url: nil,
|
49
71
|
tls: {},
|
50
72
|
&block
|
51
73
|
|
52
|
-
raise ArgumentError if url.nil? && (
|
74
|
+
raise ArgumentError if url.nil? && (host.nil? || port.nil?)
|
53
75
|
if url
|
54
76
|
url = URI.parse url unless URI === url
|
55
|
-
|
77
|
+
host = url.host
|
56
78
|
port = url.port
|
57
79
|
path = url.request_uri
|
58
80
|
end
|
59
|
-
c = Client.new
|
81
|
+
c = Client.new host: host, port: port, tls: tls
|
60
82
|
c.__send__ method, path: path, headers: headers, params: params, body: body, &block
|
61
83
|
end
|
62
84
|
end
|
data/lib/h2/client.rb
CHANGED
@@ -21,24 +21,34 @@ module H2
|
|
21
21
|
attr_accessor :last_stream
|
22
22
|
attr_reader :client, :reader, :scheme, :socket, :streams
|
23
23
|
|
24
|
-
|
25
|
-
|
24
|
+
# create a new h2 client
|
25
|
+
#
|
26
|
+
# @param [String] host IP address or hostname
|
27
|
+
# @param [Integer] port TCP port
|
28
|
+
# @param [String,URI] url full URL to parse (optional: existing +URI+ instance)
|
29
|
+
# @param [Hash,FalseClass] tls TLS options (optional: +false+ do not use TLS)
|
30
|
+
# @option tls [String] :cafile path to CA file
|
31
|
+
#
|
32
|
+
# @return [H2::Client]
|
33
|
+
#
|
34
|
+
def initialize host: nil, port: nil, url: nil, tls: {}
|
35
|
+
raise ArgumentError if url.nil? && (host.nil? || port.nil?)
|
26
36
|
|
27
37
|
if url
|
28
38
|
url = URI.parse url unless URI === url
|
29
|
-
@
|
39
|
+
@host = url.host
|
30
40
|
@port = url.port
|
31
41
|
@scheme = url.scheme
|
32
42
|
tls = false if 'http' == @scheme
|
33
43
|
else
|
34
|
-
@
|
44
|
+
@host = host
|
35
45
|
@port = port
|
36
46
|
@scheme = tls ? 'https' : 'http'
|
37
47
|
end
|
38
48
|
|
39
49
|
@tls = tls
|
40
50
|
@streams = {}
|
41
|
-
@socket = TCPSocket.new(@
|
51
|
+
@socket = TCPSocket.new(@host, @port)
|
42
52
|
@socket = tls_socket @socket if @tls
|
43
53
|
@client = HTTP2::Client.new
|
44
54
|
|
@@ -49,10 +59,14 @@ module H2
|
|
49
59
|
read
|
50
60
|
end
|
51
61
|
|
62
|
+
# @return true if the connection is closed
|
63
|
+
#
|
52
64
|
def closed?
|
53
65
|
@socket.closed?
|
54
66
|
end
|
55
67
|
|
68
|
+
# close the connection
|
69
|
+
#
|
56
70
|
def close
|
57
71
|
unblock!
|
58
72
|
@socket.close unless closed?
|
@@ -62,28 +76,55 @@ module H2
|
|
62
76
|
@socket.eof?
|
63
77
|
end
|
64
78
|
|
79
|
+
# send a goaway frame and wait until the connection is closed
|
80
|
+
#
|
65
81
|
def goaway!
|
66
82
|
goaway block: true
|
67
83
|
end
|
68
84
|
|
85
|
+
# send a goaway frame and optionally wait for the connection to be closed
|
86
|
+
#
|
87
|
+
# @param [Boolean] block waits for close if +true+, returns immediately otherwise
|
88
|
+
#
|
89
|
+
# @return +false+ if already closed
|
90
|
+
# @return +nil+
|
91
|
+
#
|
69
92
|
def goaway block: false
|
70
93
|
return false if closed?
|
71
94
|
@client.goaway
|
72
95
|
block! if block
|
73
96
|
end
|
74
97
|
|
98
|
+
# binds all connection events to their respective on_ handlers
|
99
|
+
#
|
75
100
|
def bind_events
|
76
101
|
CONNECTION_EVENTS.each do |e|
|
77
102
|
@client.on(e){|*a| __send__ "on_#{e}", *a}
|
78
103
|
end
|
79
104
|
end
|
80
105
|
|
106
|
+
# convenience wrappers to make requests with HTTP methods
|
107
|
+
#
|
108
|
+
# @see Client#request
|
109
|
+
#
|
81
110
|
REQUEST_METHODS.each do |m|
|
82
111
|
define_method m do |**args, &block|
|
83
112
|
request method: m, **args, &block
|
84
113
|
end
|
85
114
|
end
|
86
115
|
|
116
|
+
# initiate a +Stream+ by making a request with the given HTTP method
|
117
|
+
#
|
118
|
+
# @param [Symbol] method HTTP request method
|
119
|
+
# @param [String] path request path
|
120
|
+
# @param [Hash] headers request headers
|
121
|
+
# @param [Hash] params request query string parameters
|
122
|
+
# @param [String] body request body
|
123
|
+
#
|
124
|
+
# @yield [H2::Stream]
|
125
|
+
#
|
126
|
+
# @return [H2::Stream]
|
127
|
+
#
|
87
128
|
def request method:, path:, headers: {}, params: {}, body: nil, &block
|
88
129
|
s = @client.new_stream
|
89
130
|
stream = add_stream method: method, path: path, stream: s, &block
|
@@ -95,6 +136,10 @@ module H2
|
|
95
136
|
stream
|
96
137
|
end
|
97
138
|
|
139
|
+
# mutates the given hash into +String+ keys and values
|
140
|
+
#
|
141
|
+
# @param [Hash] hash the headers +Hash+ to stringify
|
142
|
+
#
|
98
143
|
def stringify_headers hash
|
99
144
|
hash.keys.each do |key|
|
100
145
|
hash[key] = hash[key].to_s unless String === hash[key]
|
@@ -103,9 +148,14 @@ module H2
|
|
103
148
|
hash
|
104
149
|
end
|
105
150
|
|
151
|
+
# builds headers +Hash+ with appropriate ordering
|
152
|
+
#
|
153
|
+
# @see https://http2.github.io/http2-spec/#rfc.section.8.1.2.1
|
154
|
+
# @see https://github.com/igrigorik/http-2/pull/136
|
155
|
+
#
|
106
156
|
def build_headers method:, path:, headers:
|
107
157
|
h = {
|
108
|
-
AUTHORITY_KEY => [@
|
158
|
+
AUTHORITY_KEY => [@host, @port.to_s].join(':'),
|
109
159
|
METHOD_KEY => method.to_s.upcase,
|
110
160
|
PATH_KEY => path,
|
111
161
|
SCHEME_KEY => @scheme
|
@@ -113,6 +163,9 @@ module H2
|
|
113
163
|
h.merge! stringify_headers(headers)
|
114
164
|
end
|
115
165
|
|
166
|
+
# creates a new stream and adds it to the +@streams+ +Hash+ keyed at both
|
167
|
+
# the method +Symbol+ and request path as well as the ID of the stream.
|
168
|
+
#
|
116
169
|
def add_stream method:, path:, stream:, &block
|
117
170
|
@streams[method] ||= {}
|
118
171
|
@streams[method][path] ||= []
|
@@ -122,6 +175,8 @@ module H2
|
|
122
175
|
stream
|
123
176
|
end
|
124
177
|
|
178
|
+
# add query string parameters the given request path +String+
|
179
|
+
#
|
125
180
|
def add_params params, path
|
126
181
|
appendage = path.index('?') ? '&' : '?'
|
127
182
|
path << appendage
|
@@ -130,10 +185,20 @@ module H2
|
|
130
185
|
|
131
186
|
# ---
|
132
187
|
|
188
|
+
# maintain a ivar for the +Array+ to send to +IO.select+
|
189
|
+
#
|
133
190
|
def selector
|
134
191
|
@selector ||= [@socket]
|
135
192
|
end
|
136
193
|
|
194
|
+
# creates a new +Thread+ to read the given number of bytes each loop from
|
195
|
+
# the current +@socket+
|
196
|
+
#
|
197
|
+
# NOTE: this is the override point for celluloid actor pool or concurrent
|
198
|
+
# ruby threadpool support
|
199
|
+
#
|
200
|
+
# @param [Integer] maxlen maximum number of bytes to read
|
201
|
+
#
|
137
202
|
def read maxlen = DEFAULT_MAXLEN
|
138
203
|
main = Thread.current
|
139
204
|
@reader = Thread.new do
|
@@ -145,6 +210,11 @@ module H2
|
|
145
210
|
end
|
146
211
|
end
|
147
212
|
|
213
|
+
# underyling read loop implementation, handling returned +Symbol+ values
|
214
|
+
# and shovelling data into the client parser
|
215
|
+
#
|
216
|
+
# @param [Integer] maxlen maximum number of bytes to read
|
217
|
+
#
|
148
218
|
def _read maxlen = DEFAULT_MAXLEN
|
149
219
|
begin
|
150
220
|
data = nil
|
@@ -173,6 +243,10 @@ module H2
|
|
173
243
|
end
|
174
244
|
end
|
175
245
|
|
246
|
+
# fake exceptionless IO for reading on older ruby versions
|
247
|
+
#
|
248
|
+
# @param [Integer] maxlen maximum number of bytes to read
|
249
|
+
#
|
176
250
|
def read_from_socket maxlen
|
177
251
|
@socket.read_nonblock maxlen
|
178
252
|
rescue IO::WaitReadable
|
@@ -181,11 +255,18 @@ module H2
|
|
181
255
|
|
182
256
|
# ---
|
183
257
|
|
258
|
+
# close callback for parser: calls custom handler, then closes connection
|
259
|
+
#
|
184
260
|
def on_close
|
185
261
|
on :close
|
186
262
|
close
|
187
263
|
end
|
188
264
|
|
265
|
+
# frame callback for parser: writes bytes to the +@socket+, and slicing
|
266
|
+
# appropriately for given return values
|
267
|
+
#
|
268
|
+
# @param [String] bytes
|
269
|
+
#
|
189
270
|
def on_frame bytes
|
190
271
|
on :frame, bytes
|
191
272
|
|
@@ -207,17 +288,26 @@ module H2
|
|
207
288
|
@socket.flush
|
208
289
|
end
|
209
290
|
|
291
|
+
# fake exceptionless IO for writing on older ruby versions
|
292
|
+
#
|
293
|
+
# @param [String] bytes
|
294
|
+
#
|
210
295
|
def write_to_socket bytes
|
211
296
|
@socket.write_nonblock bytes
|
212
297
|
rescue IO::WaitWritable
|
213
298
|
:wait_writable
|
214
299
|
end
|
215
300
|
|
301
|
+
# goaway callback for parser: calls custom handler, then closes connection
|
302
|
+
#
|
216
303
|
def on_goaway *args
|
217
304
|
on :goaway, *args
|
218
305
|
close
|
219
306
|
end
|
220
307
|
|
308
|
+
# push promise callback for parser: creates new +Stream+ with appropriate
|
309
|
+
# parent, binds close event, calls custom handler
|
310
|
+
#
|
221
311
|
def on_promise promise
|
222
312
|
push_promise = Stream.new client: self,
|
223
313
|
parent: @streams[promise.parent.id],
|
@@ -235,10 +325,14 @@ module H2
|
|
235
325
|
|
236
326
|
# ---
|
237
327
|
|
328
|
+
# build, configure, and return TLS socket
|
329
|
+
#
|
330
|
+
# @param [TCPSocket] socket unencrypted socket
|
331
|
+
#
|
238
332
|
def tls_socket socket
|
239
333
|
socket = OpenSSL::SSL::SSLSocket.new socket, create_ssl_context
|
240
334
|
socket.sync_close = true
|
241
|
-
socket.hostname = @
|
335
|
+
socket.hostname = @host unless RE_IP_ADDR.match(@host)
|
242
336
|
socket.connect
|
243
337
|
socket
|
244
338
|
end
|
@@ -261,6 +355,8 @@ module H2
|
|
261
355
|
ctx
|
262
356
|
end
|
263
357
|
|
358
|
+
# handle protocol negotiation for older ruby/openssl versions
|
359
|
+
#
|
264
360
|
if H2.alpn?
|
265
361
|
def set_ssl_context_protocols ctx
|
266
362
|
ctx.alpn_protocols = ALPN_PROTOCOLS
|
@@ -273,6 +369,8 @@ module H2
|
|
273
369
|
|
274
370
|
# ---
|
275
371
|
|
372
|
+
# use exceptionless IO if this ruby version supports it
|
373
|
+
#
|
276
374
|
module ExceptionlessIO
|
277
375
|
|
278
376
|
def read_from_socket maxlen
|
data/lib/h2/server/stream.rb
CHANGED
@@ -40,32 +40,13 @@ module H2
|
|
40
40
|
bind_events
|
41
41
|
end
|
42
42
|
|
43
|
-
#
|
43
|
+
# write status, headers, and body to +@stream+
|
44
44
|
#
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
#
|
51
|
-
if Hash === body_or_headers
|
52
|
-
headers = body_or_headers
|
53
|
-
body ||= ''
|
54
|
-
else
|
55
|
-
headers = {}
|
56
|
-
body = body_or_headers ||= ''
|
57
|
-
end
|
58
|
-
|
59
|
-
@response = case response
|
60
|
-
when Symbol, Integer
|
61
|
-
response = Response.new stream: self,
|
62
|
-
status: response,
|
63
|
-
headers: headers,
|
64
|
-
body: body
|
65
|
-
when Response
|
66
|
-
response
|
67
|
-
else raise TypeError, "invalid response: #{response.inspect}"
|
68
|
-
end
|
45
|
+
def respond status:, headers: {}, body: ''
|
46
|
+
response = Response.new stream: self,
|
47
|
+
status: status,
|
48
|
+
headers: headers,
|
49
|
+
body: body
|
69
50
|
|
70
51
|
if @closed
|
71
52
|
log :warn, 'stream closed before response sent'
|
@@ -85,20 +66,9 @@ module H2
|
|
85
66
|
@connection.server.async.handle_push_promise pp
|
86
67
|
end
|
87
68
|
|
88
|
-
# create a push promise
|
69
|
+
# create a push promise
|
89
70
|
#
|
90
|
-
def push_promise_for path
|
91
|
-
|
92
|
-
# :/
|
93
|
-
#
|
94
|
-
case body_or_headers
|
95
|
-
when Hash
|
96
|
-
headers = body_or_headers
|
97
|
-
else
|
98
|
-
headers = {}
|
99
|
-
body = body_or_headers
|
100
|
-
end
|
101
|
-
|
71
|
+
def push_promise_for path:, headers: {}, body: nil
|
102
72
|
headers.merge! AUTHORITY_KEY => @request.authority,
|
103
73
|
SCHEME_KEY => @request.scheme
|
104
74
|
|
@@ -55,8 +55,8 @@ module H2
|
|
55
55
|
|
56
56
|
# respond to this request on its stream
|
57
57
|
#
|
58
|
-
def respond
|
59
|
-
@stream.respond
|
58
|
+
def respond status:, headers: {}, body: ''
|
59
|
+
@stream.respond status: status, headers: headers, body: body
|
60
60
|
end
|
61
61
|
|
62
62
|
end
|
data/lib/h2/stream.rb
CHANGED
@@ -13,6 +13,15 @@ module H2
|
|
13
13
|
|
14
14
|
attr_reader :body, :client, :headers, :parent, :pushes, :stream
|
15
15
|
|
16
|
+
# create a new h2 stream
|
17
|
+
#
|
18
|
+
# @param [H2::Client] client the +Client+ bind this +Stream+ to
|
19
|
+
# @param [HTTP2::Stream] stream protocol library +HTTP2::Stream+ instance
|
20
|
+
# @param [Boolean] push true if a push promise stream, false otherwise
|
21
|
+
# @param [H2::Stream] parent the parent stream of this, if push promise stream
|
22
|
+
#
|
23
|
+
# @return [H2::Stream]
|
24
|
+
#
|
16
25
|
def initialize client:, stream:, push: false, parent: nil
|
17
26
|
@body = ''
|
18
27
|
@client = client
|
@@ -28,46 +37,66 @@ module H2
|
|
28
37
|
bind_events
|
29
38
|
end
|
30
39
|
|
40
|
+
# @return [Integer] stream ID
|
41
|
+
#
|
31
42
|
def id
|
32
43
|
@stream.id
|
33
44
|
end
|
34
45
|
|
46
|
+
# @return [Boolean] true if response status is 200
|
47
|
+
#
|
35
48
|
def ok?
|
36
49
|
headers[STATUS_KEY] == '200'
|
37
50
|
end
|
38
51
|
|
52
|
+
# @return [Boolean] true if this +Stream+ is closed
|
53
|
+
#
|
39
54
|
def closed?
|
40
55
|
@closed
|
41
56
|
end
|
42
57
|
|
58
|
+
# @return [Boolean] true if this +Stream+ is a push promise
|
59
|
+
#
|
43
60
|
def push?
|
44
61
|
@push
|
45
62
|
end
|
46
63
|
|
64
|
+
# add a push promise +Stream+ to this +Stream+'s list of "child" pushes
|
65
|
+
#
|
47
66
|
def add_push stream
|
48
67
|
@pushes << stream
|
49
68
|
end
|
50
69
|
|
70
|
+
# call cancel and unblock this +Stream+
|
71
|
+
#
|
51
72
|
def cancel!
|
52
73
|
@stream.cancel
|
53
74
|
unblock!
|
54
75
|
end
|
55
76
|
|
77
|
+
# block this stream until unblocked or timeout
|
78
|
+
#
|
56
79
|
def block! timeout = nil
|
57
80
|
@pushes.each {|p| p.block! timeout}
|
58
81
|
super
|
59
82
|
end
|
60
83
|
|
84
|
+
# @return [Hash] response headers (blocks)
|
85
|
+
#
|
61
86
|
def headers
|
62
87
|
block!
|
63
88
|
@headers
|
64
89
|
end
|
65
90
|
|
91
|
+
# @return [String] response headers (blocks)
|
92
|
+
#
|
66
93
|
def body
|
67
94
|
block!
|
68
95
|
@body
|
69
96
|
end
|
70
97
|
|
98
|
+
# binds all stream events to their respective on_ handlers
|
99
|
+
#
|
71
100
|
def bind_events
|
72
101
|
@stream.on(:close) do
|
73
102
|
@parent.add_push self if @parent && push?
|
@@ -87,6 +116,8 @@ module H2
|
|
87
116
|
end
|
88
117
|
end
|
89
118
|
|
119
|
+
# builds +Hash+ from associative array, merges into response headers
|
120
|
+
#
|
90
121
|
def add_headers h
|
91
122
|
h = Hash[h]
|
92
123
|
on :headers, h
|
@@ -94,6 +125,8 @@ module H2
|
|
94
125
|
@headers
|
95
126
|
end
|
96
127
|
|
128
|
+
# @return [Hash] a simple +Hash+ with +:headers+ and +:body+ keys/values
|
129
|
+
#
|
97
130
|
def to_h
|
98
131
|
{ headers: headers, body: body }
|
99
132
|
end
|
data/lib/h2/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: h2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kenichi Nakamura
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-07-
|
11
|
+
date: 2018-07-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: http-2
|
@@ -16,20 +16,20 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '0.
|
19
|
+
version: '0.10'
|
20
20
|
- - ">="
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: 0.
|
22
|
+
version: 0.10.0
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
26
26
|
requirements:
|
27
27
|
- - "~>"
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version: '0.
|
29
|
+
version: '0.10'
|
30
30
|
- - ">="
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: 0.
|
32
|
+
version: 0.10.0
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
34
|
name: colored
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|