async-http 0.50.7 → 0.50.8
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/.travis.yml +2 -0
- data/README.md +2 -2
- data/async-http.gemspec +2 -1
- data/bake/async/http.rb +1 -1
- data/examples/download/chunked.rb +84 -0
- data/examples/stream/stop.rb +28 -0
- data/lib/async/http/client.rb +25 -8
- data/lib/async/http/version.rb +1 -1
- metadata +18 -17
- data/Rakefile +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6140b4806e8fff4a017e4d2973e2d6ecd28bc248069774d351b13fc5ea907ecf
|
4
|
+
data.tar.gz: 9eef40decca9b155053f7419993d705e280d0ea51e385dc030efebe6d20d4933
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dc926ac38b9d1bf6860ff23282f9c90eb78b965c881fae8130501e0cb4260c757bcf9bb8ee634674b2c3a37b013496ae08a332e54309e0f90979779197505d2c
|
7
|
+
data.tar.gz: b1fb1c1cbbea6b31bdeccaec48972bff12b58f86c8d6b1e5d42f36e389d3c7d5040839d8a74359cc835002e15631bc754a168d8ae643814bfe71e1ea0eadbc7d
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -160,9 +160,9 @@ Here is a basic example of a client/server running in the same reactor:
|
|
160
160
|
```ruby
|
161
161
|
#!/usr/bin/env ruby
|
162
162
|
|
163
|
+
require 'async'
|
163
164
|
require 'async/http/server'
|
164
165
|
require 'async/http/client'
|
165
|
-
require 'async/reactor'
|
166
166
|
require 'async/http/endpoint'
|
167
167
|
require 'async/http/protocol/response'
|
168
168
|
|
@@ -175,7 +175,7 @@ end
|
|
175
175
|
server = Async::HTTP::Server.new(app, endpoint)
|
176
176
|
client = Async::HTTP::Client.new(endpoint)
|
177
177
|
|
178
|
-
Async
|
178
|
+
Async do |task|
|
179
179
|
server_task = task.async do
|
180
180
|
server.run
|
181
181
|
end
|
data/async-http.gemspec
CHANGED
@@ -27,6 +27,8 @@ Gem::Specification.new do |spec|
|
|
27
27
|
|
28
28
|
# spec.add_dependency("openssl")
|
29
29
|
|
30
|
+
spec.add_development_dependency "bake-bundler"
|
31
|
+
|
30
32
|
spec.add_development_dependency "async-rspec", "~> 1.10"
|
31
33
|
spec.add_development_dependency "async-container", "~> 0.14.0"
|
32
34
|
|
@@ -36,5 +38,4 @@ Gem::Specification.new do |spec|
|
|
36
38
|
spec.add_development_dependency "covered"
|
37
39
|
spec.add_development_dependency "bundler"
|
38
40
|
spec.add_development_dependency "rspec", "~> 3.6"
|
39
|
-
spec.add_development_dependency "rake"
|
40
41
|
end
|
data/bake/async/http.rb
CHANGED
@@ -21,7 +21,7 @@ def fetch(url, method:)
|
|
21
21
|
format_body = proc do |body, terminal|
|
22
22
|
if body
|
23
23
|
if length = body.length
|
24
|
-
terminal.print(:body, "body with length", :length, length, "B")
|
24
|
+
terminal.print(:body, "body with length ", :length, length, "B")
|
25
25
|
else
|
26
26
|
terminal.print(:body, "body without length")
|
27
27
|
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'async'
|
4
|
+
require 'async/clock'
|
5
|
+
require 'async/barrier'
|
6
|
+
require_relative '../../lib/async/http/endpoint'
|
7
|
+
require_relative '../../lib/async/http/client'
|
8
|
+
|
9
|
+
Async do
|
10
|
+
url = "https://static.openfoodfacts.org/data/en.openfoodfacts.org.products.csv"
|
11
|
+
|
12
|
+
endpoint = Async::HTTP::Endpoint.parse(url)
|
13
|
+
client = Async::HTTP::Client.new(endpoint)
|
14
|
+
|
15
|
+
headers = {'user-agent' => 'curl/7.69.1', 'accept' => '*/*'}
|
16
|
+
|
17
|
+
file = File.open("products.csv", "w")
|
18
|
+
Async.logger.info(self) {"Saving download to #{Dir.pwd}"}
|
19
|
+
|
20
|
+
begin
|
21
|
+
response = client.head(endpoint.path, headers)
|
22
|
+
content_length = nil
|
23
|
+
|
24
|
+
if response.success?
|
25
|
+
unless response.headers['accept-ranges'].include?('bytes')
|
26
|
+
raise "Does not advertise support for accept-ranges: bytes!"
|
27
|
+
end
|
28
|
+
|
29
|
+
unless content_length = response.body&.length
|
30
|
+
raise "Could not determine length of response!"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
ensure
|
34
|
+
response&.close
|
35
|
+
end
|
36
|
+
|
37
|
+
Async.logger.info(self) {"Content length: #{content_length/(1024**2)}MiB"}
|
38
|
+
|
39
|
+
parts = []
|
40
|
+
offset = 0
|
41
|
+
chunk_size = 1024*1024
|
42
|
+
|
43
|
+
start_time = Async::Clock.now
|
44
|
+
amount = 0
|
45
|
+
|
46
|
+
while offset < content_length
|
47
|
+
byte_range_start = offset
|
48
|
+
byte_range_end = [offset + chunk_size, content_length].min
|
49
|
+
parts << (byte_range_start...byte_range_end)
|
50
|
+
|
51
|
+
offset += chunk_size
|
52
|
+
end
|
53
|
+
|
54
|
+
Async.logger.info(self) {"Breaking download into #{parts.size} parts..."}
|
55
|
+
|
56
|
+
barrier = Async::Barrier.new
|
57
|
+
|
58
|
+
while !parts.empty?
|
59
|
+
barrier.async do
|
60
|
+
part = parts.shift
|
61
|
+
|
62
|
+
Async.logger.info(self) {"Issuing range request range: bytes=#{part.min}-#{part.max}"}
|
63
|
+
|
64
|
+
response = client.get(endpoint.path, [
|
65
|
+
["range", "bytes=#{part.min}-#{part.max-1}"],
|
66
|
+
*headers
|
67
|
+
])
|
68
|
+
|
69
|
+
if response.success?
|
70
|
+
Async.logger.info(self) {"Got response: #{response}... writing data for #{part}."}
|
71
|
+
written = file.pwrite(response.read, part.min)
|
72
|
+
|
73
|
+
amount += written
|
74
|
+
|
75
|
+
duration = Async::Clock.now - start_time
|
76
|
+
Async.logger.info(self) {"Rate: #{((amount.to_f/(1024**2))/duration).round(2)}MiB/s"}
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
barrier.wait
|
82
|
+
ensure
|
83
|
+
client&.close
|
84
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'async'
|
4
|
+
require 'async/http/internet'
|
5
|
+
|
6
|
+
Async do |parent|
|
7
|
+
internet = Async::HTTP::Internet.new
|
8
|
+
connection = nil
|
9
|
+
|
10
|
+
child = parent.async do
|
11
|
+
response = internet.get("https://utopia-falcon-heroku.herokuapp.com/beer/index")
|
12
|
+
connection = response.connection
|
13
|
+
|
14
|
+
response.each do |chunk|
|
15
|
+
Async.logger.info(response) {chunk}
|
16
|
+
end
|
17
|
+
ensure
|
18
|
+
Async.logger.info(response) {"Closing response..."}
|
19
|
+
response&.close
|
20
|
+
end
|
21
|
+
|
22
|
+
parent.sleep(5)
|
23
|
+
|
24
|
+
Async.logger.info(parent) {"Killing #{child}..."}
|
25
|
+
child.stop
|
26
|
+
ensure
|
27
|
+
internet&.close
|
28
|
+
end
|
data/lib/async/http/client.rb
CHANGED
@@ -103,36 +103,53 @@ module Async
|
|
103
103
|
# As we cache pool, it's possible these pool go bad (e.g. closed by remote host). In this case, we need to try again. It's up to the caller to impose a timeout on this. If this is the last attempt, we force a new connection.
|
104
104
|
connection = @pool.acquire
|
105
105
|
|
106
|
-
response = request
|
106
|
+
response = make_response(request, connection)
|
107
107
|
|
108
|
-
#
|
109
|
-
|
110
|
-
@pool.release(connection)
|
111
|
-
end
|
108
|
+
# This signals that the ensure block below should not try to release the connection, because it's bound into the response which will be returned:
|
109
|
+
connection = nil
|
112
110
|
|
113
111
|
return response
|
114
112
|
rescue Protocol::RequestFailed
|
115
113
|
# This is a specific case where the entire request wasn't sent before a failure occurred. So, we can even resend non-idempotent requests.
|
116
|
-
|
114
|
+
if connection
|
115
|
+
@pool.release(connection)
|
116
|
+
connection = nil
|
117
|
+
end
|
117
118
|
|
118
119
|
if attempt < @retries
|
119
120
|
retry
|
120
121
|
else
|
121
122
|
raise
|
122
123
|
end
|
123
|
-
rescue
|
124
|
-
|
124
|
+
rescue Errno::ECONNRESET, Errno::EPIPE, IOError
|
125
|
+
if connection
|
126
|
+
@pool.release(connection)
|
127
|
+
connection = nil
|
128
|
+
end
|
125
129
|
|
126
130
|
if request.idempotent? and attempt < @retries
|
127
131
|
retry
|
128
132
|
else
|
129
133
|
raise
|
130
134
|
end
|
135
|
+
ensure
|
136
|
+
@pool.release(connection) if connection
|
131
137
|
end
|
132
138
|
end
|
133
139
|
|
134
140
|
protected
|
135
141
|
|
142
|
+
def make_response(request, connection)
|
143
|
+
response = request.call(connection)
|
144
|
+
|
145
|
+
# The connection won't be released until the body is completely read/released.
|
146
|
+
::Protocol::HTTP::Body::Streamable.wrap(response) do
|
147
|
+
@pool.release(connection)
|
148
|
+
end
|
149
|
+
|
150
|
+
return response
|
151
|
+
end
|
152
|
+
|
136
153
|
def make_pool(connection_limit)
|
137
154
|
Async::Pool::Controller.wrap(limit: connection_limit) do
|
138
155
|
Async.logger.debug(self) {"Making connection to #{@endpoint.inspect}"}
|
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.50.
|
4
|
+
version: 0.50.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-03-
|
11
|
+
date: 2020-03-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: async
|
@@ -94,6 +94,20 @@ dependencies:
|
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: 0.11.0
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: bake-bundler
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
97
111
|
- !ruby/object:Gem::Dependency
|
98
112
|
name: async-rspec
|
99
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -192,20 +206,6 @@ dependencies:
|
|
192
206
|
- - "~>"
|
193
207
|
- !ruby/object:Gem::Version
|
194
208
|
version: '3.6'
|
195
|
-
- !ruby/object:Gem::Dependency
|
196
|
-
name: rake
|
197
|
-
requirement: !ruby/object:Gem::Requirement
|
198
|
-
requirements:
|
199
|
-
- - ">="
|
200
|
-
- !ruby/object:Gem::Version
|
201
|
-
version: '0'
|
202
|
-
type: :development
|
203
|
-
prerelease: false
|
204
|
-
version_requirements: !ruby/object:Gem::Requirement
|
205
|
-
requirements:
|
206
|
-
- - ">="
|
207
|
-
- !ruby/object:Gem::Version
|
208
|
-
version: '0'
|
209
209
|
description:
|
210
210
|
email:
|
211
211
|
- samuel.williams@oriontransfer.co.nz
|
@@ -219,13 +219,13 @@ files:
|
|
219
219
|
- ".travis.yml"
|
220
220
|
- Gemfile
|
221
221
|
- README.md
|
222
|
-
- Rakefile
|
223
222
|
- async-http.gemspec
|
224
223
|
- bake.rb
|
225
224
|
- bake/async/http.rb
|
226
225
|
- bake/async/http/h2spec.rb
|
227
226
|
- examples/compare/Gemfile
|
228
227
|
- examples/compare/benchmark.rb
|
228
|
+
- examples/download/chunked.rb
|
229
229
|
- examples/fetch/Gemfile
|
230
230
|
- examples/fetch/Gemfile.lock
|
231
231
|
- examples/fetch/README.md
|
@@ -234,6 +234,7 @@ files:
|
|
234
234
|
- examples/fetch/public/stream.js
|
235
235
|
- examples/google/search.rb
|
236
236
|
- examples/request.rb
|
237
|
+
- examples/stream/stop.rb
|
237
238
|
- examples/trenni/Gemfile
|
238
239
|
- examples/trenni/streaming.rb
|
239
240
|
- examples/upload/client.rb
|