async-http 0.38.1 → 0.38.2
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 +44 -0
- data/examples/trenni/Gemfile +5 -0
- data/examples/trenni/streaming.rb +35 -0
- data/lib/async/http/body/buffered.rb +2 -0
- data/lib/async/http/client.rb +5 -1
- data/lib/async/http/url_endpoint.rb +17 -7
- data/lib/async/http/version.rb +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4de4d97aa888d6458d22577150d9d7bb24d90fbfbe29c5c81d106874d0451098
|
4
|
+
data.tar.gz: bdf58708f82a9135b1d5a3ead916b3eb4a2766d79852304259de317e01896bfd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ca58c3bea25933b04cf306130ff31f3e0770fa0964b2411942fac897eb70862a30668bebaee6eaf22a92e571c178e47be27b39b510679889a3f36b0124054bf0
|
7
|
+
data.tar.gz: a1182f2f385ba1bf529511085ae8523faae36267a54586101bcbdf0b225d475285661830d8cfc323ecf310094979e30a6115ee6cab94bcaa87025b72644eaceb
|
data/README.md
CHANGED
@@ -123,6 +123,50 @@ Async::Reactor.run do |task|
|
|
123
123
|
end
|
124
124
|
```
|
125
125
|
|
126
|
+
### Advanced Verification
|
127
|
+
|
128
|
+
You can hook into SSL certificate verification to improve server verification.
|
129
|
+
|
130
|
+
```ruby
|
131
|
+
require 'async'
|
132
|
+
require 'async/http'
|
133
|
+
|
134
|
+
# These are generated from the certificate chain that the server presented.
|
135
|
+
trusted_fingerprints = {
|
136
|
+
"dac9024f54d8f6df94935fb1732638ca6ad77c13" => true,
|
137
|
+
"e6a3b45b062d509b3382282d196efe97d5956ccb" => true,
|
138
|
+
"07d63f4c05a03f1c306f9941b8ebf57598719ea2" => true,
|
139
|
+
"e8d994f44ff20dc78dbff4e59d7da93900572bbf" => true,
|
140
|
+
}
|
141
|
+
|
142
|
+
Async do
|
143
|
+
endpoint = Async::HTTP::URLEndpoint.parse("https://www.codeotaku.com/index")
|
144
|
+
|
145
|
+
# This is a quick hack/POC:
|
146
|
+
ssl_context = endpoint.ssl_context
|
147
|
+
|
148
|
+
ssl_context.verify_callback = proc do |verified, store_context|
|
149
|
+
certificate = store_context.current_cert
|
150
|
+
fingerprint = OpenSSL::Digest::SHA1.new(certificate.to_der).to_s
|
151
|
+
|
152
|
+
if trusted_fingerprints.include? fingerprint
|
153
|
+
true
|
154
|
+
else
|
155
|
+
Async.logger.warn("Untrusted Certificate Fingerprint"){fingerprint}
|
156
|
+
false
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
endpoint = endpoint.with(ssl_context: ssl_context)
|
161
|
+
|
162
|
+
client = Async::HTTP::Client.new(endpoint)
|
163
|
+
|
164
|
+
response = client.get(endpoint.path)
|
165
|
+
|
166
|
+
pp response.status, response.headers.fields, response.read
|
167
|
+
end
|
168
|
+
```
|
169
|
+
|
126
170
|
## Performance
|
127
171
|
|
128
172
|
On a 4-core 8-thread i7, running `ab` which uses discrete (non-keep-alive) connections:
|
@@ -0,0 +1,35 @@
|
|
1
|
+
|
2
|
+
require 'trenni/template'
|
3
|
+
|
4
|
+
require 'async'
|
5
|
+
require 'async/http/body/writable'
|
6
|
+
|
7
|
+
# The template, using inline text. The sleep could be anything - database query, HTTP request, redis, etc.
|
8
|
+
buffer = Trenni::Buffer.new(<<-EOF)
|
9
|
+
The "\#{self[:count]} bottles of \#{self[:drink]} on the wall" song!
|
10
|
+
|
11
|
+
<?r self[:count].downto(1) do |index| ?>
|
12
|
+
\#{index} bottles of \#{self[:drink]} on the wall,
|
13
|
+
\#{index} bottles of \#{self[:drink]},
|
14
|
+
take one down, and pass it around,
|
15
|
+
\#{index - 1} bottles of \#{self[:drink]} on the wall.
|
16
|
+
|
17
|
+
<?r Async::Task.current.sleep(1) ?>
|
18
|
+
<?r end ?>
|
19
|
+
EOF
|
20
|
+
|
21
|
+
template = Trenni::Template.new(buffer)
|
22
|
+
|
23
|
+
Async do
|
24
|
+
body = Async::HTTP::Body::Writable.new
|
25
|
+
|
26
|
+
generator = Async do
|
27
|
+
template.to_string({count: 100, drink: 'coffee'}, body)
|
28
|
+
end
|
29
|
+
|
30
|
+
while chunk = body.read
|
31
|
+
$stdout.write chunk
|
32
|
+
end
|
33
|
+
|
34
|
+
generator.wait
|
35
|
+
end.wait
|
data/lib/async/http/client.rb
CHANGED
@@ -28,6 +28,11 @@ require_relative 'middleware'
|
|
28
28
|
module Async
|
29
29
|
module HTTP
|
30
30
|
class Client
|
31
|
+
# Provides a robust interface to a server.
|
32
|
+
# * If there are no connections, it will create one.
|
33
|
+
# * If there are already connections, it will reuse it.
|
34
|
+
# * If a request fails, it will retry it up to N times if it was idempotent.
|
35
|
+
# The client object will never become unusable. It internally manages persistent connections (or non-persistent connections if that's required).
|
31
36
|
def initialize(endpoint, protocol = endpoint.protocol, scheme = endpoint.scheme, authority = endpoint.authority, retries: 3, connection_limit: nil)
|
32
37
|
@endpoint = endpoint
|
33
38
|
@protocol = protocol
|
@@ -91,7 +96,6 @@ module Async
|
|
91
96
|
# This is a specific case where the entire request wasn't sent before a failure occurred. So, we can even resend non-idempotent requests.
|
92
97
|
@pool.release(connection) if connection
|
93
98
|
|
94
|
-
attempt += 1
|
95
99
|
if attempt < @retries
|
96
100
|
retry
|
97
101
|
else
|
@@ -34,10 +34,15 @@ module Async
|
|
34
34
|
self.new(url, **options)
|
35
35
|
end
|
36
36
|
|
37
|
+
# @option scheme [String] the scheme to use, overrides the URL scheme.
|
38
|
+
# @option port [Integer] the port to bind to, overrides the URL port.
|
39
|
+
# @option hostname [String] the hostname to use, overrides the URL hostname.
|
40
|
+
# @option ssl_context [OpenSSL::SSL::SSLContext] the context to use for TLS.
|
41
|
+
# @option alpn_protocols [Array<String>] the alpn protocols to negotiate.
|
37
42
|
def initialize(url, endpoint = nil, **options)
|
38
43
|
super(**options)
|
39
44
|
|
40
|
-
raise ArgumentError
|
45
|
+
raise ArgumentError, "URL must be absolute (include scheme, host): #{url}" unless url.absolute?
|
41
46
|
|
42
47
|
@url = url
|
43
48
|
@endpoint = endpoint
|
@@ -143,10 +148,15 @@ module Async
|
|
143
148
|
end
|
144
149
|
|
145
150
|
def tcp_options
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
151
|
+
options = @options.dup
|
152
|
+
|
153
|
+
options.delete(:scheme)
|
154
|
+
options.delete(:port)
|
155
|
+
options.delete(:hostname)
|
156
|
+
options.delete(:ssl_context)
|
157
|
+
options.delete(:alpn_protocols)
|
158
|
+
|
159
|
+
return options
|
150
160
|
end
|
151
161
|
|
152
162
|
def build_endpoint(endpoint = nil)
|
@@ -172,8 +182,8 @@ module Async
|
|
172
182
|
endpoint.bind(*args, &block)
|
173
183
|
end
|
174
184
|
|
175
|
-
def connect(
|
176
|
-
endpoint.connect(
|
185
|
+
def connect(&block)
|
186
|
+
endpoint.connect(&block)
|
177
187
|
end
|
178
188
|
|
179
189
|
def each
|
data/lib/async/http/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: async-http
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.38.
|
4
|
+
version: 0.38.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-04-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: async
|
@@ -144,6 +144,8 @@ files:
|
|
144
144
|
- examples/fetch/public/index.html
|
145
145
|
- examples/fetch/public/stream.js
|
146
146
|
- examples/request.rb
|
147
|
+
- examples/trenni/Gemfile
|
148
|
+
- examples/trenni/streaming.rb
|
147
149
|
- examples/upload/client.rb
|
148
150
|
- examples/upload/data.txt
|
149
151
|
- examples/upload/server.rb
|
@@ -219,7 +221,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
219
221
|
- !ruby/object:Gem::Version
|
220
222
|
version: '0'
|
221
223
|
requirements: []
|
222
|
-
rubygems_version: 3.0.
|
224
|
+
rubygems_version: 3.0.3
|
223
225
|
signing_key:
|
224
226
|
specification_version: 4
|
225
227
|
summary: A HTTP client and server library.
|