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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 83a5e5bd7e766404f4011a11d61d6cfcc75d1cff40c1822895f21f71ccf91925
4
- data.tar.gz: ecdde5ee1128a0ee9f947a1da217c683fda1c65c281dba0694ff72749a31ca9f
3
+ metadata.gz: 4de4d97aa888d6458d22577150d9d7bb24d90fbfbe29c5c81d106874d0451098
4
+ data.tar.gz: bdf58708f82a9135b1d5a3ead916b3eb4a2766d79852304259de317e01896bfd
5
5
  SHA512:
6
- metadata.gz: ce5c4265e43776b208ef51ebcb5b54b84f0b86e7d8d52dbccf3905869fc6094ec88d74b6afb35eba557ffcae0699dae5f130e7b72f507ac989c28e2904e55fbb
7
- data.tar.gz: 74b16c8ad259f0aaded61ef8094d26043354b717d299c3de04b2a4aab0169b74ebc2808f889ff284468a5ffabd4ad1a2f0e988dd67b268d3a8660303df9b7fe3
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,5 @@
1
+
2
+ source 'https://rubygems.org'
3
+
4
+ gem "trenni"
5
+ gem "async-http"
@@ -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
@@ -32,6 +32,8 @@ module Async
32
32
  return body
33
33
  elsif body.is_a? Array
34
34
  return self.new(body)
35
+ elsif body.is_a?(String)
36
+ return self.new([body])
35
37
  elsif body
36
38
  return self.for(body)
37
39
  end
@@ -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.new("URL must be absolute (include scheme, host): #{url}") unless url.absolute?
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
- reuse_port: self.reuse_port,
148
- timeout: self.timeout,
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(*args, &block)
176
- endpoint.connect(*args, &block)
185
+ def connect(&block)
186
+ endpoint.connect(&block)
177
187
  end
178
188
 
179
189
  def each
@@ -20,6 +20,6 @@
20
20
 
21
21
  module Async
22
22
  module HTTP
23
- VERSION = "0.38.1"
23
+ VERSION = "0.38.2"
24
24
  end
25
25
  end
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.1
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-02-20 00:00:00.000000000 Z
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.2
224
+ rubygems_version: 3.0.3
223
225
  signing_key:
224
226
  specification_version: 4
225
227
  summary: A HTTP client and server library.