async-http 0.60.1 → 0.60.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/bake/async/http/h2spec.rb +4 -0
  4. data/bake/async/http.rb +4 -0
  5. data/lib/async/http/body/delayed.rb +4 -20
  6. data/lib/async/http/body/hijack.rb +3 -20
  7. data/lib/async/http/body/pipe.rb +4 -20
  8. data/lib/async/http/body/slowloris.rb +3 -20
  9. data/lib/async/http/body/writable.rb +3 -20
  10. data/lib/async/http/body.rb +3 -20
  11. data/lib/async/http/client.rb +6 -22
  12. data/lib/async/http/endpoint.rb +8 -20
  13. data/lib/async/http/internet/instance.rb +3 -20
  14. data/lib/async/http/internet.rb +3 -20
  15. data/lib/async/http/protocol/http1/client.rb +3 -20
  16. data/lib/async/http/protocol/http1/connection.rb +3 -20
  17. data/lib/async/http/protocol/http1/request.rb +3 -20
  18. data/lib/async/http/protocol/http1/response.rb +3 -20
  19. data/lib/async/http/protocol/http1/server.rb +4 -20
  20. data/lib/async/http/protocol/http1.rb +3 -20
  21. data/lib/async/http/protocol/http10.rb +3 -20
  22. data/lib/async/http/protocol/http11.rb +4 -20
  23. data/lib/async/http/protocol/http2/client.rb +3 -20
  24. data/lib/async/http/protocol/http2/connection.rb +4 -20
  25. data/lib/async/http/protocol/http2/input.rb +3 -20
  26. data/lib/async/http/protocol/http2/output.rb +3 -20
  27. data/lib/async/http/protocol/http2/request.rb +3 -20
  28. data/lib/async/http/protocol/http2/response.rb +3 -20
  29. data/lib/async/http/protocol/http2/server.rb +3 -20
  30. data/lib/async/http/protocol/http2/stream.rb +4 -20
  31. data/lib/async/http/protocol/http2.rb +3 -20
  32. data/lib/async/http/protocol/https.rb +4 -20
  33. data/lib/async/http/protocol/request.rb +3 -20
  34. data/lib/async/http/protocol/response.rb +3 -20
  35. data/lib/async/http/protocol.rb +3 -20
  36. data/lib/async/http/proxy.rb +3 -20
  37. data/lib/async/http/reference.rb +3 -20
  38. data/lib/async/http/relative_location.rb +5 -19
  39. data/lib/async/http/server.rb +6 -22
  40. data/lib/async/http/statistics.rb +3 -20
  41. data/lib/async/http/version.rb +4 -21
  42. data/lib/async/http.rb +3 -20
  43. data/license.md +37 -0
  44. data/readme.md +376 -0
  45. data.tar.gz.sig +0 -0
  46. metadata +10 -7
  47. metadata.gz.sig +0 -0
data/readme.md ADDED
@@ -0,0 +1,376 @@
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](https://github.com/socketry/async) and [async-io](https://github.com/socketry/async-io). [falcon](https://github.com/socketry/falcon) provides a rack-compatible server.
4
+
5
+ [![Development Status](https://github.com/socketry/async-http/workflows/Test/badge.svg)](https://github.com/socketry/async-http/actions?workflow=Test)
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
+ ## Usage
24
+
25
+ Please see the [project documentation](https://socketry.github.io/async-http/) or serve it locally using `bake utopia:project:serve`.
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.
355
+
356
+ ### Reason
357
+
358
+ HTTP/1 responses contain a reason field which is largely irrelevant. HTTP/2 does not support this field.
359
+
360
+ ## Contributing
361
+
362
+ We welcome contributions to this project.
363
+
364
+ 1. Fork it.
365
+ 2. Create your feature branch (`git checkout -b my-new-feature`).
366
+ 3. Commit your changes (`git commit -am 'Add some feature'`).
367
+ 4. Push to the branch (`git push origin my-new-feature`).
368
+ 5. Create new Pull Request.
369
+
370
+ ## See Also
371
+
372
+ - [benchmark-http](https://github.com/socketry/benchmark-http) — A benchmarking tool to report on web server concurrency.
373
+ - [falcon](https://github.com/socketry/falcon) — A rack compatible server built on top of `async-http`.
374
+ - [async-websocket](https://github.com/socketry/async-websocket) — Asynchronous client and server websockets.
375
+ - [async-rest](https://github.com/socketry/async-rest) — A RESTful resource layer built on top of `async-http`.
376
+ - [async-http-faraday](https://github.com/socketry/async-http-faraday) — A faraday adapter to use `async-http`.
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.60.1
4
+ version: 0.60.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
@@ -16,10 +16,11 @@ authors:
16
16
  - Marco Concetto Rudilosso
17
17
  - Olle Jonsson
18
18
  - Orgad Shaneh
19
+ - Sam Shadwell
19
20
  - Stefan Wrobel
20
- - TheAthlete
21
+ - Tim Meusel
21
22
  - Trevor Turk
22
- - samshadwell
23
+ - Viacheslav Koval
23
24
  autorequire:
24
25
  bindir: bin
25
26
  cert_chain:
@@ -52,7 +53,7 @@ cert_chain:
52
53
  Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
53
54
  voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
54
55
  -----END CERTIFICATE-----
55
- date: 2023-02-03 00:00:00.000000000 Z
56
+ date: 2023-06-19 00:00:00.000000000 Z
56
57
  dependencies:
57
58
  - !ruby/object:Gem::Dependency
58
59
  name: async
@@ -144,14 +145,14 @@ dependencies:
144
145
  requirements:
145
146
  - - ">="
146
147
  - !ruby/object:Gem::Version
147
- version: 0.8.0
148
+ version: 0.10.0
148
149
  type: :runtime
149
150
  prerelease: false
150
151
  version_requirements: !ruby/object:Gem::Requirement
151
152
  requirements:
152
153
  - - ">="
153
154
  - !ruby/object:Gem::Version
154
- version: 0.8.0
155
+ version: 0.10.0
155
156
  - !ruby/object:Gem::Dependency
156
157
  name: async-container
157
158
  requirement: !ruby/object:Gem::Requirement
@@ -282,6 +283,8 @@ files:
282
283
  - lib/async/http/server.rb
283
284
  - lib/async/http/statistics.rb
284
285
  - lib/async/http/version.rb
286
+ - license.md
287
+ - readme.md
285
288
  homepage: https://github.com/socketry/async-http
286
289
  licenses:
287
290
  - MIT
@@ -301,7 +304,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
301
304
  - !ruby/object:Gem::Version
302
305
  version: '0'
303
306
  requirements: []
304
- rubygems_version: 3.4.1
307
+ rubygems_version: 3.4.7
305
308
  signing_key:
306
309
  specification_version: 4
307
310
  summary: A HTTP client and server library.
metadata.gz.sig CHANGED
Binary file