h2 0.1.0 → 0.2.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 +15 -0
- data/Gemfile +9 -1
- data/README.md +54 -7
- data/bin/console +18 -1
- data/exe/h2 +3 -1
- data/lib/h2/client/celluloid.rb +33 -0
- data/lib/h2/client/concurrent.rb +34 -0
- data/lib/h2/client.rb +8 -20
- data/lib/h2/stream.rb +1 -1
- data/lib/h2/version.rb +1 -1
- data/lib/h2.rb +6 -1
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7026103d2e280847900dbd974196250ef62d1ba0
|
4
|
+
data.tar.gz: df6cd50fb58732f2256aa90c85d36a6d8d9cb6c3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: af9a7b61585c3b9a0dc9f4fca518dd7516a57db3ac7299969c95b9790c7e3de965a83b63391fcba731079ef88db30dacabb872e1c05573b2077dc0db3a660c0a
|
7
|
+
data.tar.gz: 54ad799634b7cc747cdfa0cf2cf9b42324f67464bcfd941bb86bd7b118d4d44975aab518ab4d558be0d0f54d79a76a4e63e55a9e0b86b7b2e520d3e84310d866
|
data/CHANGELOG.md
ADDED
data/Gemfile
CHANGED
@@ -1,9 +1,17 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
|
-
gem 'http-2', git: 'https://github.com/
|
3
|
+
gem 'http-2', git: 'https://github.com/kenichi/http-2', branch: 'stream_close_state'
|
4
4
|
|
5
5
|
gemspec
|
6
6
|
|
7
|
+
group :concurrent_ruby do
|
8
|
+
gem 'concurrent-ruby'
|
9
|
+
end
|
10
|
+
|
11
|
+
group :celluloid do
|
12
|
+
gem 'celluloid'
|
13
|
+
end
|
14
|
+
|
7
15
|
group :development, :test do
|
8
16
|
gem 'awesome_print'
|
9
17
|
gem 'pry-byebug'
|
data/README.md
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
# H2
|
2
2
|
|
3
|
-
[](https://travis-ci.org/kenichi/h2)
|
4
4
|
|
5
|
-
H2 is a basic HTTP/2 client based on the [http-2](https://github.com/igrigorik/http-2) gem.
|
5
|
+
H2 is a basic, _experimental_ HTTP/2 client based on the [http-2](https://github.com/igrigorik/http-2) gem.
|
6
6
|
|
7
|
-
H2
|
7
|
+
H2 uses:
|
8
8
|
|
9
|
-
* one new thread per client (see [TODO](#TODO) item 3)
|
10
9
|
* keyword arguments (>=2.0)
|
11
10
|
* exception-less socket IO (>=2.3).
|
12
11
|
|
@@ -70,17 +69,65 @@ end
|
|
70
69
|
client.goaway!
|
71
70
|
```
|
72
71
|
|
72
|
+
## CLI
|
73
|
+
|
74
|
+
For more info on using the CLI `h2` installed with this gem:
|
75
|
+
|
76
|
+
`$ h2 --help`
|
77
|
+
|
78
|
+
## Alternate Concurrency Models
|
79
|
+
|
80
|
+
Right now, h2 uses one new thread per connection. This is hardly ideal, so a
|
81
|
+
couple other models are tentatively supported out of the box:
|
82
|
+
|
83
|
+
* [celluloid](https://github.com/celluloid/celluloid)
|
84
|
+
* [concurrent-ruby](https://github.com/ruby-concurrency/concurrent-ruby)
|
85
|
+
|
86
|
+
Neither of these gems are hard dependencies. If you want to use either one, you must
|
87
|
+
have it available to your Ruby VM, most likely via Bundler, *and* require the
|
88
|
+
sub-component of h2 that will prepend and extend `H2::Client`. They are also intended
|
89
|
+
to be mutually exclusive: you can have both in your VM, but you can only use one at a
|
90
|
+
time with h2.
|
91
|
+
|
92
|
+
#### Celluloid Pool
|
93
|
+
|
94
|
+
To use a celluloid actor pool for reading from `H2::Client` connections:
|
95
|
+
|
96
|
+
```ruby
|
97
|
+
require 'h2/client/celluloid'
|
98
|
+
```
|
99
|
+
|
100
|
+
This will lazily fire up a celluloid pool, with defaults defined by Celluloid.
|
101
|
+
|
102
|
+
#### Concurrent-Ruby ThreadPoolExecutor
|
103
|
+
|
104
|
+
To use a concurrent-ruby thread pool executor for reading from `H2::Client` connections:
|
105
|
+
|
106
|
+
```ruby
|
107
|
+
require 'h2/client/concurrent'
|
108
|
+
```
|
109
|
+
|
110
|
+
This will lazily fire up a `Concurrent::ThreadPoolExecutor` with the following settings:
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
procs = ::Concurrent.processor_count
|
114
|
+
|
115
|
+
min_threads: 0,
|
116
|
+
max_threads: procs,
|
117
|
+
max_queue: procs * 5
|
118
|
+
```
|
119
|
+
|
73
120
|
## TODO
|
74
121
|
|
75
122
|
* [x] HTTPS / TLS
|
76
|
-
* [
|
77
|
-
* [
|
123
|
+
* [x] push promise cancellation
|
124
|
+
* [x] alternate concurrency models
|
125
|
+
* [ ] fix up CLI to be more curlish
|
78
126
|
|
79
127
|
## Contributing
|
80
128
|
|
81
129
|
Bug reports and pull requests are welcome on GitHub at https://github.com/kenichi/h2. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
82
130
|
|
83
|
-
|
84
131
|
## License
|
85
132
|
|
86
133
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/bin/console
CHANGED
@@ -4,5 +4,22 @@ require 'bundler/setup'
|
|
4
4
|
require 'h2'
|
5
5
|
require 'irb'
|
6
6
|
|
7
|
-
Bundler.require :development
|
7
|
+
#Bundler.require :development
|
8
|
+
|
9
|
+
def trunc_payload f
|
10
|
+
x = f.dup
|
11
|
+
size = x[:payload]&.to_s&.bytesize || 0
|
12
|
+
x[:payload] = "#{size} redacted" if size > 64
|
13
|
+
x
|
14
|
+
end
|
15
|
+
|
16
|
+
def new_client
|
17
|
+
# H2::Client.new url: 'https://127.0.0.1:4430', tls: { ca_file: '/Users/ken/src/ruby/other_reel/tmp/certs/ca.crt' } do |client|
|
18
|
+
# H2::Client.new url: 'http://127.0.0.1:1234' do |client|
|
19
|
+
H2::Client.new url: 'https://vux.nakamura.io:4430', tls: { ca_file: '/usr/local/etc/cacert-201611290415.pem' } do |client|
|
20
|
+
client.client.on(:frame_sent){|f| STDERR.puts ">> #{trunc_payload(f).inspect}"}
|
21
|
+
client.client.on(:frame_received){|f| STDERR.puts "<< #{trunc_payload(f).inspect}"}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
8
25
|
IRB.start
|
data/exe/h2
CHANGED
@@ -22,6 +22,7 @@ end # }}}
|
|
22
22
|
options = {
|
23
23
|
body: nil,
|
24
24
|
block: false,
|
25
|
+
debug: false,
|
25
26
|
headers: {},
|
26
27
|
goaway: false,
|
27
28
|
method: nil,
|
@@ -52,7 +53,7 @@ OptionParser.new do |o|
|
|
52
53
|
end
|
53
54
|
|
54
55
|
o.on '-g', '--goaway', 'send GOAWAY frame when stream is complete' do
|
55
|
-
options[:
|
56
|
+
options[:goaway] = true
|
56
57
|
end
|
57
58
|
|
58
59
|
o.on '-v', '--verbose', 'turn on verbosity' do
|
@@ -65,6 +66,7 @@ OptionParser.new do |o|
|
|
65
66
|
options[:method] = meth
|
66
67
|
end
|
67
68
|
|
69
|
+
|
68
70
|
end.parse!
|
69
71
|
|
70
72
|
options[:method] ||= :get
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'celluloid/current'
|
2
|
+
require 'h2'
|
3
|
+
|
4
|
+
module H2
|
5
|
+
class Client
|
6
|
+
module Celluloid
|
7
|
+
|
8
|
+
class Reader
|
9
|
+
include ::Celluloid
|
10
|
+
|
11
|
+
def read client, maxlen = DEFAULT_MAXLEN
|
12
|
+
client._read maxlen
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module ClassMethods
|
17
|
+
def thread_pool
|
18
|
+
@thread_pool ||= Reader.pool
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def read maxlen = DEFAULT_MAXLEN
|
23
|
+
self.class.thread_pool.async.read self
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
extend H2::Client::Celluloid::ClassMethods
|
29
|
+
prepend H2::Client::Celluloid
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'concurrent'
|
2
|
+
require 'h2'
|
3
|
+
|
4
|
+
module H2
|
5
|
+
class Client
|
6
|
+
module Concurrent
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def thread_pool
|
10
|
+
procs = ::Concurrent.processor_count
|
11
|
+
@thread_pool ||= ::Concurrent::ThreadPoolExecutor.new min_threads: 0,
|
12
|
+
max_threads: procs,
|
13
|
+
max_queue: procs * 5
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def read maxlen = DEFAULT_MAXLEN
|
18
|
+
main = Thread.current
|
19
|
+
@reader = self.class.thread_pool.post do
|
20
|
+
begin
|
21
|
+
_read maxlen
|
22
|
+
rescue => e
|
23
|
+
main.raise e
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
extend H2::Client::Concurrent::ClassMethods
|
31
|
+
prepend H2::Client::Concurrent
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
data/lib/h2/client.rb
CHANGED
@@ -13,11 +13,6 @@ module H2
|
|
13
13
|
:promise
|
14
14
|
]
|
15
15
|
|
16
|
-
PROMISE_EVENTS = [
|
17
|
-
:headers,
|
18
|
-
:data
|
19
|
-
]
|
20
|
-
|
21
16
|
ALPN_PROTOCOLS = ['h2']
|
22
17
|
DEFAULT_MAXLEN = 4096
|
23
18
|
|
@@ -112,17 +107,16 @@ module H2
|
|
112
107
|
METHOD_KEY => method.to_s.upcase,
|
113
108
|
PATH_KEY => path,
|
114
109
|
SCHEME_KEY => @scheme
|
115
|
-
}
|
110
|
+
}.merge USER_AGENT
|
116
111
|
h.merge! stringify_headers(headers)
|
117
112
|
end
|
118
113
|
|
119
114
|
def add_stream method:, path:, stream:, &block
|
120
|
-
stream_id = stream.id
|
121
115
|
@streams[method] ||= {}
|
122
116
|
@streams[method][path] ||= []
|
123
117
|
stream = Stream.new client: self, stream: stream, &block unless Stream === stream
|
124
118
|
@streams[method][path] << stream
|
125
|
-
@streams[
|
119
|
+
@streams[stream.id] = stream
|
126
120
|
stream
|
127
121
|
end
|
128
122
|
|
@@ -143,12 +137,6 @@ module H2
|
|
143
137
|
main.raise e
|
144
138
|
end
|
145
139
|
end
|
146
|
-
rescue => e
|
147
|
-
STDERR.puts "#{e.message} - closing socket"
|
148
|
-
STDERR.puts e.backtrace.map {|l| "\t" + l}
|
149
|
-
close
|
150
|
-
ensure
|
151
|
-
unblock!
|
152
140
|
end
|
153
141
|
|
154
142
|
def _read maxlen = DEFAULT_MAXLEN
|
@@ -214,18 +202,18 @@ module H2
|
|
214
202
|
end
|
215
203
|
|
216
204
|
def on_promise promise
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
push: true,
|
222
|
-
stream: promise do |p|
|
205
|
+
push_promise = Stream.new client: self,
|
206
|
+
parent: @streams[promise.parent.id],
|
207
|
+
push: true,
|
208
|
+
stream: promise do |p|
|
223
209
|
p.on :close do
|
224
210
|
method = p.headers[METHOD_KEY].downcase.to_sym rescue :error
|
225
211
|
path = p.headers[PATH_KEY]
|
226
212
|
add_stream method: method, path: path, stream: p
|
227
213
|
end
|
228
214
|
end
|
215
|
+
|
216
|
+
on :promise, push_promise
|
229
217
|
end
|
230
218
|
|
231
219
|
# ---
|
data/lib/h2/stream.rb
CHANGED
data/lib/h2/version.rb
CHANGED
data/lib/h2.rb
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
require 'http/2'
|
4
4
|
require 'uri'
|
5
5
|
|
6
|
+
require 'h2/version'
|
7
|
+
|
6
8
|
module H2
|
7
9
|
|
8
10
|
# http/2 psuedo-headers
|
@@ -13,6 +15,10 @@ module H2
|
|
13
15
|
SCHEME_KEY = ':scheme'
|
14
16
|
STATUS_KEY = ':status'
|
15
17
|
|
18
|
+
USER_AGENT = {
|
19
|
+
'user-agent' => "h2/#{H2::VERSION} #{RUBY_ENGINE}-#{RUBY_VERSION}/#{RUBY_PLATFORM}"
|
20
|
+
}
|
21
|
+
|
16
22
|
REQUEST_METHODS = [
|
17
23
|
:get,
|
18
24
|
:delete,
|
@@ -92,4 +98,3 @@ end
|
|
92
98
|
|
93
99
|
require 'h2/client'
|
94
100
|
require 'h2/stream'
|
95
|
-
require 'h2/version'
|
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.2.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:
|
11
|
+
date: 2017-03-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: http-2
|
@@ -96,6 +96,7 @@ extra_rdoc_files: []
|
|
96
96
|
files:
|
97
97
|
- ".gitignore"
|
98
98
|
- ".travis.yml"
|
99
|
+
- CHANGELOG.md
|
99
100
|
- CODE_OF_CONDUCT.md
|
100
101
|
- Gemfile
|
101
102
|
- LICENSE.txt
|
@@ -106,6 +107,8 @@ files:
|
|
106
107
|
- h2.gemspec
|
107
108
|
- lib/h2.rb
|
108
109
|
- lib/h2/client.rb
|
110
|
+
- lib/h2/client/celluloid.rb
|
111
|
+
- lib/h2/client/concurrent.rb
|
109
112
|
- lib/h2/client/tcp_socket.rb
|
110
113
|
- lib/h2/stream.rb
|
111
114
|
- lib/h2/version.rb
|
@@ -129,7 +132,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
129
132
|
version: '0'
|
130
133
|
requirements: []
|
131
134
|
rubyforge_project:
|
132
|
-
rubygems_version: 2.6.
|
135
|
+
rubygems_version: 2.6.10
|
133
136
|
signing_key:
|
134
137
|
specification_version: 4
|
135
138
|
summary: an http/2 client based on http-2 and modern ruby
|