async-http 0.67.1 → 0.69.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/lib/async/http/endpoint.rb +13 -0
- data/lib/async/http/internet/instance.rb +14 -3
- data/lib/async/http/internet.rb +15 -6
- data/lib/async/http/mock/endpoint.rb +70 -0
- data/lib/async/http/mock.rb +23 -0
- data/lib/async/http/protocol/http.rb +2 -1
- data/lib/async/http/protocol/http1.rb +1 -1
- data/lib/async/http/protocol/http10.rb +1 -1
- data/lib/async/http/protocol/http11.rb +1 -1
- data/lib/async/http/protocol/http2.rb +1 -1
- data/lib/async/http/version.rb +1 -1
- data/lib/async/http.rb +2 -0
- data/license.md +3 -2
- data/readme.md +3 -348
- data.tar.gz.sig +0 -0
- metadata +21 -24
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9f4a2ceef3a210332512cbe39f7bcac65cecaf23308f151534cfeb955fccb3e1
|
4
|
+
data.tar.gz: a44a6b0f61b2bdbf81b868e1e05bfe7e45855bc92a3efdc266534688d9f84fd8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 22f65ce662e632e0d7836f13c12db69f86305ecae5e88ccf97ace1e0dc3c861e5af4fce7877dd489fec2be48031dfef99b1b697c1c0153df9670f095061465e4
|
7
|
+
data.tar.gz: 9cfb68209dcff74007c239d94176e7a3b9ac76a6667066e55e1d230b54e77663389cb5f81743ca2ac0f40b5f832e2f4720d65fa55c69962f808a9860a1018bd8
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/lib/async/http/endpoint.rb
CHANGED
@@ -3,6 +3,9 @@
|
|
3
3
|
# Released under the MIT License.
|
4
4
|
# Copyright, 2019-2024, by Samuel Williams.
|
5
5
|
# Copyright, 2021-2022, by Adam Daniels.
|
6
|
+
# Copyright, 2024, by Thomas Morgan.
|
7
|
+
# Copyright, 2024, by Igor Sidorov.
|
8
|
+
# Copyright, 2024, by Hal Brodigan.
|
6
9
|
|
7
10
|
require 'io/endpoint'
|
8
11
|
require 'io/endpoint/host_endpoint'
|
@@ -32,6 +35,16 @@ module Async
|
|
32
35
|
)
|
33
36
|
end
|
34
37
|
|
38
|
+
# Coerce the given object into an endpoint.
|
39
|
+
# @parameter url [String | Endpoint] The URL or endpoint to convert.
|
40
|
+
def self.[](url)
|
41
|
+
if url.is_a?(Endpoint)
|
42
|
+
return url
|
43
|
+
else
|
44
|
+
Endpoint.parse(url.to_s)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
35
48
|
# @option scheme [String] the scheme to use, overrides the URL scheme.
|
36
49
|
# @option hostname [String] the hostname to connect to (or bind to), overrides the URL hostname (used for SNI).
|
37
50
|
# @option port [Integer] the port to bind to, overrides the URL port.
|
@@ -4,13 +4,24 @@
|
|
4
4
|
# Copyright, 2021-2023, by Samuel Williams.
|
5
5
|
|
6
6
|
require_relative '../internet'
|
7
|
-
|
7
|
+
|
8
|
+
::Thread.attr_accessor :async_http_internet_instance
|
8
9
|
|
9
10
|
module Async
|
10
11
|
module HTTP
|
11
12
|
class Internet
|
12
|
-
#
|
13
|
-
|
13
|
+
# The global instance of the internet.
|
14
|
+
def self.instance
|
15
|
+
::Thread.current.async_http_internet_instance ||= self.new
|
16
|
+
end
|
17
|
+
|
18
|
+
class << self
|
19
|
+
::Protocol::HTTP::Methods.each do |name, verb|
|
20
|
+
define_method(verb.downcase) do |url, headers = nil, body = nil, &block|
|
21
|
+
self.instance.call(verb, url, headers, body, &block)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
14
25
|
end
|
15
26
|
end
|
16
27
|
end
|
data/lib/async/http/internet.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2018-
|
4
|
+
# Copyright, 2018-2024, by Samuel Williams.
|
5
|
+
# Copyright, 2024, by Igor Sidorov.
|
5
6
|
|
6
7
|
require_relative 'client'
|
7
8
|
require_relative 'endpoint'
|
@@ -38,8 +39,8 @@ module Async
|
|
38
39
|
# @parameter url [String] The URL to request, e.g. `https://www.codeotaku.com`.
|
39
40
|
# @parameter headers [Hash | Protocol::HTTP::Headers] The headers to send with the request.
|
40
41
|
# @parameter body [String | Protocol::HTTP::Body] The body to send with the request.
|
41
|
-
def call(method, url, headers = nil, body = nil)
|
42
|
-
endpoint = Endpoint
|
42
|
+
def call(method, url, headers = nil, body = nil, &block)
|
43
|
+
endpoint = Endpoint[url]
|
43
44
|
client = self.client_for(endpoint)
|
44
45
|
|
45
46
|
body = Body::Buffered.wrap(body)
|
@@ -47,7 +48,15 @@ module Async
|
|
47
48
|
|
48
49
|
request = ::Protocol::HTTP::Request.new(endpoint.scheme, endpoint.authority, method, endpoint.path, nil, headers, body)
|
49
50
|
|
50
|
-
|
51
|
+
response = client.call(request)
|
52
|
+
|
53
|
+
return response unless block_given?
|
54
|
+
|
55
|
+
begin
|
56
|
+
yield response
|
57
|
+
ensure
|
58
|
+
response.close
|
59
|
+
end
|
51
60
|
end
|
52
61
|
|
53
62
|
def close
|
@@ -59,8 +68,8 @@ module Async
|
|
59
68
|
end
|
60
69
|
|
61
70
|
::Protocol::HTTP::Methods.each do |name, verb|
|
62
|
-
define_method(verb.downcase) do |url, headers = nil, body = nil|
|
63
|
-
self.call(verb, url
|
71
|
+
define_method(verb.downcase) do |url, headers = nil, body = nil, &block|
|
72
|
+
self.call(verb, url, headers, body, &block)
|
64
73
|
end
|
65
74
|
end
|
66
75
|
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
# Copyright, 2019, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
# THE SOFTWARE.
|
22
|
+
|
23
|
+
require_relative '../protocol'
|
24
|
+
|
25
|
+
require 'async/queue'
|
26
|
+
|
27
|
+
module Async
|
28
|
+
module HTTP
|
29
|
+
module Mock
|
30
|
+
# This is an endpoint which bridges a client with a local server.
|
31
|
+
class Endpoint
|
32
|
+
def initialize(protocol = Protocol::HTTP2, scheme = "http", authority = "localhost", queue: Queue.new)
|
33
|
+
@protocol = protocol
|
34
|
+
@scheme = scheme
|
35
|
+
@authority = authority
|
36
|
+
|
37
|
+
@queue = queue
|
38
|
+
end
|
39
|
+
|
40
|
+
attr :protocol
|
41
|
+
attr :scheme
|
42
|
+
attr :authority
|
43
|
+
|
44
|
+
# Processing incoming connections
|
45
|
+
# @yield [::HTTP::Protocol::Request] the requests as they come in.
|
46
|
+
def run(parent: Task.current, &block)
|
47
|
+
while peer = @queue.dequeue
|
48
|
+
server = @protocol.server(peer)
|
49
|
+
|
50
|
+
parent.async do
|
51
|
+
server.each(&block)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def connect
|
57
|
+
local, remote = ::Socket.pair(Socket::AF_UNIX, Socket::SOCK_STREAM)
|
58
|
+
|
59
|
+
@queue.enqueue(remote)
|
60
|
+
|
61
|
+
return local
|
62
|
+
end
|
63
|
+
|
64
|
+
def wrap(endpoint)
|
65
|
+
self.class.new(@protocol, endpoint.scheme, endpoint.authority, queue: @queue)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
# Copyright, 2019, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
# THE SOFTWARE.
|
22
|
+
|
23
|
+
require_relative 'mock/endpoint'
|
data/lib/async/http/version.rb
CHANGED
data/lib/async/http.rb
CHANGED
data/license.md
CHANGED
@@ -7,7 +7,7 @@ Copyright, 2019, by Denis Talakevich.
|
|
7
7
|
Copyright, 2019-2020, by Brian Morearty.
|
8
8
|
Copyright, 2019, by Cyril Roelandt.
|
9
9
|
Copyright, 2020, by Stefan Wrobel.
|
10
|
-
Copyright, 2020, by Igor Sidorov.
|
10
|
+
Copyright, 2020-2024, by Igor Sidorov.
|
11
11
|
Copyright, 2020, by Bruno Sutic.
|
12
12
|
Copyright, 2020, by Sam Shadwell.
|
13
13
|
Copyright, 2020, by Orgad Shaneh.
|
@@ -17,10 +17,11 @@ Copyright, 2021-2022, by Adam Daniels.
|
|
17
17
|
Copyright, 2022, by Ian Ker-Seymer.
|
18
18
|
Copyright, 2022, by Marco Concetto Rudilosso.
|
19
19
|
Copyright, 2022, by Tim Meusel.
|
20
|
-
Copyright, 2023, by Thomas Morgan.
|
20
|
+
Copyright, 2023-2024, by Thomas Morgan.
|
21
21
|
Copyright, 2023, by dependabot[bot].
|
22
22
|
Copyright, 2023, by Josh Huber.
|
23
23
|
Copyright, 2024, by Anton Zhuravsky.
|
24
|
+
Copyright, 2024, by Hal Brodigan.
|
24
25
|
|
25
26
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
26
27
|
of this software and associated documentation files (the "Software"), to deal
|
data/readme.md
CHANGED
@@ -4,358 +4,13 @@ An asynchronous client and server implementation of HTTP/1.0, HTTP/1.1 and HTTP/
|
|
4
4
|
|
5
5
|
[![Development Status](https://github.com/socketry/async-http/workflows/Test/badge.svg)](https://github.com/socketry/async-http/actions?workflow=Test)
|
6
6
|
|
7
|
-
## Installation
|
8
|
-
|
9
|
-
Add this line to your application's Gemfile:
|
10
|
-
|
11
|
-
``` ruby
|
12
|
-
gem 'async-http'
|
13
|
-
```
|
14
|
-
|
15
|
-
And then execute:
|
16
|
-
|
17
|
-
$ bundle
|
18
|
-
|
19
|
-
Or install it yourself as:
|
20
|
-
|
21
|
-
$ gem install async-http
|
22
|
-
|
23
7
|
## Usage
|
24
8
|
|
25
|
-
Please see the [project documentation](https://socketry.github.io/async-http/)
|
26
|
-
|
27
|
-
### Post JSON data
|
28
|
-
|
29
|
-
Here is an example showing how to post a data structure as JSON to a remote resource:
|
30
|
-
|
31
|
-
``` ruby
|
32
|
-
#!/usr/bin/env ruby
|
33
|
-
|
34
|
-
require 'json'
|
35
|
-
require 'async'
|
36
|
-
require 'async/http/internet'
|
37
|
-
|
38
|
-
data = {'life' => 42}
|
39
|
-
|
40
|
-
Async do
|
41
|
-
# Make a new internet:
|
42
|
-
internet = Async::HTTP::Internet.new
|
43
|
-
|
44
|
-
# Prepare the request:
|
45
|
-
headers = [['accept', 'application/json']]
|
46
|
-
body = [JSON.dump(data)]
|
47
|
-
|
48
|
-
# Issues a POST request:
|
49
|
-
response = internet.post("https://httpbin.org/anything", headers, body)
|
50
|
-
|
51
|
-
# Save the response body to a local file:
|
52
|
-
pp JSON.parse(response.read)
|
53
|
-
ensure
|
54
|
-
# The internet is closed for business:
|
55
|
-
internet.close
|
56
|
-
end
|
57
|
-
```
|
58
|
-
|
59
|
-
Consider using [async-rest](https://github.com/socketry/async-rest) instead.
|
60
|
-
|
61
|
-
### Multiple Requests
|
62
|
-
|
63
|
-
To issue multiple requests concurrently, you should use a barrier, e.g.
|
64
|
-
|
65
|
-
``` ruby
|
66
|
-
#!/usr/bin/env ruby
|
67
|
-
|
68
|
-
require 'async'
|
69
|
-
require 'async/barrier'
|
70
|
-
require 'async/http/internet'
|
71
|
-
|
72
|
-
TOPICS = ["ruby", "python", "rust"]
|
73
|
-
|
74
|
-
Async do
|
75
|
-
internet = Async::HTTP::Internet.new
|
76
|
-
barrier = Async::Barrier.new
|
77
|
-
|
78
|
-
# Spawn an asynchronous task for each topic:
|
79
|
-
TOPICS.each do |topic|
|
80
|
-
barrier.async do
|
81
|
-
response = internet.get "https://www.google.com/search?q=#{topic}"
|
82
|
-
puts "Found #{topic}: #{response.read.scan(topic).size} times."
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
# Ensure we wait for all requests to complete before continuing:
|
87
|
-
barrier.wait
|
88
|
-
ensure
|
89
|
-
internet&.close
|
90
|
-
end
|
91
|
-
```
|
92
|
-
|
93
|
-
#### Limiting Requests
|
94
|
-
|
95
|
-
If you need to limit the number of simultaneous requests, use a semaphore.
|
96
|
-
|
97
|
-
``` ruby
|
98
|
-
#!/usr/bin/env ruby
|
99
|
-
|
100
|
-
require 'async'
|
101
|
-
require 'async/barrier'
|
102
|
-
require 'async/semaphore'
|
103
|
-
require 'async/http/internet'
|
104
|
-
|
105
|
-
TOPICS = ["ruby", "python", "rust"]
|
106
|
-
|
107
|
-
Async do
|
108
|
-
internet = Async::HTTP::Internet.new
|
109
|
-
barrier = Async::Barrier.new
|
110
|
-
semaphore = Async::Semaphore.new(2, parent: barrier)
|
111
|
-
|
112
|
-
# Spawn an asynchronous task for each topic:
|
113
|
-
TOPICS.each do |topic|
|
114
|
-
semaphore.async do
|
115
|
-
response = internet.get "https://www.google.com/search?q=#{topic}"
|
116
|
-
puts "Found #{topic}: #{response.read.scan(topic).size} times."
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
# Ensure we wait for all requests to complete before continuing:
|
121
|
-
barrier.wait
|
122
|
-
ensure
|
123
|
-
internet&.close
|
124
|
-
end
|
125
|
-
```
|
126
|
-
|
127
|
-
### Persistent Connections
|
128
|
-
|
129
|
-
To keep connections alive, install the `thread-local` gem,
|
130
|
-
require `async/http/internet/instance`, and use the `instance`, e.g.
|
131
|
-
|
132
|
-
``` ruby
|
133
|
-
#!/usr/bin/env ruby
|
134
|
-
|
135
|
-
require 'async'
|
136
|
-
require 'async/http/internet/instance'
|
137
|
-
|
138
|
-
Async do
|
139
|
-
internet = Async::HTTP::Internet.instance
|
140
|
-
response = internet.get "https://www.google.com/search?q=test"
|
141
|
-
puts "Found #{response.read.size} results."
|
142
|
-
end
|
143
|
-
```
|
144
|
-
|
145
|
-
### Downloading a File
|
146
|
-
|
147
|
-
Here is an example showing how to download a file and save it to a local path:
|
148
|
-
|
149
|
-
``` ruby
|
150
|
-
#!/usr/bin/env ruby
|
151
|
-
|
152
|
-
require 'async'
|
153
|
-
require 'async/http/internet'
|
154
|
-
|
155
|
-
Async do
|
156
|
-
# Make a new internet:
|
157
|
-
internet = Async::HTTP::Internet.new
|
158
|
-
|
159
|
-
# Issues a GET request to Google:
|
160
|
-
response = internet.get("https://www.google.com/search?q=kittens")
|
161
|
-
|
162
|
-
# Save the response body to a local file:
|
163
|
-
response.save("/tmp/search.html")
|
164
|
-
ensure
|
165
|
-
# The internet is closed for business:
|
166
|
-
internet.close
|
167
|
-
end
|
168
|
-
```
|
169
|
-
|
170
|
-
### Basic Client/Server
|
171
|
-
|
172
|
-
Here is a basic example of a client/server running in the same reactor:
|
173
|
-
|
174
|
-
``` ruby
|
175
|
-
#!/usr/bin/env ruby
|
176
|
-
|
177
|
-
require 'async'
|
178
|
-
require 'async/http/server'
|
179
|
-
require 'async/http/client'
|
180
|
-
require 'async/http/endpoint'
|
181
|
-
require 'async/http/protocol/response'
|
182
|
-
|
183
|
-
endpoint = Async::HTTP::Endpoint.parse('http://127.0.0.1:9294')
|
184
|
-
|
185
|
-
app = lambda do |request|
|
186
|
-
Protocol::HTTP::Response[200, {}, ["Hello World"]]
|
187
|
-
end
|
188
|
-
|
189
|
-
server = Async::HTTP::Server.new(app, endpoint)
|
190
|
-
client = Async::HTTP::Client.new(endpoint)
|
191
|
-
|
192
|
-
Async do |task|
|
193
|
-
server_task = task.async do
|
194
|
-
server.run
|
195
|
-
end
|
196
|
-
|
197
|
-
response = client.get("/")
|
198
|
-
|
199
|
-
puts response.status
|
200
|
-
puts response.read
|
201
|
-
|
202
|
-
server_task.stop
|
203
|
-
end
|
204
|
-
```
|
205
|
-
|
206
|
-
### Advanced Verification
|
207
|
-
|
208
|
-
You can hook into SSL certificate verification to improve server verification.
|
209
|
-
|
210
|
-
``` ruby
|
211
|
-
require 'async'
|
212
|
-
require 'async/http'
|
213
|
-
|
214
|
-
# These are generated from the certificate chain that the server presented.
|
215
|
-
trusted_fingerprints = {
|
216
|
-
"dac9024f54d8f6df94935fb1732638ca6ad77c13" => true,
|
217
|
-
"e6a3b45b062d509b3382282d196efe97d5956ccb" => true,
|
218
|
-
"07d63f4c05a03f1c306f9941b8ebf57598719ea2" => true,
|
219
|
-
"e8d994f44ff20dc78dbff4e59d7da93900572bbf" => true,
|
220
|
-
}
|
221
|
-
|
222
|
-
Async do
|
223
|
-
endpoint = Async::HTTP::Endpoint.parse("https://www.codeotaku.com/index")
|
224
|
-
|
225
|
-
# This is a quick hack/POC:
|
226
|
-
ssl_context = endpoint.ssl_context
|
227
|
-
|
228
|
-
ssl_context.verify_callback = proc do |verified, store_context|
|
229
|
-
certificate = store_context.current_cert
|
230
|
-
fingerprint = OpenSSL::Digest::SHA1.new(certificate.to_der).to_s
|
231
|
-
|
232
|
-
if trusted_fingerprints.include? fingerprint
|
233
|
-
true
|
234
|
-
else
|
235
|
-
Console.logger.warn("Untrusted Certificate Fingerprint"){fingerprint}
|
236
|
-
false
|
237
|
-
end
|
238
|
-
end
|
239
|
-
|
240
|
-
endpoint = endpoint.with(ssl_context: ssl_context)
|
241
|
-
|
242
|
-
client = Async::HTTP::Client.new(endpoint)
|
243
|
-
|
244
|
-
response = client.get(endpoint.path)
|
245
|
-
|
246
|
-
pp response.status, response.headers.fields, response.read
|
247
|
-
end
|
248
|
-
```
|
249
|
-
|
250
|
-
### Timeouts
|
251
|
-
|
252
|
-
Here's a basic example with a timeout:
|
253
|
-
|
254
|
-
``` ruby
|
255
|
-
#!/usr/bin/env ruby
|
256
|
-
|
257
|
-
require 'async/http/internet'
|
258
|
-
|
259
|
-
Async do |task|
|
260
|
-
internet = Async::HTTP::Internet.new
|
261
|
-
|
262
|
-
# Request will timeout after 2 seconds
|
263
|
-
task.with_timeout(2) do
|
264
|
-
response = internet.get "https://httpbin.org/delay/10"
|
265
|
-
end
|
266
|
-
rescue Async::TimeoutError
|
267
|
-
puts "The request timed out"
|
268
|
-
ensure
|
269
|
-
internet&.close
|
270
|
-
end
|
271
|
-
```
|
272
|
-
|
273
|
-
## Performance
|
274
|
-
|
275
|
-
On a 4-core 8-thread i7, running `ab` which uses discrete (non-keep-alive) connections:
|
276
|
-
|
277
|
-
$ ab -c 8 -t 10 http://127.0.0.1:9294/
|
278
|
-
This is ApacheBench, Version 2.3 <$Revision: 1757674 $>
|
279
|
-
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
|
280
|
-
Licensed to The Apache Software Foundation, http://www.apache.org/
|
281
|
-
|
282
|
-
Benchmarking 127.0.0.1 (be patient)
|
283
|
-
Completed 5000 requests
|
284
|
-
Completed 10000 requests
|
285
|
-
Completed 15000 requests
|
286
|
-
Completed 20000 requests
|
287
|
-
Completed 25000 requests
|
288
|
-
Completed 30000 requests
|
289
|
-
Completed 35000 requests
|
290
|
-
Completed 40000 requests
|
291
|
-
Completed 45000 requests
|
292
|
-
Completed 50000 requests
|
293
|
-
Finished 50000 requests
|
294
|
-
|
295
|
-
|
296
|
-
Server Software:
|
297
|
-
Server Hostname: 127.0.0.1
|
298
|
-
Server Port: 9294
|
299
|
-
|
300
|
-
Document Path: /
|
301
|
-
Document Length: 13 bytes
|
302
|
-
|
303
|
-
Concurrency Level: 8
|
304
|
-
Time taken for tests: 1.869 seconds
|
305
|
-
Complete requests: 50000
|
306
|
-
Failed requests: 0
|
307
|
-
Total transferred: 2450000 bytes
|
308
|
-
HTML transferred: 650000 bytes
|
309
|
-
Requests per second: 26755.55 [#/sec] (mean)
|
310
|
-
Time per request: 0.299 [ms] (mean)
|
311
|
-
Time per request: 0.037 [ms] (mean, across all concurrent requests)
|
312
|
-
Transfer rate: 1280.29 [Kbytes/sec] received
|
313
|
-
|
314
|
-
Connection Times (ms)
|
315
|
-
min mean[+/-sd] median max
|
316
|
-
Connect: 0 0 0.0 0 0
|
317
|
-
Processing: 0 0 0.2 0 6
|
318
|
-
Waiting: 0 0 0.2 0 6
|
319
|
-
Total: 0 0 0.2 0 6
|
320
|
-
|
321
|
-
Percentage of the requests served within a certain time (ms)
|
322
|
-
50% 0
|
323
|
-
66% 0
|
324
|
-
75% 0
|
325
|
-
80% 0
|
326
|
-
90% 0
|
327
|
-
95% 1
|
328
|
-
98% 1
|
329
|
-
99% 1
|
330
|
-
100% 6 (longest request)
|
331
|
-
|
332
|
-
On a 4-core 8-thread i7, running `wrk`, which uses 8 keep-alive connections:
|
333
|
-
|
334
|
-
$ wrk -c 8 -d 10 -t 8 http://127.0.0.1:9294/
|
335
|
-
Running 10s test @ http://127.0.0.1:9294/
|
336
|
-
8 threads and 8 connections
|
337
|
-
Thread Stats Avg Stdev Max +/- Stdev
|
338
|
-
Latency 217.69us 0.99ms 23.21ms 97.39%
|
339
|
-
Req/Sec 12.18k 1.58k 17.67k 83.21%
|
340
|
-
974480 requests in 10.10s, 60.41MB read
|
341
|
-
Requests/sec: 96485.00
|
342
|
-
Transfer/sec: 5.98MB
|
343
|
-
|
344
|
-
According to these results, the cost of handling connections is quite high, while general throughput seems pretty decent.
|
345
|
-
|
346
|
-
## Semantic Model
|
347
|
-
|
348
|
-
### Scheme
|
349
|
-
|
350
|
-
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.
|
351
|
-
|
352
|
-
### Version
|
353
|
-
|
354
|
-
HTTP/1 has an explicit version while HTTP/2 does not expose the version in any way.
|
9
|
+
Please see the [project documentation](https://socketry.github.io/async-http/) for more details.
|
355
10
|
|
356
|
-
|
11
|
+
- [Getting Started](https://socketry.github.io/async-http/guides/getting-started/index) - This guide explains how to get started with `Async::HTTP`.
|
357
12
|
|
358
|
-
|
13
|
+
- [Testing](https://socketry.github.io/async-http/guides/testing/index) - This guide explains how to use `Async::HTTP` clients and servers in your tests.
|
359
14
|
|
360
15
|
## Contributing
|
361
16
|
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: async-http
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.69.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
@@ -10,11 +10,12 @@ authors:
|
|
10
10
|
- Janko Marohnić
|
11
11
|
- Thomas Morgan
|
12
12
|
- Adam Daniels
|
13
|
+
- Igor Sidorov
|
13
14
|
- Anton Zhuravsky
|
14
15
|
- Cyril Roelandt
|
15
16
|
- Denis Talakevich
|
17
|
+
- Hal Brodigan
|
16
18
|
- Ian Ker-Seymer
|
17
|
-
- Igor Sidorov
|
18
19
|
- Josh Huber
|
19
20
|
- Marco Concetto Rudilosso
|
20
21
|
- Olle Jonsson
|
@@ -57,7 +58,7 @@ cert_chain:
|
|
57
58
|
Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
|
58
59
|
voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
|
59
60
|
-----END CERTIFICATE-----
|
60
|
-
date: 2024-06-
|
61
|
+
date: 2024-06-24 00:00:00.000000000 Z
|
61
62
|
dependencies:
|
62
63
|
- !ruby/object:Gem::Dependency
|
63
64
|
name: async
|
@@ -77,36 +78,30 @@ dependencies:
|
|
77
78
|
name: async-pool
|
78
79
|
requirement: !ruby/object:Gem::Requirement
|
79
80
|
requirements:
|
80
|
-
- - "
|
81
|
+
- - "~>"
|
81
82
|
- !ruby/object:Gem::Version
|
82
|
-
version: 0.
|
83
|
+
version: '0.7'
|
83
84
|
type: :runtime
|
84
85
|
prerelease: false
|
85
86
|
version_requirements: !ruby/object:Gem::Requirement
|
86
87
|
requirements:
|
87
|
-
- - "
|
88
|
+
- - "~>"
|
88
89
|
- !ruby/object:Gem::Version
|
89
|
-
version: 0.
|
90
|
+
version: '0.7'
|
90
91
|
- !ruby/object:Gem::Dependency
|
91
92
|
name: io-endpoint
|
92
93
|
requirement: !ruby/object:Gem::Requirement
|
93
94
|
requirements:
|
94
95
|
- - "~>"
|
95
96
|
- !ruby/object:Gem::Version
|
96
|
-
version: '0.
|
97
|
-
- - ">="
|
98
|
-
- !ruby/object:Gem::Version
|
99
|
-
version: 0.10.3
|
97
|
+
version: '0.11'
|
100
98
|
type: :runtime
|
101
99
|
prerelease: false
|
102
100
|
version_requirements: !ruby/object:Gem::Requirement
|
103
101
|
requirements:
|
104
102
|
- - "~>"
|
105
103
|
- !ruby/object:Gem::Version
|
106
|
-
version: '0.
|
107
|
-
- - ">="
|
108
|
-
- !ruby/object:Gem::Version
|
109
|
-
version: 0.10.3
|
104
|
+
version: '0.11'
|
110
105
|
- !ruby/object:Gem::Dependency
|
111
106
|
name: io-stream
|
112
107
|
requirement: !ruby/object:Gem::Requirement
|
@@ -127,56 +122,56 @@ dependencies:
|
|
127
122
|
requirements:
|
128
123
|
- - "~>"
|
129
124
|
- !ruby/object:Gem::Version
|
130
|
-
version: 0.26
|
125
|
+
version: '0.26'
|
131
126
|
type: :runtime
|
132
127
|
prerelease: false
|
133
128
|
version_requirements: !ruby/object:Gem::Requirement
|
134
129
|
requirements:
|
135
130
|
- - "~>"
|
136
131
|
- !ruby/object:Gem::Version
|
137
|
-
version: 0.26
|
132
|
+
version: '0.26'
|
138
133
|
- !ruby/object:Gem::Dependency
|
139
134
|
name: protocol-http1
|
140
135
|
requirement: !ruby/object:Gem::Requirement
|
141
136
|
requirements:
|
142
137
|
- - "~>"
|
143
138
|
- !ruby/object:Gem::Version
|
144
|
-
version: 0.19
|
139
|
+
version: '0.19'
|
145
140
|
type: :runtime
|
146
141
|
prerelease: false
|
147
142
|
version_requirements: !ruby/object:Gem::Requirement
|
148
143
|
requirements:
|
149
144
|
- - "~>"
|
150
145
|
- !ruby/object:Gem::Version
|
151
|
-
version: 0.19
|
146
|
+
version: '0.19'
|
152
147
|
- !ruby/object:Gem::Dependency
|
153
148
|
name: protocol-http2
|
154
149
|
requirement: !ruby/object:Gem::Requirement
|
155
150
|
requirements:
|
156
151
|
- - "~>"
|
157
152
|
- !ruby/object:Gem::Version
|
158
|
-
version: 0.18
|
153
|
+
version: '0.18'
|
159
154
|
type: :runtime
|
160
155
|
prerelease: false
|
161
156
|
version_requirements: !ruby/object:Gem::Requirement
|
162
157
|
requirements:
|
163
158
|
- - "~>"
|
164
159
|
- !ruby/object:Gem::Version
|
165
|
-
version: 0.18
|
160
|
+
version: '0.18'
|
166
161
|
- !ruby/object:Gem::Dependency
|
167
162
|
name: traces
|
168
163
|
requirement: !ruby/object:Gem::Requirement
|
169
164
|
requirements:
|
170
165
|
- - ">="
|
171
166
|
- !ruby/object:Gem::Version
|
172
|
-
version: 0.10
|
167
|
+
version: '0.10'
|
173
168
|
type: :runtime
|
174
169
|
prerelease: false
|
175
170
|
version_requirements: !ruby/object:Gem::Requirement
|
176
171
|
requirements:
|
177
172
|
- - ">="
|
178
173
|
- !ruby/object:Gem::Version
|
179
|
-
version: 0.10
|
174
|
+
version: '0.10'
|
180
175
|
description:
|
181
176
|
email:
|
182
177
|
executables: []
|
@@ -196,6 +191,8 @@ files:
|
|
196
191
|
- lib/async/http/endpoint.rb
|
197
192
|
- lib/async/http/internet.rb
|
198
193
|
- lib/async/http/internet/instance.rb
|
194
|
+
- lib/async/http/mock.rb
|
195
|
+
- lib/async/http/mock/endpoint.rb
|
199
196
|
- lib/async/http/protocol.rb
|
200
197
|
- lib/async/http/protocol/http.rb
|
201
198
|
- lib/async/http/protocol/http1.rb
|
@@ -247,7 +244,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
247
244
|
- !ruby/object:Gem::Version
|
248
245
|
version: '0'
|
249
246
|
requirements: []
|
250
|
-
rubygems_version: 3.5.
|
247
|
+
rubygems_version: 3.5.11
|
251
248
|
signing_key:
|
252
249
|
specification_version: 4
|
253
250
|
summary: A HTTP client and server library.
|
metadata.gz.sig
CHANGED
Binary file
|