async-http 0.52.0 → 0.52.5

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.
data/Gemfile DELETED
@@ -1,11 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gemspec
4
-
5
- # gem "async", path: "../async"
6
- # gem "async-io", path: "../async-io"
7
-
8
- # gem "protocol-http", path: "../protocol-http"
9
- # gem "protocol-http1", path: "../protocol-http1"
10
- # gem "protocol-http2", path: "../protocol-http2"
11
- # gem "protocol-hpack", path: "../protocol-hpack"
data/README.md DELETED
@@ -1,365 +0,0 @@
1
- # Async::HTTP
2
-
3
- An asynchronous client and server implementation of HTTP/1.0, HTTP/1.1 and HTTP/2 including TLS. Support for streaming requests and responses. Built on top of [async] and [async-io]. [falcon] provides a rack-compatible server.
4
-
5
- [![Build Status](https://travis-ci.com/socketry/async-http.svg?branch=master)](https://travis-ci.com/socketry/async-http)
6
- [![Code Climate](https://codeclimate.com/github/socketry/async-http.svg)](https://codeclimate.com/github/socketry/async-http)
7
- [![Coverage Status](https://coveralls.io/repos/socketry/async-http/badge.svg)](https://coveralls.io/r/socketry/async-http)
8
-
9
- [async]: https://github.com/socketry/async
10
- [async-io]: https://github.com/socketry/async-io
11
- [falcon]: https://github.com/socketry/falcon
12
-
13
- ## Installation
14
-
15
- Add this line to your application's Gemfile:
16
-
17
- ```ruby
18
- gem 'async-http'
19
- ```
20
-
21
- And then execute:
22
-
23
- $ bundle
24
-
25
- Or install it yourself as:
26
-
27
- $ gem install async-http
28
-
29
- ## Usage
30
-
31
- ### Post JSON data
32
-
33
- Here is an example showing how to post a data structure as JSON to a remote resource:
34
-
35
- ```ruby
36
- #!/usr/bin/env ruby
37
-
38
- require 'json'
39
- require 'async'
40
- require 'async/http/internet'
41
-
42
- data = {'life' => 42}
43
-
44
- Async do
45
- # Make a new internet:
46
- internet = Async::HTTP::Internet.new
47
-
48
- # Prepare the request:
49
- headers = [['accept', 'application/json']]
50
- body = [JSON.dump(data)]
51
-
52
- # Issues a POST request:
53
- response = internet.post("https://httpbin.org/anything", headers, body)
54
-
55
- # Save the response body to a local file:
56
- pp JSON.parse(response.read)
57
- ensure
58
- # The internet is closed for business:
59
- internet.close
60
- end
61
- ```
62
-
63
- Consider using [async-rest](https://github.com/socketry/async-rest) instead.
64
-
65
- ### Multiple Requests
66
-
67
- To issue multiple requests concurrently, you should use a barrier, e.g.
68
-
69
- ```ruby
70
- #!/usr/bin/env ruby
71
-
72
- require 'async'
73
- require 'async/barrier'
74
- require 'async/http/internet'
75
-
76
- TOPICS = ["ruby", "python", "rust"]
77
-
78
- Async do
79
- internet = Async::HTTP::Internet.new
80
- barrier = Async::Barrier.new
81
-
82
- # Spawn an asynchronous task for each topic:
83
- TOPICS.each do |topic|
84
- barrier.async do
85
- response = internet.get "https://www.google.com/search?q=#{topic}"
86
- puts "Found #{topic}: #{response.read.scan(topic).size} times."
87
- end
88
- end
89
-
90
- # Ensure we wait for all requests to complete before continuing:
91
- barrier.wait
92
- ensure
93
- internet&.close
94
- end
95
- ```
96
-
97
- #### Limiting Requests
98
-
99
- If you need to limit the number of simultaneous requests, use a semaphore.
100
-
101
- ```ruby
102
- #!/usr/bin/env ruby
103
-
104
- require 'async'
105
- require 'async/barrier'
106
- require 'async/semaphore'
107
- require 'async/http/internet'
108
-
109
- TOPICS = ["ruby", "python", "rust"]
110
-
111
- Async do
112
- internet = Async::HTTP::Internet.new
113
- barrier = Async::Barrier.new
114
- semaphore = Async::Semaphore.new(2, parent: barrier)
115
-
116
- # Spawn an asynchronous task for each topic:
117
- TOPICS.each do |topic|
118
- semaphore.async do
119
- response = internet.get "https://www.google.com/search?q=#{topic}"
120
- puts "Found #{topic}: #{response.read.scan(topic).size} times."
121
- end
122
- end
123
-
124
- # Ensure we wait for all requests to complete before continuing:
125
- barrier.wait
126
- ensure
127
- internet&.close
128
- end
129
- ```
130
-
131
- ### Downloading a File
132
-
133
- Here is an example showing how to download a file and save it to a local path:
134
-
135
- ```ruby
136
- #!/usr/bin/env ruby
137
-
138
- require 'async'
139
- require 'async/http/internet'
140
-
141
- Async do
142
- # Make a new internet:
143
- internet = Async::HTTP::Internet.new
144
-
145
- # Issues a GET request to Google:
146
- response = internet.get("https://www.google.com/search?q=kittens")
147
-
148
- # Save the response body to a local file:
149
- response.save("/tmp/search.html")
150
- ensure
151
- # The internet is closed for business:
152
- internet.close
153
- end
154
- ```
155
-
156
- ### Basic Client/Server
157
-
158
- Here is a basic example of a client/server running in the same reactor:
159
-
160
- ```ruby
161
- #!/usr/bin/env ruby
162
-
163
- require 'async'
164
- require 'async/http/server'
165
- require 'async/http/client'
166
- require 'async/http/endpoint'
167
- require 'async/http/protocol/response'
168
-
169
- endpoint = Async::HTTP::Endpoint.parse('http://127.0.0.1:9294')
170
-
171
- app = lambda do |request|
172
- Protocol::HTTP::Response[200, {}, ["Hello World"]]
173
- end
174
-
175
- server = Async::HTTP::Server.new(app, endpoint)
176
- client = Async::HTTP::Client.new(endpoint)
177
-
178
- Async do |task|
179
- server_task = task.async do
180
- server.run
181
- end
182
-
183
- response = client.get("/")
184
-
185
- puts response.status
186
- puts response.read
187
-
188
- server_task.stop
189
- end
190
- ```
191
-
192
- ### Advanced Verification
193
-
194
- You can hook into SSL certificate verification to improve server verification.
195
-
196
- ```ruby
197
- require 'async'
198
- require 'async/http'
199
-
200
- # These are generated from the certificate chain that the server presented.
201
- trusted_fingerprints = {
202
- "dac9024f54d8f6df94935fb1732638ca6ad77c13" => true,
203
- "e6a3b45b062d509b3382282d196efe97d5956ccb" => true,
204
- "07d63f4c05a03f1c306f9941b8ebf57598719ea2" => true,
205
- "e8d994f44ff20dc78dbff4e59d7da93900572bbf" => true,
206
- }
207
-
208
- Async do
209
- endpoint = Async::HTTP::Endpoint.parse("https://www.codeotaku.com/index")
210
-
211
- # This is a quick hack/POC:
212
- ssl_context = endpoint.ssl_context
213
-
214
- ssl_context.verify_callback = proc do |verified, store_context|
215
- certificate = store_context.current_cert
216
- fingerprint = OpenSSL::Digest::SHA1.new(certificate.to_der).to_s
217
-
218
- if trusted_fingerprints.include? fingerprint
219
- true
220
- else
221
- Async.logger.warn("Untrusted Certificate Fingerprint"){fingerprint}
222
- false
223
- end
224
- end
225
-
226
- endpoint = endpoint.with(ssl_context: ssl_context)
227
-
228
- client = Async::HTTP::Client.new(endpoint)
229
-
230
- response = client.get(endpoint.path)
231
-
232
- pp response.status, response.headers.fields, response.read
233
- end
234
- ```
235
-
236
- ## Performance
237
-
238
- On a 4-core 8-thread i7, running `ab` which uses discrete (non-keep-alive) connections:
239
-
240
- ```
241
- $ ab -c 8 -t 10 http://127.0.0.1:9294/
242
- This is ApacheBench, Version 2.3 <$Revision: 1757674 $>
243
- Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
244
- Licensed to The Apache Software Foundation, http://www.apache.org/
245
-
246
- Benchmarking 127.0.0.1 (be patient)
247
- Completed 5000 requests
248
- Completed 10000 requests
249
- Completed 15000 requests
250
- Completed 20000 requests
251
- Completed 25000 requests
252
- Completed 30000 requests
253
- Completed 35000 requests
254
- Completed 40000 requests
255
- Completed 45000 requests
256
- Completed 50000 requests
257
- Finished 50000 requests
258
-
259
-
260
- Server Software:
261
- Server Hostname: 127.0.0.1
262
- Server Port: 9294
263
-
264
- Document Path: /
265
- Document Length: 13 bytes
266
-
267
- Concurrency Level: 8
268
- Time taken for tests: 1.869 seconds
269
- Complete requests: 50000
270
- Failed requests: 0
271
- Total transferred: 2450000 bytes
272
- HTML transferred: 650000 bytes
273
- Requests per second: 26755.55 [#/sec] (mean)
274
- Time per request: 0.299 [ms] (mean)
275
- Time per request: 0.037 [ms] (mean, across all concurrent requests)
276
- Transfer rate: 1280.29 [Kbytes/sec] received
277
-
278
- Connection Times (ms)
279
- min mean[+/-sd] median max
280
- Connect: 0 0 0.0 0 0
281
- Processing: 0 0 0.2 0 6
282
- Waiting: 0 0 0.2 0 6
283
- Total: 0 0 0.2 0 6
284
-
285
- Percentage of the requests served within a certain time (ms)
286
- 50% 0
287
- 66% 0
288
- 75% 0
289
- 80% 0
290
- 90% 0
291
- 95% 1
292
- 98% 1
293
- 99% 1
294
- 100% 6 (longest request)
295
- ```
296
-
297
- On a 4-core 8-thread i7, running `wrk`, which uses 8 keep-alive connections:
298
-
299
- ```
300
- $ wrk -c 8 -d 10 -t 8 http://127.0.0.1:9294/
301
- Running 10s test @ http://127.0.0.1:9294/
302
- 8 threads and 8 connections
303
- Thread Stats Avg Stdev Max +/- Stdev
304
- Latency 217.69us 0.99ms 23.21ms 97.39%
305
- Req/Sec 12.18k 1.58k 17.67k 83.21%
306
- 974480 requests in 10.10s, 60.41MB read
307
- Requests/sec: 96485.00
308
- Transfer/sec: 5.98MB
309
- ```
310
-
311
- According to these results, the cost of handling connections is quite high, while general throughput seems pretty decent.
312
-
313
- ## Semantic Model
314
-
315
- ### Scheme
316
-
317
- HTTP/1 has an implicit scheme determined by the kind of connection made to the server (either `http` or `https`), while HTTP/2 models this explicitly and the client indicates this in the request using the `:scheme` pseudo-header (typically `https`). To normalize this, `Async::HTTP::Client` and `Async::HTTP::Server` have a default scheme which is used if none is supplied.
318
-
319
- ### Version
320
-
321
- HTTP/1 has an explicit version while HTTP/2 does not expose the version in any way.
322
-
323
- ### Reason
324
-
325
- HTTP/1 responses contain a reason field which is largely irrelevant. HTTP/2 does not support this field.
326
-
327
- ## Contributing
328
-
329
- 1. Fork it
330
- 2. Create your feature branch (`git checkout -b my-new-feature`)
331
- 3. Commit your changes (`git commit -am 'Add some feature'`)
332
- 4. Push to the branch (`git push origin my-new-feature`)
333
- 5. Create new Pull Request
334
-
335
- ## See Also
336
-
337
- - [benchmark-http](https://github.com/socketry/benchmark-http) — A benchmarking tool to report on web server concurrency.
338
- - [falcon](https://github.com/socketry/falcon) — A rack compatible server built on top of `async-http`.
339
- - [async-websocket](https://github.com/socketry/async-websocket) — Asynchronous client and server websockets.
340
- - [async-rest](https://github.com/socketry/async-rest) — A RESTful resource layer built on top of `async-http`.
341
- - [async-http-faraday](https://github.com/socketry/async-http-faraday) — A faraday adapter to use `async-http`.
342
-
343
- ## License
344
-
345
- Released under the MIT license.
346
-
347
- Copyright, 2018, by [Samuel G. D. Williams](http://www.codeotaku.com/samuel-williams).
348
-
349
- Permission is hereby granted, free of charge, to any person obtaining a copy
350
- of this software and associated documentation files (the "Software"), to deal
351
- in the Software without restriction, including without limitation the rights
352
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
353
- copies of the Software, and to permit persons to whom the Software is
354
- furnished to do so, subject to the following conditions:
355
-
356
- The above copyright notice and this permission notice shall be included in
357
- all copies or substantial portions of the Software.
358
-
359
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
360
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
361
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
362
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
363
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
364
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
365
- THE SOFTWARE.
@@ -1,39 +0,0 @@
1
-
2
- require_relative 'lib/async/http/version'
3
-
4
- Gem::Specification.new do |spec|
5
- spec.name = "async-http"
6
- spec.version = Async::HTTP::VERSION
7
- spec.licenses = ["MIT"]
8
- spec.authors = ["Samuel Williams"]
9
- spec.email = ["samuel.williams@oriontransfer.co.nz"]
10
-
11
- spec.summary = "A HTTP client and server library."
12
- spec.homepage = "https://github.com/socketry/async-http"
13
-
14
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
15
- f.match(%r{^(test|spec|features)/})
16
- end
17
- spec.executables = spec.files.grep(%r{^bin/}) {|f| File.basename(f)}
18
- spec.require_paths = ["lib"]
19
-
20
- spec.add_dependency("async", "~> 1.25")
21
- spec.add_dependency("async-io", "~> 1.28")
22
- spec.add_dependency("async-pool", "~> 0.2")
23
-
24
- spec.add_dependency("protocol-http", "~> 0.18.0")
25
- spec.add_dependency("protocol-http1", "~> 0.12.0")
26
- spec.add_dependency("protocol-http2", "~> 0.14.0")
27
-
28
- # spec.add_dependency("openssl")
29
-
30
- spec.add_development_dependency "async-rspec", "~> 1.10"
31
- spec.add_development_dependency "async-container", "~> 0.14.0"
32
-
33
- spec.add_development_dependency "rack-test"
34
-
35
- spec.add_development_dependency "covered"
36
- spec.add_development_dependency "bundler"
37
- spec.add_development_dependency "bake-bundler"
38
- spec.add_development_dependency "rspec", "~> 3.6"
39
- end
data/bake.rb DELETED
File without changes
@@ -1,9 +0,0 @@
1
-
2
- source "https://rubygems.org"
3
-
4
- gem "benchmark-ips"
5
-
6
- gem "async"
7
- gem "async-http"
8
-
9
- gem "httpx"
@@ -1,78 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'benchmark'
4
-
5
- require 'httpx'
6
-
7
- require 'async'
8
- require 'async/barrier'
9
- require 'async/semaphore'
10
- require 'async/http/internet'
11
-
12
- URL = "https://www.codeotaku.com/index"
13
- REPEATS = 10
14
-
15
- Benchmark.bmbm do |x|
16
- x.report("async-http") do
17
- Async do
18
- internet = Async::HTTP::Internet.new
19
-
20
- i = 0
21
- while i < REPEATS
22
- response = internet.get(URL)
23
- response.read
24
-
25
- i += 1
26
- end
27
- ensure
28
- internet&.close
29
- end
30
- end
31
-
32
- x.report("async-http (pipelined)") do
33
- Async do |task|
34
- internet = Async::HTTP::Internet.new
35
- semaphore = Async::Semaphore.new(100, parent: task)
36
- barrier = Async::Barrier.new(parent: semaphore)
37
-
38
- # Warm up the connection pool...
39
- response = internet.get(URL)
40
- response.read
41
-
42
- i = 0
43
- while i < REPEATS
44
- barrier.async do
45
- response = internet.get(URL)
46
-
47
- response.read
48
- end
49
-
50
- i += 1
51
- end
52
-
53
- barrier.wait
54
- ensure
55
- internet&.close
56
- end
57
- end
58
-
59
- x.report("httpx") do
60
- i = 0
61
- while i < REPEATS
62
- response = HTTPX.get(URL)
63
-
64
- response.read
65
-
66
- i += 1
67
- end
68
- end
69
-
70
- x.report("httpx (pipelined)") do
71
- urls = [URL] * REPEATS
72
- responses = HTTPX.get(*urls)
73
-
74
- responses.each do |response|
75
- response.read
76
- end
77
- end
78
- end