net-http2 0.8.0 → 0.9.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/README.md +100 -23
- data/lib/net-http2/client.rb +23 -16
- data/lib/net-http2/request.rb +14 -1
- data/lib/net-http2/stream.rb +61 -25
- data/lib/net-http2/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 06fbac929ced0fe89503612541dd469842084882
|
4
|
+
data.tar.gz: b1a1119b920b3e2aaa2445b4f3810a9438ee32b2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7a49a1d2f16b33d0260464584880d74e55076d4b7c1f59545f9fabb6af2861c8c357596f1fd16ab30b4212450be0ea242f2bcf2ea76c6a966d8d9f38762f73f4
|
7
|
+
data.tar.gz: 1d7492113575170952373c2326c13bab44018f1ae941f71a1ff56eeb6e4426ef9721499c6b88a12febda67fb73c1b93d65eb76ec7bada382b0650fa7b2aa49d4
|
data/README.md
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
[](https://travis-ci.org/ostinelli/net-http2)
|
2
|
+
[](https://codeclimate.com/github/ostinelli/net-http2)
|
2
3
|
|
3
4
|
# NetHttp2
|
4
5
|
|
5
6
|
NetHttp2 is an HTTP/2 client for Ruby.
|
6
7
|
|
7
|
-
|
8
8
|
## Installation
|
9
9
|
Just install the gem:
|
10
10
|
|
@@ -19,7 +19,9 @@ gem 'net-http2'
|
|
19
19
|
```
|
20
20
|
|
21
21
|
## Usage
|
22
|
+
NetHttp2 can perform sync and async calls. Sync calls are very similar to the HTTP/1 calls, while async calls take advantage of the streaming properties of HTTP/2.
|
22
23
|
|
24
|
+
To perform a sync call:
|
23
25
|
```ruby
|
24
26
|
require 'net-http2'
|
25
27
|
|
@@ -27,7 +29,7 @@ require 'net-http2'
|
|
27
29
|
client = NetHttp2::Client.new("http://106.186.112.116")
|
28
30
|
|
29
31
|
# send request
|
30
|
-
response = client.get
|
32
|
+
response = client.call(:get, '/')
|
31
33
|
|
32
34
|
# read the response
|
33
35
|
response.ok? # => true
|
@@ -39,16 +41,34 @@ response.body # => "A body"
|
|
39
41
|
client.close
|
40
42
|
```
|
41
43
|
|
44
|
+
To perform an async call:
|
45
|
+
```ruby
|
46
|
+
require 'net-http2'
|
42
47
|
|
43
|
-
|
48
|
+
# create a client
|
49
|
+
client = NetHttp2::Client.new("http://106.186.112.116")
|
44
50
|
|
45
|
-
|
46
|
-
|
51
|
+
# prepare request
|
52
|
+
request = client.prepare_request(:get, '/')
|
53
|
+
request.on(:headers) { |headers| p headers }
|
54
|
+
request.on(:body_chunk) { |chunk| p chunk }
|
55
|
+
request.on(:close) { puts "request completed!" }
|
47
56
|
|
48
|
-
|
49
|
-
|
57
|
+
# send
|
58
|
+
client.call_async(request)
|
59
|
+
|
60
|
+
# We need to wait for the callbacks to be triggered, so here's a quick and dirty fix for this example.
|
61
|
+
# In real life, if you are using async requests so you probably have a running loop that keeps your program alive.
|
62
|
+
sleep 5
|
63
|
+
|
64
|
+
# close the connection
|
65
|
+
client.close
|
50
66
|
```
|
51
67
|
|
68
|
+
## Objects
|
69
|
+
|
70
|
+
### `NetHttp2::Client`
|
71
|
+
|
52
72
|
#### Methods
|
53
73
|
|
54
74
|
* **new(url, options={})** → **`NetHttp2::Client`**
|
@@ -56,16 +76,20 @@ NetHttp2::Client.new(url)
|
|
56
76
|
Returns a new client. `url` is a `string` such as https://localhost:443.
|
57
77
|
The only current option is `:ssl_context`, in case the url has an https scheme and you want your SSL client to use a custom context.
|
58
78
|
|
59
|
-
|
79
|
+
To create a new client:
|
80
|
+
```ruby
|
81
|
+
NetHttp2::Client.new("http://106.186.112.116")
|
82
|
+
```
|
60
83
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
84
|
+
To create a new client with a custom SSL context:
|
85
|
+
```ruby
|
86
|
+
certificate = File.read("cert.pem")
|
87
|
+
ctx = OpenSSL::SSL::SSLContext.new
|
88
|
+
ctx.key = OpenSSL::PKey::RSA.new(certificate, "cert_password")
|
89
|
+
ctx.cert = OpenSSL::X509::Certificate.new(certificate)
|
66
90
|
|
67
|
-
|
68
|
-
|
91
|
+
NetHttp2::Client.new("http://106.186.112.116", ssl_context: ctx)
|
92
|
+
```
|
69
93
|
|
70
94
|
* **uri** → **`URI`**
|
71
95
|
|
@@ -80,18 +104,68 @@ These behave similarly to HTTP/1 calls.
|
|
80
104
|
|
81
105
|
`method` is a symbol that specifies the `:method` header (`:get`, `:post`, `:put`, `:patch`, `:delete`, `:options`). The body and the headers of the request can be specified in the options, together with the timeout.
|
82
106
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
response_3 = client.call(:post '/path3', body: "the request body", timeout: 1)
|
89
|
-
```
|
107
|
+
```ruby
|
108
|
+
response_1 = client.call(:get, '/path1')
|
109
|
+
response_2 = client.call(:get, '/path2', headers: { 'x-custom' => 'custom' })
|
110
|
+
response_3 = client.call(:post '/path3', body: "the request body", timeout: 1)
|
111
|
+
```
|
90
112
|
|
91
113
|
|
92
114
|
##### Non-blocking calls
|
115
|
+
The real benefit of HTTP/2 is being able to receive body and header streams. Instead of buffering the whole response, you might want to react immediately upon receiving those streams. This is what non-blocking calls are for.
|
116
|
+
|
117
|
+
* **prepare_request(method, path, options={})** → **`NetHttp2::Request`**
|
118
|
+
|
119
|
+
Prepares an async request. Arguments are the same as the `call` method, with the difference that the `:timeout` option will be ignored. In an async call, you will need to write your own logic for timeouts.
|
93
120
|
|
94
|
-
|
121
|
+
```ruby
|
122
|
+
request = client.prepare_request(:get, '/path', headers: { 'x-custom-header' => 'custom' })
|
123
|
+
```
|
124
|
+
|
125
|
+
* **on(event, &block)**
|
126
|
+
|
127
|
+
Allows to set a callback for the request. Available events are:
|
128
|
+
|
129
|
+
* `:headers`: triggered when headers are received (called once).
|
130
|
+
* `:body_chunk`: triggered when body chunks are received (may be called multiple times).
|
131
|
+
* `:close`: triggered when the request has been completed (called once).
|
132
|
+
|
133
|
+
Even if NetHttp2 is thread-safe, the async callbacks will be executed in a different thread, so ensure that your code in the callbacks is thread-safe.
|
134
|
+
|
135
|
+
```ruby
|
136
|
+
request.on(:headers) { |headers| p headers }
|
137
|
+
request.on(:body_chunk) { |chunk| p chunk }
|
138
|
+
request.on(:close) { puts "request completed!" }
|
139
|
+
```
|
140
|
+
|
141
|
+
* **call_async(request)**
|
142
|
+
|
143
|
+
Calls the server with the async request.
|
144
|
+
|
145
|
+
|
146
|
+
### `NetHttp2::Request`
|
147
|
+
|
148
|
+
#### Methods
|
149
|
+
|
150
|
+
* **method** → **`symbol`**
|
151
|
+
|
152
|
+
The request's method.
|
153
|
+
|
154
|
+
* **uri** → **`URI`**
|
155
|
+
|
156
|
+
The request's URI.
|
157
|
+
|
158
|
+
* **path** → **`string`**
|
159
|
+
|
160
|
+
The request's path.
|
161
|
+
|
162
|
+
* **body** → **`string`**
|
163
|
+
|
164
|
+
The request's body.
|
165
|
+
|
166
|
+
* **timeout** → **`integer`**
|
167
|
+
|
168
|
+
The request's timeout.
|
95
169
|
|
96
170
|
|
97
171
|
### `NetHttp2::Response`
|
@@ -115,6 +189,9 @@ These behave similarly to HTTP/1 calls.
|
|
115
189
|
Returns the RAW body of the response.
|
116
190
|
|
117
191
|
|
192
|
+
## Thread-Safety
|
193
|
+
NetHttp2 is thread-safe.
|
194
|
+
|
118
195
|
## Contributing
|
119
196
|
So you want to contribute? That's great! Please follow the guidelines below. It will make it easier to get merged in.
|
120
197
|
|
data/lib/net-http2/client.rb
CHANGED
@@ -22,11 +22,20 @@ module NetHttp2
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def call(method, path, options={})
|
25
|
-
request =
|
25
|
+
request = prepare_request(method, path, options)
|
26
26
|
ensure_open
|
27
27
|
new_stream.call_with request
|
28
28
|
end
|
29
29
|
|
30
|
+
def call_async(request)
|
31
|
+
ensure_open
|
32
|
+
new_stream.async_call_with request
|
33
|
+
end
|
34
|
+
|
35
|
+
def prepare_request(method, path, options={})
|
36
|
+
NetHttp2::Request.new(method, @uri, path, options)
|
37
|
+
end
|
38
|
+
|
30
39
|
def ssl?
|
31
40
|
@is_ssl
|
32
41
|
end
|
@@ -42,27 +51,25 @@ module NetHttp2
|
|
42
51
|
|
43
52
|
private
|
44
53
|
|
45
|
-
def async_call_with(request, &block)
|
46
|
-
ensure_open
|
47
|
-
new_stream.async_call_with request, &block
|
48
|
-
end
|
49
|
-
|
50
54
|
def new_stream
|
51
55
|
NetHttp2::Stream.new(uri: @uri, h2_stream: h2.new_stream)
|
52
56
|
end
|
53
57
|
|
54
58
|
def ensure_open
|
55
|
-
|
59
|
+
@mutex.synchronize do
|
56
60
|
|
57
|
-
|
61
|
+
return if @socket_thread
|
58
62
|
|
59
|
-
|
63
|
+
socket = new_socket
|
60
64
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
65
|
+
@socket_thread = Thread.new do
|
66
|
+
|
67
|
+
begin
|
68
|
+
thread_loop(socket)
|
69
|
+
ensure
|
70
|
+
socket.close unless socket.closed?
|
71
|
+
@socket_thread = nil
|
72
|
+
end
|
66
73
|
end
|
67
74
|
end
|
68
75
|
end
|
@@ -83,7 +90,7 @@ module NetHttp2
|
|
83
90
|
end
|
84
91
|
|
85
92
|
if ready[0].include?(@pipe_r)
|
86
|
-
data_to_send = @pipe_r.
|
93
|
+
data_to_send = @pipe_r.readpartial(1024)
|
87
94
|
socket.write(data_to_send)
|
88
95
|
end
|
89
96
|
end
|
@@ -146,7 +153,7 @@ module NetHttp2
|
|
146
153
|
end
|
147
154
|
|
148
155
|
def exit_thread(thread)
|
149
|
-
return unless thread
|
156
|
+
return unless thread
|
150
157
|
thread.exit
|
151
158
|
thread.join
|
152
159
|
end
|
data/lib/net-http2/request.rb
CHANGED
@@ -13,6 +13,8 @@ module NetHttp2
|
|
13
13
|
@body = options[:body]
|
14
14
|
@headers = options[:headers] || {}
|
15
15
|
@timeout = options[:timeout] || DEFAULT_TIMEOUT
|
16
|
+
|
17
|
+
@events = {}
|
16
18
|
end
|
17
19
|
|
18
20
|
def headers
|
@@ -30,8 +32,19 @@ module NetHttp2
|
|
30
32
|
@headers.delete('content-length')
|
31
33
|
end
|
32
34
|
|
33
|
-
|
34
35
|
@headers
|
35
36
|
end
|
37
|
+
|
38
|
+
def on(event, &block)
|
39
|
+
raise ArgumentError, 'on event must provide a block' unless block_given?
|
40
|
+
|
41
|
+
@events[event] ||= []
|
42
|
+
@events[event] << block
|
43
|
+
end
|
44
|
+
|
45
|
+
def emit(event, arg)
|
46
|
+
return unless @events[event]
|
47
|
+
@events[event].each { |b| b.call(arg) }
|
48
|
+
end
|
36
49
|
end
|
37
50
|
end
|
data/lib/net-http2/stream.rb
CHANGED
@@ -4,30 +4,72 @@ module NetHttp2
|
|
4
4
|
|
5
5
|
def initialize(options={})
|
6
6
|
@h2_stream = options[:h2_stream]
|
7
|
-
@uri = options[:uri]
|
8
7
|
@headers = {}
|
9
8
|
@data = ''
|
9
|
+
@request = nil
|
10
10
|
@completed = false
|
11
|
-
@
|
11
|
+
@async = false
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
@h2_stream.on(:data) { |d| @data << d }
|
18
|
-
@h2_stream.on(:close) { @completed = true }
|
13
|
+
listen_for_headers
|
14
|
+
listen_for_data
|
15
|
+
listen_for_close
|
19
16
|
end
|
20
17
|
|
21
18
|
def call_with(request)
|
22
|
-
|
23
|
-
|
19
|
+
@request = request
|
20
|
+
send_request_data
|
21
|
+
sync_respond
|
22
|
+
end
|
23
|
+
|
24
|
+
def async_call_with(request)
|
25
|
+
@request = request
|
26
|
+
@async = true
|
27
|
+
send_request_data
|
28
|
+
end
|
29
|
+
|
30
|
+
def completed?
|
31
|
+
@completed
|
32
|
+
end
|
33
|
+
|
34
|
+
def async?
|
35
|
+
@async
|
24
36
|
end
|
25
37
|
|
26
38
|
private
|
27
39
|
|
28
|
-
def
|
29
|
-
headers
|
30
|
-
|
40
|
+
def listen_for_headers
|
41
|
+
@h2_stream.on(:headers) do |hs_array|
|
42
|
+
hs = Hash[*hs_array.flatten]
|
43
|
+
|
44
|
+
if async?
|
45
|
+
@request.emit(:headers, hs)
|
46
|
+
else
|
47
|
+
@headers.merge!(hs)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def listen_for_data
|
53
|
+
@h2_stream.on(:data) do |data|
|
54
|
+
if async?
|
55
|
+
@request.emit(:body_chunk, data)
|
56
|
+
else
|
57
|
+
@data << data
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def listen_for_close
|
63
|
+
@h2_stream.on(:close) do |data|
|
64
|
+
@completed = true
|
65
|
+
|
66
|
+
@request.emit(:close, data) if async?
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def send_request_data
|
71
|
+
headers = @request.headers
|
72
|
+
body = @request.body
|
31
73
|
|
32
74
|
if body
|
33
75
|
@h2_stream.headers(headers, end_stream: false)
|
@@ -37,24 +79,18 @@ module NetHttp2
|
|
37
79
|
end
|
38
80
|
end
|
39
81
|
|
40
|
-
def sync_respond
|
41
|
-
|
42
|
-
|
82
|
+
def sync_respond
|
83
|
+
wait_for_completed
|
84
|
+
|
85
|
+
NetHttp2::Response.new(headers: @headers, body: @data) if @completed
|
43
86
|
end
|
44
87
|
|
45
|
-
def
|
46
|
-
cutoff_time = Time.now + timeout
|
88
|
+
def wait_for_completed
|
89
|
+
cutoff_time = Time.now + @request.timeout
|
47
90
|
|
48
91
|
while !@completed && Time.now < cutoff_time
|
49
92
|
sleep 0.1
|
50
93
|
end
|
51
94
|
end
|
52
|
-
|
53
|
-
def response
|
54
|
-
NetHttp2::Response.new(
|
55
|
-
headers: @headers,
|
56
|
-
body: @data
|
57
|
-
)
|
58
|
-
end
|
59
95
|
end
|
60
96
|
end
|
data/lib/net-http2/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: net-http2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Roberto Ostinelli
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-04-
|
11
|
+
date: 2016-04-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: http-2
|