async-http 0.91.0 → 0.92.0

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: ae6c0e0cd15050304be680a8c0b5e49447fb83c6533b7aefebec427a83ae3aac
4
- data.tar.gz: 614910bd99032d2f19672876eada276fba03a887feafec21f3a50263410f272e
3
+ metadata.gz: 913107bd5ebd3ac637bcaea82f20b46069f0a5bf15f128abb1c3ec7e93c23387
4
+ data.tar.gz: c4b20ec61acafd0572e744aadac9b3878f2052a8c5057aabfafbf983a15b79e3
5
5
  SHA512:
6
- metadata.gz: 301c38b5c92a927d11d73420e88a47442e3dbcc7445850bb269412dc1c2c4edf3dd97456e8bbbed13de1fa013dec139652b274248ae882e294ce48912d3935e9
7
- data.tar.gz: 6e2dc40b2761b4484191368b2ebadea5045d1d35c28306bbd09b97d5433badeb1c5e31a1c9af54222e7583377488bdcf4f300b545882d7ed1d303c2470c13716
6
+ metadata.gz: f51b80df07971626284803c186ff44dc8367422bd0833952b176d30418f1c0d43aea66deebc5d1d5d444da833ac655ba230c7ac4c8c98797c233c8a161228519
7
+ data.tar.gz: 59ba90830afaa9da4f72c4719abdae629a26cfe9fd218714f88f02bec6d2654a81b2935c75baf2d782b8470726afc6b5b5bd15a51cfd4e70d677bf661413800b
checksums.yaml.gz.sig CHANGED
Binary file
@@ -0,0 +1,148 @@
1
+ # Getting Started
2
+
3
+ This guide explains how to get started with `Async::HTTP`.
4
+
5
+ ## Installation
6
+
7
+ Add the gem to your project:
8
+
9
+ ~~~ bash
10
+ $ bundle add async-http
11
+ ~~~
12
+
13
+ ## Core Concepts
14
+
15
+ - {ruby Async::HTTP::Client} is the main class for making HTTP requests.
16
+ - {ruby Async::HTTP::Internet} provides a simple interface for making requests to any server "on the internet".
17
+ - {ruby Async::HTTP::Server} is the main class for handling HTTP requests.
18
+ - {ruby Async::HTTP::Endpoint} can parse HTTP URLs in order to create a client or server.
19
+ - [`protocol-http`](https://github.com/socketry/protocol-http) provides the abstract HTTP protocol interfaces.
20
+
21
+ ## Usage
22
+
23
+ ### Making a Request
24
+
25
+ To make a request, use {ruby Async::HTTP::Internet} and call the appropriate method:
26
+
27
+ ~~~ ruby
28
+ require 'async/http/internet/instance'
29
+
30
+ Sync do
31
+ Async::HTTP::Internet.get("https://httpbin.org/get") do |response|
32
+ puts response.read
33
+ end
34
+ end
35
+ ~~~
36
+
37
+ The following methods are supported:
38
+
39
+ ~~~ ruby
40
+ Async::HTTP::Internet.methods(false)
41
+ # => [:patch, :options, :connect, :post, :get, :delete, :head, :trace, :put]
42
+ ~~~
43
+
44
+ Using a block will automatically close the response when the block completes. If you want to keep the response open, you can manage it manually:
45
+
46
+ ~~~ ruby
47
+ require 'async/http/internet/instance'
48
+
49
+ Sync do
50
+ response = Async::HTTP::Internet.get("https://httpbin.org/get")
51
+ puts response.read
52
+ ensure
53
+ response&.close
54
+ end
55
+ ~~~
56
+
57
+ As responses are streamed, you must ensure it is closed when you are finished with it.
58
+
59
+ #### Persistence
60
+
61
+ By default, {ruby Async::HTTP::Internet} will create a {ruby Async::HTTP::Client} for each remote host you communicate with, and will keep those connections open for as long as possible. This is useful for reducing the latency of subsequent requests to the same host. When you exit the event loop, the connections will be closed automatically.
62
+
63
+ ### Downloading a File
64
+
65
+ ~~~ ruby
66
+ require 'async/http/internet/instance'
67
+
68
+ Sync do
69
+ # Issue a GET request to Google:
70
+ response = Async::HTTP::Internet.get("https://www.google.com/search?q=kittens")
71
+
72
+ # Save the response body to a local file:
73
+ response.save("/tmp/search.html")
74
+ ensure
75
+ response&.close
76
+ end
77
+ ~~~
78
+
79
+ ### Posting Data
80
+
81
+ To post data, use the `post` method:
82
+
83
+ ~~~ ruby
84
+ require 'async/http/internet/instance'
85
+
86
+ data = {'life' => 42}
87
+
88
+ Sync do
89
+ # Prepare the request:
90
+ headers = [['accept', 'application/json']]
91
+ body = JSON.dump(data)
92
+
93
+ # Issues a POST request:
94
+ response = Async::HTTP::Internet.post("https://httpbin.org/anything", headers, body)
95
+
96
+ # Save the response body to a local file:
97
+ pp JSON.parse(response.read)
98
+ ensure
99
+ response&.close
100
+ end
101
+ ~~~
102
+
103
+ For more complex scenarios, including HTTP APIs, consider using [async-rest](https://github.com/socketry/async-rest) instead.
104
+
105
+ ### Timeouts
106
+
107
+ To set a timeout for a request, use the `Task#with_timeout` method:
108
+
109
+ ~~~ ruby
110
+ require 'async/http/internet/instance'
111
+
112
+ Sync do |task|
113
+ # Request will timeout after 2 seconds
114
+ task.with_timeout(2) do
115
+ response = Async::HTTP::Internet.get "https://httpbin.org/delay/10"
116
+ ensure
117
+ response&.close
118
+ end
119
+ rescue Async::TimeoutError
120
+ puts "The request timed out"
121
+ end
122
+ ~~~
123
+
124
+ ### Making a Server
125
+
126
+ To create a server, use an instance of {ruby Async::HTTP::Server}:
127
+
128
+ ~~~ ruby
129
+ require 'async/http'
130
+
131
+ endpoint = Async::HTTP::Endpoint.parse('http://localhost:9292')
132
+
133
+ Sync do |task|
134
+ Async(transient: true) do
135
+ server = Async::HTTP::Server.for(endpoint) do |request|
136
+ ::Protocol::HTTP::Response[200, {}, ["Hello World"]]
137
+ end
138
+
139
+ server.run
140
+ end
141
+
142
+ client = Async::HTTP::Client.new(endpoint)
143
+ response = client.get("/")
144
+ puts response.read
145
+ ensure
146
+ response&.close
147
+ end
148
+ ~~~
@@ -0,0 +1,15 @@
1
+ # Automatically generated context index for Utopia::Project guides.
2
+ # Do not edit then files in this directory directly, instead edit the guides and then run `bake utopia:project:agent:context:update`.
3
+ ---
4
+ description: A HTTP client and server library.
5
+ metadata:
6
+ documentation_uri: https://socketry.github.io/async-http/
7
+ source_code_uri: https://github.com/socketry/async-http.git
8
+ files:
9
+ - path: getting-started.md
10
+ title: Getting Started
11
+ description: This guide explains how to get started with `Async::HTTP`.
12
+ - path: testing.md
13
+ title: Testing
14
+ description: This guide explains how to use `Async::HTTP` clients and servers in
15
+ your tests.
@@ -0,0 +1,77 @@
1
+ # Testing
2
+
3
+ This guide explains how to use `Async::HTTP` clients and servers in your tests.
4
+
5
+ In general, you should avoid making real HTTP requests in your tests. Instead, you should use a mock server or a fake client.
6
+
7
+ ## Mocking HTTP Responses
8
+
9
+ The mocking feature of `Async::HTTP` uses a real server running in a separate task, and routes all requests to it. This allows you to intercept requests and return custom responses, but still use the real HTTP client.
10
+
11
+ In order to enable this feature, you must create an instance of {ruby Async::HTTP::Mock::Endpoint} which will handle the requests.
12
+
13
+ ~~~ ruby
14
+ require 'async/http'
15
+ require 'async/http/mock'
16
+
17
+ mock_endpoint = Async::HTTP::Mock::Endpoint.new
18
+
19
+ Sync do
20
+ # Start a background server:
21
+ server_task = Async(transient: true) do
22
+ mock_endpoint.run do |request|
23
+ # Respond to the request:
24
+ ::Protocol::HTTP::Response[200, {}, ["Hello, World"]]
25
+ end
26
+ end
27
+
28
+ endpoint = Async::HTTP::Endpoint.parse("https://www.google.com")
29
+ mocked_endpoint = mock_endpoint.wrap(endpoint)
30
+ client = Async::HTTP::Client.new(mocked_endpoint)
31
+
32
+ response = client.get("/")
33
+ puts response.read
34
+ # => "Hello, World"
35
+ end
36
+ ~~~
37
+
38
+ ## Transparent Mocking
39
+
40
+ Using your test framework's mocking capabilities, you can easily replace the `Async::HTTP::Client#new` with a method that returns a client with a mocked endpoint.
41
+
42
+ ### Sus Integration
43
+
44
+ ~~~ ruby
45
+ require 'async/http'
46
+ require 'async/http/mock'
47
+ require 'sus/fixtures/async/reactor_context'
48
+
49
+ include Sus::Fixtures::Async::ReactorContext
50
+
51
+ let(:mock_endpoint) {Async::HTTP::Mock::Endpoint.new}
52
+
53
+ def before
54
+ super
55
+
56
+ # Mock the HTTP client:
57
+ mock(Async::HTTP::Client) do |mock|
58
+ mock.wrap(:new) do |original, endpoint|
59
+ original.call(mock_endpoint.wrap(endpoint))
60
+ end
61
+ end
62
+
63
+ # Run the mock server:
64
+ Async(transient: true) do
65
+ mock_endpoint.run do |request|
66
+ ::Protocol::HTTP::Response[200, {}, ["Hello, World"]]
67
+ end
68
+ end
69
+ end
70
+
71
+ it "should perform a web request" do
72
+ client = Async::HTTP::Client.new(Async::HTTP::Endpoint.parse("https://www.google.com"))
73
+ response = client.get("/")
74
+ # The response is mocked:
75
+ expect(response.read).to be == "Hello, World"
76
+ end
77
+ ~~~
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2019-2024, by Samuel Williams.
4
+ # Copyright, 2019-2025, by Samuel Williams.
5
5
  # Copyright, 2020, by Bruno Sutic.
6
6
 
7
7
  require_relative "writable"
@@ -6,6 +6,7 @@
6
6
  # Copyright, 2024, by Thomas Morgan.
7
7
  # Copyright, 2024, by Igor Sidorov.
8
8
  # Copyright, 2024, by Hal Brodigan.
9
+ # Copyright, 2025, by William T. Nelson.
9
10
 
10
11
  require "io/endpoint"
11
12
  require "io/endpoint/host_endpoint"
@@ -16,9 +17,6 @@ require_relative "protocol/https"
16
17
 
17
18
  require "uri"
18
19
 
19
- # Compatibility with Ruby 3.1.2
20
- require "uri/wss"
21
-
22
20
  module Async
23
21
  module HTTP
24
22
  # Represents a way to connect to a remote HTTP server.
@@ -1,12 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2024, by Samuel Williams.
5
-
6
- require_relative "../reference"
4
+ # Copyright, 2024-2025, by Samuel Williams.
7
5
 
8
6
  require "protocol/http/middleware"
9
7
  require "protocol/http/body/rewindable"
8
+ require "protocol/url/reference"
10
9
 
11
10
  module Async
12
11
  module HTTP
@@ -71,7 +70,7 @@ module Async
71
70
  end
72
71
 
73
72
  # Update the path of the request:
74
- request.path = Reference[request.path] + location
73
+ request.path = ::Protocol::URL::Reference[request.path] + location
75
74
 
76
75
  # Follow the redirect:
77
76
  return true
@@ -151,8 +151,6 @@ module Async
151
151
  # Do not remove this line or you will unleash the gods of concurrency hell.
152
152
  task.yield
153
153
  end
154
- rescue => error
155
- raise
156
154
  ensure
157
155
  body&.close(error)
158
156
  end
@@ -5,6 +5,6 @@
5
5
 
6
6
  module Async
7
7
  module HTTP
8
- VERSION = "0.91.0"
8
+ VERSION = "0.92.0"
9
9
  end
10
10
  end
@@ -9,7 +9,7 @@ require "metrics/provider"
9
9
  Metrics::Provider(Async::HTTP::Client) do
10
10
  ASYNC_HTTP_CLIENT_REQUEST_INITIATED = Metrics.metric("async.http.client.request.initiated", :counter, description: "The number of HTTP client requests initiated.")
11
11
  ASYNC_HTTP_CLIENT_REQUEST_FINISHED = Metrics.metric("async.http.client.request.finished", :counter, description: "The number of HTTP client requests finished.")
12
-
12
+
13
13
  def make_response(request, connection, attempt)
14
14
  ASYNC_HTTP_CLIENT_REQUEST_INITIATED.emit(1, tags: ["method:#{request.method}"])
15
15
 
@@ -9,7 +9,7 @@ require "metrics/provider"
9
9
  Metrics::Provider(Async::HTTP::Server) do
10
10
  ASYNC_HTTP_SERVER_REQUEST_INITIATED = Metrics.metric("async.http.server.request.initiated", :counter, description: "The number of HTTP server requests initiated.")
11
11
  ASYNC_HTTP_SERVER_REQUEST_FINISHED = Metrics.metric("async.http.server.request.finished", :counter, description: "The number of HTTP server requests finished.")
12
-
12
+
13
13
  def call(request)
14
14
  ASYNC_HTTP_SERVER_REQUEST_INITIATED.emit(1, tags: ["method:#{request.method}"])
15
15
 
@@ -1,4 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Released under the MIT License.
4
+ # Copyright, 2025, by Samuel Williams.
5
+
3
6
  require_relative "http/client"
4
7
  require_relative "http/server"
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2025, by Samuel Williams.
5
+
1
6
  require_relative "../../../../async/http/client"
2
7
 
3
8
  Traces::Provider(Async::HTTP::Client) do
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2025, by Samuel Williams.
5
+
1
6
  require_relative "../../../../../../async/http/protocol/http1/client"
2
7
 
3
8
  Traces::Provider(Async::HTTP::Protocol::HTTP1::Client) do
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2025, by Samuel Williams.
5
+
1
6
  require_relative "../../../../../../async/http/protocol/http2/client"
2
7
 
3
8
  Traces::Provider(Async::HTTP::Protocol::HTTP2::Client) do
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2025, by Samuel Williams.
5
+
1
6
  require_relative "../../../../async/http/server"
2
7
 
3
8
  Traces::Provider(Async::HTTP::Server) do
@@ -35,4 +40,4 @@ Traces::Provider(Async::HTTP::Server) do
35
40
  end
36
41
  end
37
42
  end
38
- end
43
+ end
@@ -1,2 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2025, by Samuel Williams.
5
+
1
6
  require_relative "http/client"
2
- require_relative "http/server"
7
+ require_relative "http/server"
data/license.md CHANGED
@@ -23,6 +23,8 @@ Copyright, 2023, by Josh Huber.
23
23
  Copyright, 2024, by Anton Zhuravsky.
24
24
  Copyright, 2024, by Hal Brodigan.
25
25
  Copyright, 2025, by Jean Boussier.
26
+ Copyright, 2025, by Yuuji Yaginuma.
27
+ Copyright, 2025, by William T. Nelson.
26
28
 
27
29
  Permission is hereby granted, free of charge, to any person obtaining a copy
28
30
  of this software and associated documentation files (the "Software"), to deal
data/readme.md CHANGED
@@ -16,6 +16,10 @@ Please see the [project documentation](https://socketry.github.io/async-http/) f
16
16
 
17
17
  Please see the [project releases](https://socketry.github.io/async-http/releases/index) for all releases.
18
18
 
19
+ ### v0.92.0
20
+
21
+ - **Breaking**: Remove `Async::HTTP::Reference`. Use `Protocol::URL::Reference` directly instead.
22
+
19
23
  ### v0.91.0
20
24
 
21
25
  - Move all default trace providers into `traces/provider/async/http`.
@@ -53,12 +57,6 @@ Please see the [project releases](https://socketry.github.io/async-http/releases
53
57
  - Improved HTTP/1 connection handling.
54
58
  - The input stream is no longer closed when the output stream is closed.
55
59
 
56
- ### v0.76.0
57
-
58
- - `Async::HTTP::Body::Writable` is moved to `Protocol::HTTP::Body::Writable`.
59
- - Remove `Async::HTTP::Body::Delayed` with no replacement.
60
- - Remove `Async::HTTP::Body::Slowloris` with no replacement.
61
-
62
60
  ## See Also
63
61
 
64
62
  - [benchmark-http](https://github.com/socketry/benchmark-http) — A benchmarking tool to report on web server concurrency.
data/releases.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Releases
2
2
 
3
+ ## v0.92.0
4
+
5
+ - **Breaking**: Remove `Async::HTTP::Reference`. Use `Protocol::URL::Reference` directly instead.
6
+
3
7
  ## v0.91.0
4
8
 
5
9
  - Move all default trace providers into `traces/provider/async/http`.
@@ -145,7 +149,7 @@ internet.get("https://example.com", scheme: "http")
145
149
  ``` ruby
146
150
  # Server side:
147
151
  def call(request)
148
- if request.headers['expect'].include?('100-continue')
152
+ if request.headers["expect"].include?("100-continue")
149
153
  request.send_interim_response(100)
150
154
  end
151
155
 
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.91.0
4
+ version: 0.92.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
@@ -11,6 +11,7 @@ authors:
11
11
  - Thomas Morgan
12
12
  - Adam Daniels
13
13
  - Igor Sidorov
14
+ - William T. Nelson
14
15
  - Anton Zhuravsky
15
16
  - Cyril Roelandt
16
17
  - Denis Talakevich
@@ -26,6 +27,7 @@ authors:
26
27
  - Tim Meusel
27
28
  - Trevor Turk
28
29
  - Viacheslav Koval
30
+ - Yuuji Yaginuma
29
31
  - dependabot[bot]
30
32
  bindir: bin
31
33
  cert_chain:
@@ -172,6 +174,20 @@ dependencies:
172
174
  - - "~>"
173
175
  - !ruby/object:Gem::Version
174
176
  version: '0.22'
177
+ - !ruby/object:Gem::Dependency
178
+ name: protocol-url
179
+ requirement: !ruby/object:Gem::Requirement
180
+ requirements:
181
+ - - "~>"
182
+ - !ruby/object:Gem::Version
183
+ version: '0.2'
184
+ type: :runtime
185
+ prerelease: false
186
+ version_requirements: !ruby/object:Gem::Requirement
187
+ requirements:
188
+ - - "~>"
189
+ - !ruby/object:Gem::Version
190
+ version: '0.2'
175
191
  - !ruby/object:Gem::Dependency
176
192
  name: traces
177
193
  requirement: !ruby/object:Gem::Requirement
@@ -192,6 +208,9 @@ extra_rdoc_files: []
192
208
  files:
193
209
  - bake/async/http.rb
194
210
  - bake/async/http/h2spec.rb
211
+ - context/getting-started.md
212
+ - context/index.yaml
213
+ - context/testing.md
195
214
  - lib/async/http.rb
196
215
  - lib/async/http/body.rb
197
216
  - lib/async/http/body/hijack.rb
@@ -230,7 +249,6 @@ files:
230
249
  - lib/async/http/protocol/request.rb
231
250
  - lib/async/http/protocol/response.rb
232
251
  - lib/async/http/proxy.rb
233
- - lib/async/http/reference.rb
234
252
  - lib/async/http/relative_location.rb
235
253
  - lib/async/http/server.rb
236
254
  - lib/async/http/statistics.rb
@@ -266,7 +284,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
266
284
  - !ruby/object:Gem::Version
267
285
  version: '0'
268
286
  requirements: []
269
- rubygems_version: 3.6.9
287
+ rubygems_version: 3.7.2
270
288
  specification_version: 4
271
289
  summary: A HTTP client and server library.
272
290
  test_files: []
metadata.gz.sig CHANGED
Binary file
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Released under the MIT License.
4
- # Copyright, 2018-2024, by Samuel Williams.
5
-
6
- require "protocol/http/reference"
7
-
8
- module Async
9
- module HTTP
10
- Reference = ::Protocol::HTTP::Reference
11
- end
12
- end