async-http 0.101.0 → 0.102.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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/context/choosing-a-client.md +164 -0
  4. data/context/concurrent-requests.md +134 -0
  5. data/context/getting-started.md +106 -69
  6. data/context/index.yaml +14 -3
  7. data/context/testing.md +173 -45
  8. data/lib/async/http/body/hijack.rb +1 -1
  9. data/lib/async/http/body/pipe.rb +2 -2
  10. data/lib/async/http/body.rb +1 -1
  11. data/lib/async/http/internet.rb +1 -1
  12. data/lib/async/http/middleware/location_redirector.rb +1 -1
  13. data/lib/async/http/mock/endpoint.rb +1 -1
  14. data/lib/async/http/protocol/configurable.rb +1 -1
  15. data/lib/async/http/protocol/http.rb +1 -1
  16. data/lib/async/http/protocol/http1/finishable.rb +2 -1
  17. data/lib/async/http/protocol/http1/request.rb +1 -1
  18. data/lib/async/http/protocol/http1/response.rb +1 -1
  19. data/lib/async/http/protocol/http1.rb +1 -1
  20. data/lib/async/http/protocol/http10.rb +1 -1
  21. data/lib/async/http/protocol/http11.rb +1 -1
  22. data/lib/async/http/protocol/http2/client.rb +7 -2
  23. data/lib/async/http/protocol/http2/connection.rb +20 -6
  24. data/lib/async/http/protocol/http2/input.rb +14 -3
  25. data/lib/async/http/protocol/http2/output.rb +23 -8
  26. data/lib/async/http/protocol/http2/response.rb +15 -2
  27. data/lib/async/http/protocol/http2/server.rb +1 -1
  28. data/lib/async/http/protocol/http2/stream.rb +61 -4
  29. data/lib/async/http/protocol/http2.rb +1 -1
  30. data/lib/async/http/protocol/request.rb +1 -1
  31. data/lib/async/http/protocol/response.rb +1 -1
  32. data/lib/async/http/proxy.rb +1 -1
  33. data/lib/async/http/statistics.rb +1 -1
  34. data/lib/async/http/version.rb +1 -1
  35. data/lib/async/http.rb +1 -1
  36. data/lib/traces/provider/async/http/client.rb +1 -1
  37. data/lib/traces/provider/async/http/protocol/http1/client.rb +1 -1
  38. data/lib/traces/provider/async/http/protocol/http2/client.rb +1 -1
  39. data/lib/traces/provider/async/http/server.rb +1 -1
  40. data/license.md +2 -1
  41. data/readme.md +18 -13
  42. data/releases.md +5 -0
  43. data.tar.gz.sig +0 -0
  44. metadata +11 -6
  45. metadata.gz.sig +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 397ffac9d60c7ff7d3524624cffdfb5105a2cdc1e7a540cc8b282824b5bf890a
4
- data.tar.gz: fd2cc4a962f28ddb8cb79f4035315816b9a7dd5785e76494b764764f3dd51ef6
3
+ metadata.gz: 39dbd69fca4e67586111ac9fc21231b0a4aff0ade936468c8851707004d9c456
4
+ data.tar.gz: c10435cb4d54c2220170624931c87ed77302eb01ff8c9b70a89c22bc56c551cb
5
5
  SHA512:
