async-http 0.38.1 → 0.38.2
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|