6
- metadata.gz: 771358b08bcf8ceba72af908b5b5f3d78be7a3751a7051d6350132bb9e942809054da7b2b205169df6eb9e26a49a873743b187df9d0a7d49bc43166db9769519
7
- data.tar.gz: 998d6fcc02cf5b56f5883d6c26cf3e4a5ca009e2fa1234037c9c2fe4ccabc6262c9b6ede32778a98aabf5e1fd1f9ed3cdae7c9f8613b96338df8bf84ebd36b0c
6
+ metadata.gz: 0367722b925fb45a0517f1890aac1b724aa025d9db58843f034e8b9cc03ffb18044398a442b6ffff92cd4e0ac0a04773730c22384a21640521865e4dd3858993
7
+ data.tar.gz: 7602b933368edfdbc7e812fab78193a5436ed7a95de8214ecc3ba42618e7c20c2b26c10309c6609952381992387c23d2f4a5ff30b7bff25dd4a315055fd5d24d
checksums.yaml.gz.sig CHANGED
Binary file
@@ -0,0 +1,164 @@
1
+ # Choosing a Client
2
+
3
+ This guide explains how to choose between ruby:`Async::HTTP::Internet`, ruby:`Async::HTTP::Client`, and higher-level interfaces for libraries.
4
+
5
+ ruby:`Async::HTTP::Internet` and ruby:`Async::HTTP::Client` use the same request and response model. The important differences are how destinations are selected, where connection settings are applied, and who owns the client life cycle.
6
+
7
+ ## Quick Decision
8
+
9
+ | Situation | Interface | Why |
10
+ | --- | --- | --- |
11
+ | Requests may target different origins and the defaults are suitable. | Shared ruby:`Async::HTTP::Internet` | Selects and reuses a client for each origin automatically. |
12
+ | Requests may target different origins, but need common client options or explicit ownership. | Explicit ruby:`Async::HTTP::Internet` | Applies the same options to each managed client and can be injected or closed early. |
13
+ | Requests repeatedly target one configured origin. | ruby:`Async::HTTP::Client` | Exposes the endpoint, protocol, retry, and connection-pool configuration directly. |
14
+ | A library wraps one HTTP service directly. | Injected ruby:`Async::HTTP::Client` or `Protocol::HTTP` middleware | Leaves transport configuration, ownership, and testing under application control. |
15
+ | A library models an HTTP API as resources and representations. | [`async-rest`](https://socketry.github.io/async-rest/guides/getting-started/) | Provides higher-level API modeling over an injectable `Protocol::HTTP` delegate. |
16
+ | A library uses Faraday as its HTTP abstraction. | [`async-http-faraday`](https://socketry.github.io/async-http-faraday/guides/getting-started/) | Lets the application retain the Faraday interface while using `Async::HTTP` as the transport. |
17
+
18
+ Application code can start with the shared `Internet` interface unless it has a specific ownership or configuration requirement. Library code should accept an explicit HTTP dependency.
19
+
20
+ ## Shared Internet for General Requests
21
+
22
+ The shared `Internet` interface is the simplest choice for requests to arbitrary URLs. It maintains one client for each origin and reuses persistent connections:
23
+
24
+ ~~~ ruby
25
+ require "async/http/internet/instance"
26
+
27
+ urls = [
28
+ "https://www.ruby-lang.org/en/",
29
+ "https://example.com/",
30
+ ]
31
+
32
+ Sync do
33
+ urls.each do |url|
34
+ Async::HTTP::Internet.get(url) do |response|
35
+ puts "#{url}: #{response.status}"
36
+ end
37
+ end
38
+ end
39
+ ~~~
40
+
41
+ The class-level interface uses a thread-local `Internet` instance. Its connection pools are bound to the event loop and close when that event loop exits. The response block closes each response after it is processed.
42
+
43
+ Use the shared interface when:
44
+
45
+ - The application requests URLs from multiple or dynamically selected origins.
46
+ - Default retry and connection-pool settings are suitable.
47
+ - The client does not need to be injected as an application dependency.
48
+
49
+ ## Explicit Internet for Shared Configuration
50
+
51
+ An explicit `Internet` provides the same per-origin client selection while making ownership and client options visible. Options are passed to every client it creates; for example, `limit` applies independently to the pool for each origin:
52
+
53
+ ~~~ ruby
54
+ require "async/http/internet"
55
+
56
+ Sync do
57
+ internet = Async::HTTP::Internet.new(retries: 1, limit: 4)
58
+
59
+ begin
60
+ internet.get("https://www.ruby-lang.org/en/") do |response|
61
+ puts response.status
62
+ end
63
+ ensure
64
+ internet.close
65
+ end
66
+ end
67
+ ~~~
68
+
69
+ Use an explicit `Internet` when:
70
+
71
+ - Several origins should share the same retry or pool settings.
72
+ - The client should be injected into another object or replaced during testing.
73
+ - Connections should be released before the event loop exits.
74
+
75
+ ## Client for One Endpoint
76
+
77
+ A `Client` targets one ruby:`Async::HTTP::Endpoint`. Use it when a remote service is a stable part of the application architecture and needs its own protocol, TLS, retry, or pool configuration:
78
+
79
+ ~~~ ruby
80
+ require "async/http"
81
+
82
+ endpoint = Async::HTTP::Endpoint.parse("https://httpbin.org")
83
+
84
+ Sync do
85
+ Async::HTTP::Client.open(endpoint, retries: 1, limit: 4) do |client|
86
+ response = client.get("/status/200")
87
+
88
+ begin
89
+ puts response.status
90
+ ensure
91
+ response.close
92
+ end
93
+ end
94
+ end
95
+ ~~~
96
+
97
+ Client convenience methods accept a path rather than a complete URL. They return a response that the caller must close. `Client.open` closes the client and its connection pool when the block exits.
98
+
99
+ Reuse a client for repeated requests rather than creating one per request; otherwise the application cannot benefit from persistent connections.
100
+
101
+ ## Building a Library That Makes HTTP Requests
102
+
103
+ A library should generally accept its HTTP client as an explicit dependency. This lets the application configure connection limits, retries, proxies, instrumentation, and test doubles without the library creating hidden global state:
104
+
105
+ ~~~ ruby
106
+ require "async/http"
107
+
108
+ class StatusService
109
+ def initialize(client)
110
+ @client = client
111
+ end
112
+
113
+ def healthy?
114
+ response = @client.get("/status/200")
115
+ response.status == 200
116
+ ensure
117
+ response&.close
118
+ end
119
+ end
120
+
121
+ endpoint = Async::HTTP::Endpoint.parse("https://httpbin.org")
122
+
123
+ Sync do
124
+ Async::HTTP::Client.open(endpoint) do |client|
125
+ puts StatusService.new(client).healthy?
126
+ end
127
+ end
128
+ ~~~
129
+
130
+ The library does not close an injected client because the caller owns it and may share it with other components. If the library also provides an `open` convenience method that constructs a client, that method should close the client it creates when its block exits.
131
+
132
+ Define the accepted interface precisely. A ruby:`Async::HTTP::Client` is bound to one endpoint and its convenience methods accept relative paths, while ruby:`Async::HTTP::Internet` selects an endpoint from a complete URL. They should not be treated as interchangeable merely because both provide methods such as `get`. If the library constructs ruby:`Protocol::HTTP::Request` objects and only calls `call`, it can accept a `Protocol::HTTP` middleware delegate instead of requiring a concrete client.
133
+
134
+ ## Modeling Resources with async-rest
135
+
136
+ Use [`async-rest`](https://socketry.github.io/async-rest/guides/getting-started/) when a library benefits from modeling a remote HTTP API as resources and representations rather than exposing request operations directly.
137
+
138
+ ruby:`Async::REST::Resource` accepts a `Protocol::HTTP` middleware delegate, so the application can supply and configure the transport. Its `open` method provides the complementary convenience interface: it creates a ruby:`Async::HTTP::Client`, yields the resource, and closes the client when the block exits.
139
+
140
+ ## Supporting Faraday with async-http-faraday
141
+
142
+ Use [`async-http-faraday`](https://socketry.github.io/async-http-faraday/guides/getting-started/) when a library uses Faraday as its public HTTP abstraction or needs compatibility with the Faraday ecosystem. A new Async-native library can usually accept a ruby:`Async::HTTP::Client` or `Protocol::HTTP` middleware delegate directly.
143
+
144
+ If a library uses Faraday, accept a configured `Faraday::Connection` rather than changing `Faraday.default_adapter` globally. The application can then select the `Async::HTTP` adapter for that connection:
145
+
146
+ ~~~ ruby
147
+ require "async/http/faraday"
148
+
149
+ class StatusService
150
+ def initialize(connection)
151
+ @connection = connection
152
+ end
153
+
154
+ def healthy?
155
+ @connection.get("/status/200").success?
156
+ end
157
+ end
158
+
159
+ connection = Faraday.new("https://httpbin.org") do |builder|
160
+ builder.adapter :async_http
161
+ end
162
+
163
+ puts StatusService.new(connection).healthy?
164
+ ~~~
@@ -0,0 +1,134 @@
1
+ # Concurrent Requests and Connection Pooling
2
+
3
+ This guide explains how to run HTTP requests concurrently while keeping request fan-out, connection usage, and resource life cycles bounded.
4
+
5
+ Concurrent requests allow independent network operations to overlap. `Async::HTTP` combines this task concurrency with persistent connection pools, but the number of request tasks and the number of connections are separate controls.
6
+
7
+ ## Running Independent Requests Concurrently
8
+
9
+ When several requests do not depend on each other, start one child task for each request and then wait for their results:
10
+
11
+ ~~~ ruby
12
+ require "async/http/internet/instance"
13
+ require "json"
14
+
15
+ names = ["async", "async-http", "falcon"]
16
+
17
+ versions = Sync do |task|
18
+ tasks = names.map do |name|
19
+ task.async do
20
+ url = "https://rubygems.org/api/v1/gems/#{name}.json"
21
+
22
+ Async::HTTP::Internet.get(url) do |response|
23
+ raise "Could not fetch #{name}: #{response.status}" unless response.success?
24
+
25
+ JSON.parse(response.read).fetch("version")
26
+ end
27
+ end
28
+ end
29
+
30
+ tasks.map(&:wait)
31
+ end
32
+
33
+ names.zip(versions) do |name, version|
34
+ puts "#{name}: #{version}"
35
+ end
36
+ ~~~
37
+
38
+ Each request can make progress while the others are waiting for network I/O. ruby:`Async::Task#wait` returns the task result and re-raises any failure, so request errors are not silently discarded. Results are collected in the original order even if requests finish in a different order.
39
+
40
+ The response block closes each response after its body is processed. General task creation, cancellation, and failure propagation are covered by the [`async` Tasks guide](https://socketry.github.io/async/guides/tasks/).
41
+
42
+ ## Limiting Request Concurrency
43
+
44
+ Creating one task per item is appropriate for a small, fixed collection. A large or externally supplied collection should have an explicit concurrency limit so it cannot overwhelm the remote service or retain an unbounded number of pending operations.
45
+
46
+ Use ruby:`Async::Semaphore` to set a fixed request limit and ruby:`Async::Barrier` to wait for all the work:
47
+
48
+ ~~~ ruby
49
+ require "async"
50
+ require "async/semaphore"
51
+ require "async/http/internet"
52
+
53
+ names = ["async", "async-http", "falcon", "protocol-http", "io-event", "console"]
54
+
55
+ Sync do
56
+ internet = Async::HTTP::Internet.new(limit: 2)
57
+
58
+ begin
59
+ Barrier(parent: nil) do |barrier|
60
+ requests = Async::Semaphore.new(4, parent: barrier)
61
+
62
+ names.each do |name|
63
+ requests.async do
64
+ url = "https://rubygems.org/api/v1/gems/#{name}.json"
65
+
66
+ internet.get(url) do |response|
67
+ raise "Could not fetch #{name}: #{response.status}" unless response.success?
68
+
69
+ puts "#{name}: #{response.status}"
70
+ end
71
+ end
72
+ end
73
+ end
74
+ ensure
75
+ internet.close
76
+ end
77
+ end
78
+ ~~~
79
+
80
+ The semaphore allows at most four request operations to run at once. The barrier tracks those tasks, waits for them to finish, propagates failures, and cancels unfinished work if the block exits early. `parent: nil` disables the barrier's load-based scheduling because the semaphore already provides an explicit limit.
81
+
82
+ See the [`async` Best Practices guide](https://socketry.github.io/async/guides/best-practices/) for general guidance on barriers, semaphores, and large workloads.
83
+
84
+ ## Request Limits and Connection Limits
85
+
86
+ Request concurrency and pool capacity solve different problems:
87
+
88
+ | Control | What It Limits | Scope |
89
+ | --- | --- | --- |
90
+ | ruby:`Async::Semaphore` | Concurrent request operations and their application work. | The tasks started through that semaphore. |
91
+ | `limit` passed to ruby:`Async::HTTP::Client` | Connections held by one client pool. | One configured endpoint. |
92
+ | `limit` passed to ruby:`Async::HTTP::Internet` | Connections held by each client it creates. | Applied independently to every origin. |
93
+
94
+ The client creates connections lazily and reuses viable persistent connections. When its pool reaches `limit`, a request waits until an existing connection has capacity or is released.
95
+
96
+ For HTTP/1, a connection normally processes one request at a time, so connection capacity also constrains active requests. HTTP/2 can multiplex several request streams over one connection, so a small connection pool may support much higher request concurrency. Use a semaphore when the application needs a protocol-independent request limit; use `limit` to control connection resources.
97
+
98
+ ## Releasing Connections for Reuse
99
+
100
+ A response retains pool capacity until its body is finished or closed. Always close responses promptly, especially before starting more requests that use the same limited pool:
101
+
102
+ ~~~ ruby
103
+ require "async/http"
104
+
105
+ endpoint = Async::HTTP::Endpoint.parse("https://rubygems.org")
106
+
107
+ Sync do
108
+ Async::HTTP::Client.open(endpoint, limit: 1) do |client|
109
+ response = client.get("/api/v1/gems/async-http.json")
110
+
111
+ begin
112
+ raise "Download failed: #{response.status}" unless response.success?
113
+
114
+ bytes = 0
115
+
116
+ response.each do |chunk|
117
+ bytes += chunk.bytesize
118
+ end
119
+
120
+ puts "Downloaded #{bytes} bytes."
121
+ ensure
122
+ response.close
123
+ end
124
+ end
125
+ end
126
+ ~~~
127
+
128
+ The block form of ruby:`Async::HTTP::Internet` performs this cleanup automatically. When using ruby:`Async::HTTP::Client`, close the response explicitly with an `ensure` block. Holding an unread response while waiting for another request can exhaust an HTTP/1 pool and make the second request wait indefinitely.
129
+
130
+ ## Pool Life Cycle
131
+
132
+ Connection pools are bound to the event loop in which they are used. Their internal maintenance task is transient, so it does not keep the event loop alive and closes the pool when the event loop exits.
133
+
134
+ Use the shared ruby:`Async::HTTP::Internet` interface when connections can remain open until the event loop exits. Use ruby:`Async::HTTP::Client.open`, or explicitly close an `Internet` or `Client`, when connections should be released earlier. See [Choosing a Client](../choosing-a-client/) for the ownership trade-offs.
@@ -1,6 +1,6 @@
1
1
  # Getting Started
2
2
 
3
- This guide explains how to get started with `Async::HTTP`.
3
+ This guide explains how to make HTTP requests and serve HTTP responses with `Async::HTTP`.
4
4
 
5
5
  ## Installation
6
6
 
@@ -12,137 +12,174 @@ $ bundle add async-http
12
12
 
13
13
  ## Core Concepts
14
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.
15
+ `Async::HTTP` provides several interfaces for different kinds of HTTP applications:
20
16
 
21
- ## Usage
17
+ - ruby:`Async::HTTP::Internet` makes requests to arbitrary hosts and manages a client for each remote endpoint.
18
+ - ruby:`Async::HTTP::Client` manages persistent connections to a specific endpoint.
19
+ - ruby:`Async::HTTP::Server` accepts connections and dispatches requests to an HTTP application.
20
+ - ruby:`Async::HTTP::Endpoint` describes how a client connects or a server listens, including the URL, protocol, and TLS configuration.
21
+ - [`protocol-http`](https://github.com/socketry/protocol-http) provides the shared request, response, header, and body interfaces.
22
22
 
23
- ### Making a Request
23
+ Use `Internet` for general-purpose requests to different hosts. Use `Client` when your application repeatedly communicates with one endpoint or needs endpoint-specific configuration. See [Choosing a Client](../choosing-a-client/) for the ownership and configuration trade-offs.
24
24
 
25
- To make a request, use {ruby Async::HTTP::Internet} and call the appropriate method:
25
+ ## Making a Request
26
+
27
+ The shared ruby:`Async::HTTP::Internet` instance provides a convenient starting point. Run asynchronous HTTP operations inside `Sync`, which creates or reuses the event loop while returning the block result directly:
26
28
 
27
29
  ~~~ ruby
28
- require 'async/http/internet/instance'
30
+ require "async/http/internet/instance"
29
31
 
30
32
  Sync do
31
33
  Async::HTTP::Internet.get("https://httpbin.org/get") do |response|
34
+ puts "Status: #{response.status}"
32
35
  puts response.read
33
36
  end
34
37
  end
35
38
  ~~~
36
39
 
37
- The following methods are supported:
40
+ Passing a block automatically closes the response when the block exits, including when an exception is raised. Responses are streamed, so callers that do not use the block form must close the response explicitly.
38
41
 
39
42
  ~~~ ruby
40
- Async::HTTP::Internet.methods(false)
41
- # => [:patch, :options, :connect, :post, :get, :delete, :head, :trace, :put]
43
+ require "async/http/internet/instance"
44
+
45
+ Sync do
46
+ response = Async::HTTP::Internet.get("https://httpbin.org/get")
47
+ puts response.read
48
+ ensure
49
+ response&.close
50
+ end
42
51
  ~~~
43
52
 
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:
53
+ Convenience methods are provided for `GET`, `HEAD`, `POST`, `PUT`, `DELETE`, `CONNECT`, `OPTIONS`, `TRACE`, `PATCH`, and `QUERY` requests.
54
+
55
+ ### Connection Persistence
56
+
57
+ `Internet` creates a ruby:`Async::HTTP::Client` for each remote endpoint and reuses its persistent connections. The underlying async pools are bound to the event loop and are closed when that event loop exits.
58
+
59
+ An explicitly created `Internet` can also be closed early when an application wants to release all cached clients before the event loop exits:
45
60
 
46
61
  ~~~ ruby
47
- require 'async/http/internet/instance'
62
+ require "async/http/internet"
48
63
 
49
64
  Sync do
50
- response = Async::HTTP::Internet.get("https://httpbin.org/get")
51
- puts response.read
65
+ internet = Async::HTTP::Internet.new
66
+
67
+ internet.get("https://example.com") do |response|
68
+ puts response.status
69
+ end
52
70
  ensure
53
- response&.close
71
+ internet&.close
54
72
  end
55
73
  ~~~
56
74
 
57
- As responses are streamed, you must ensure it is closed when you are finished with it.
75
+ See [Concurrent Requests and Connection Pooling](../concurrent-requests/) to run independent requests together and configure separate limits for request tasks and connections.
58
76
 
59
- #### Persistence
77
+ ## Working with Responses
60
78
 
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.
79
+ A response contains a status, headers, and a streaming body. Check the status before processing content, and use header names in lower case:
80
+
81
+ ~~~ ruby
82
+ require "async/http/internet/instance"
83
+
84
+ Sync do
85
+ Async::HTTP::Internet.get("https://httpbin.org/json") do |response|
86
+ if response.success?
87
+ puts response.headers["content-type"]
88
+ puts response.read
89
+ else
90
+ warn "Request failed with status #{response.status}."
91
+ end
92
+ end
93
+ end
94
+ ~~~
95
+
96
+ For larger responses, process the body incrementally rather than reading it into one string. See the [`protocol-http` message body documentation](https://socketry.github.io/protocol-http/guides/message-body/) for the complete body interface.
62
97
 
63
98
  ### Downloading a File
64
99
 
100
+ Use `response.save` to stream a response directly to a file:
101
+
65
102
  ~~~ ruby
66
- require 'async/http/internet/instance'
103
+ require "async/http/internet/instance"
67
104
 
68
105
  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
106
+ Async::HTTP::Internet.get("https://example.com/archive.zip") do |response|
107
+ raise "Download failed with status #{response.status}." unless response.success?
108
+
109
+ response.save("archive.zip")
110
+ end
76
111
  end
77
112
  ~~~
78
113
 
79
- ### Posting Data
114
+ ## Posting JSON
80
115
 
81
- To post data, use the `post` method:
116
+ Pass headers and a body after the request target. The body may be a string or a compatible `protocol-http` body object.
82
117
 
83
118
  ~~~ ruby
84
- require 'async/http/internet/instance'
119
+ require "async/http/internet/instance"
120
+ require "json"
85
121
 
86
- data = {'life' => 42}
122
+ data = {life: 42}
123
+ headers = [
124
+ ["accept", "application/json"],
125
+ ["content-type", "application/json"],
126
+ ]
87
127
 
88
128
  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
129
+ Async::HTTP::Internet.post("https://httpbin.org/anything", headers, JSON.dump(data)) do |response|
130
+ raise "Request failed with status #{response.status}." unless response.success?
131
+
132
+ puts JSON.pretty_generate(JSON.parse(response.read))
133
+ end
100
134
  end
101
135
  ~~~
102
136
 
103
- For more complex scenarios, including HTTP APIs, consider using [async-rest](https://github.com/socketry/async-rest) instead.
137
+ For resource-oriented HTTP APIs, consider using [`async-rest`](https://github.com/socketry/async-rest), which builds on `Async::HTTP`.
104
138
 
105
- ### Timeouts
139
+ ## Applying a Timeout
106
140
 
107
- To set a timeout for a request, use the `Task#with_timeout` method:
141
+ Networks can stall indefinitely, so impose a timeout around operations that must complete within a fixed duration:
108
142
 
109
143
  ~~~ ruby
110
- require 'async/http/internet/instance'
144
+ require "async/http/internet/instance"
111
145
 
112
146
  Sync do |task|
113
- # Request will timeout after 2 seconds
114
147
  task.with_timeout(2) do
115
- response = Async::HTTP::Internet.get "https://httpbin.org/delay/10"
116
- ensure
117
- response&.close
148
+ Async::HTTP::Internet.get("https://httpbin.org/delay/10") do |response|
149
+ puts response.read
150
+ end
118
151
  end
119
152
  rescue Async::TimeoutError
120
- puts "The request timed out"
153
+ warn "The request timed out."
121
154
  end
122
155
  ~~~
123
156
 
124
- ### Making a Server
157
+ The response block still closes the response if the timeout interrupts the request while its body is being processed.
158
+
159
+ ## Making a Server
125
160
 
126
- To create a server, use an instance of {ruby Async::HTTP::Server}:
161
+ ruby:`Async::HTTP::Server` accepts an application that maps each request to a ruby:`Protocol::HTTP::Response`. The following example starts a local server, makes one request, and then releases both client and server resources:
127
162
 
128
163
  ~~~ ruby
129
- require 'async/http'
164
+ require "async/http"
130
165
 
131
- endpoint = Async::HTTP::Endpoint.parse('http://localhost:9292')
166
+ endpoint = Async::HTTP::Endpoint.parse("http://localhost:9292")
167
+ server = Async::HTTP::Server.for(endpoint) do |request|
168
+ Protocol::HTTP::Response[200, {"content-type" => "text/plain"}, ["Hello World"]]
169
+ end
132
170
 
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
171
+ Sync do
172
+ server_task = server.run
141
173
 
142
- client = Async::HTTP::Client.new(endpoint)
143
- response = client.get("/")
144
- puts response.read
174
+ Async::HTTP::Client.open(endpoint) do |client|
175
+ response = client.get("/")
176
+ puts response.read
177
+ ensure
178
+ response&.close
179
+ end
145
180
  ensure
146
- response&.close
181
+ server_task&.stop
147
182
  end
148
183
  ~~~
184
+
185
+ Use Falcon when you need to host a Rack application or deploy an HTTP server in production. Use `Async::HTTP::Server` directly when building a protocol-level server or embedding HTTP handling into another asynchronous application.
data/context/index.yaml CHANGED
@@ -3,13 +3,24 @@
3
3
  ---
4
4
  description: A HTTP client and server library.
5
5
  metadata:
6
+ bug_tracker_uri: https://github.com/socketry/async-http/issues
7
+ changelog_uri: https://github.com/socketry/async-http/blob/main/releases.md
6
8
  documentation_uri: https://socketry.github.io/async-http/
7
9
  source_code_uri: https://github.com/socketry/async-http.git
8
10
  files:
9
11
  - path: getting-started.md
10
12
  title: Getting Started
11
- description: This guide explains how to get started with `Async::HTTP`.
13
+ description: This guide explains how to make HTTP requests and serve HTTP responses
14
+ with `Async::HTTP`.
15
+ - path: choosing-a-client.md
16
+ title: Choosing a Client
17
+ description: This guide explains how to choose between ruby:`Async::HTTP::Internet`,
18
+ ruby:`Async::HTTP::Client`, and higher-level interfaces for libraries.
19
+ - path: concurrent-requests.md
20
+ title: Concurrent Requests and Connection Pooling
21
+ description: This guide explains how to run HTTP requests concurrently while keeping
22
+ request fan-out, connection usage, and resource life cycles bounded.
12
23
  - path: testing.md
13
24
  title: Testing
14
- description: This guide explains how to use `Async::HTTP` clients and servers in
15
- your tests.
25
+ description: This guide explains how to test `Async::HTTP` clients and servers without
26
+ depending on external HTTP services.