async-http 0.69.0 → 0.71.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 +15 -2
- data/lib/async/http/internet/instance.rb +1 -1
- data/lib/async/http/middleware/location_redirector.rb +143 -0
- data/lib/async/http/mock/endpoint.rb +3 -20
- data/lib/async/http/mock.rb +3 -20
- data/lib/async/http/protocol/http1/response.rb +2 -1
- data/lib/async/http/protocol/http1/server.rb +9 -11
- data/lib/async/http/relative_location.rb +6 -107
- data/lib/async/http/server.rb +1 -1
- data/lib/async/http/version.rb +1 -1
- data/lib/async/http.rb +1 -1
- data/readme.md +11 -11
- data.tar.gz.sig +0 -0
- metadata +7 -6
- 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: 86bc918af980c024aca7370544452805f98f6b207eb6bc9e86f605ba21a7a612
|
4
|
+
data.tar.gz: 595e2f1ce10a7cf2864992380e27398602f7e7ff956f73fd2e733f50bd02ca31
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0fa98ca844d1f7b956128b17b4587756df1b8d164effb82b6704fbc61f50106095429fd7cb6596f6658ebe050c605b819fcbf3121ebc9939f739c1435f6eaac1
|
7
|
+
data.tar.gz: 9489e703af232522f9db1f8d931a2a31c810538b5592cdd0868af102ee3585161a7f912a14b62dcfccd412d1cd06e26e03472bd033c5f4183f4974b3e8d7f6d4
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/lib/async/http/endpoint.rb
CHANGED
@@ -18,6 +18,13 @@ module Async
|
|
18
18
|
module HTTP
|
19
19
|
# Represents a way to connect to a remote HTTP server.
|
20
20
|
class Endpoint < ::IO::Endpoint::Generic
|
21
|
+
SCHEMES = {
|
22
|
+
'http' => URI::HTTP,
|
23
|
+
'https' => URI::HTTPS,
|
24
|
+
'ws' => URI::WS,
|
25
|
+
'wss' => URI::WSS,
|
26
|
+
}
|
27
|
+
|
21
28
|
def self.parse(string, endpoint = nil, **options)
|
22
29
|
url = URI.parse(string).normalize
|
23
30
|
|
@@ -25,9 +32,15 @@ module Async
|
|
25
32
|
end
|
26
33
|
|
27
34
|
# Construct an endpoint with a specified scheme, hostname, optional path, and options.
|
35
|
+
#
|
36
|
+
# @parameter scheme [String] The scheme to use, e.g. "http" or "https".
|
37
|
+
# @parameter hostname [String] The hostname to connect to (or bind to).
|
38
|
+
# @parameter *options [Hash] Additional options, passed to {#initialize}.
|
28
39
|
def self.for(scheme, hostname, path = "/", **options)
|
29
40
|
# TODO: Consider using URI.for once it becomes available:
|
30
|
-
uri_klass =
|
41
|
+
uri_klass = SCHEMES.fetch(scheme.downcase) do
|
42
|
+
raise ArgumentError, "Unsupported scheme: #{scheme.inspect}"
|
43
|
+
end
|
31
44
|
|
32
45
|
self.new(
|
33
46
|
uri_klass.new(scheme, nil, hostname, nil, nil, path, nil, nil, nil).normalize,
|
@@ -49,7 +62,7 @@ module Async
|
|
49
62
|
# @option hostname [String] the hostname to connect to (or bind to), overrides the URL hostname (used for SNI).
|
50
63
|
# @option port [Integer] the port to bind to, overrides the URL port.
|
51
64
|
# @option ssl_context [OpenSSL::SSL::SSLContext] the context to use for TLS.
|
52
|
-
# @option alpn_protocols [Array
|
65
|
+
# @option alpn_protocols [Array(String)] the alpn protocols to negotiate.
|
53
66
|
def initialize(url, endpoint = nil, **options)
|
54
67
|
super(**options)
|
55
68
|
|
@@ -0,0 +1,143 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2024, by Samuel Williams.
|
5
|
+
|
6
|
+
require_relative '../reference'
|
7
|
+
|
8
|
+
require 'protocol/http/middleware'
|
9
|
+
require 'protocol/http/body/rewindable'
|
10
|
+
|
11
|
+
module Async
|
12
|
+
module HTTP
|
13
|
+
module Middleware
|
14
|
+
# A client wrapper which transparently handles redirects to a given maximum number of hops.
|
15
|
+
#
|
16
|
+
# The default implementation will only follow relative locations (i.e. those without a scheme) and will switch to GET if the original request was not a GET.
|
17
|
+
#
|
18
|
+
# The best reference for these semantics is defined by the [Fetch specification](https://fetch.spec.whatwg.org/#http-redirect-fetch).
|
19
|
+
#
|
20
|
+
# | Redirect using GET | Permanent | Temporary |
|
21
|
+
# |:-----------------------------------------:|:---------:|:---------:|
|
22
|
+
# | Allowed | 301 | 302 |
|
23
|
+
# | Preserve original method | 308 | 307 |
|
24
|
+
#
|
25
|
+
# For the specific details of the redirect handling, see:
|
26
|
+
# - <https://datatracker.ietf.org/doc/html/rfc7231#section-6-4-2> 301 Moved Permanently.
|
27
|
+
# - <https://datatracker.ietf.org/doc/html/rfc7231#section-6-4-3> 302 Found.
|
28
|
+
# - <https://datatracker.ietf.org/doc/html/rfc7538 308 Permanent Redirect.
|
29
|
+
# - <https://datatracker.ietf.org/doc/html/rfc7231#section-6-4-7> 307 Temporary Redirect.
|
30
|
+
#
|
31
|
+
class LocationRedirector < ::Protocol::HTTP::Middleware
|
32
|
+
class TooManyRedirects < StandardError
|
33
|
+
end
|
34
|
+
|
35
|
+
# Header keys which should be deleted when changing a request from a POST to a GET as defined by <https://fetch.spec.whatwg.org/#request-body-header-name>.
|
36
|
+
PROHIBITED_GET_HEADERS = [
|
37
|
+
'content-encoding',
|
38
|
+
'content-language',
|
39
|
+
'content-location',
|
40
|
+
'content-type',
|
41
|
+
]
|
42
|
+
|
43
|
+
# maximum_hops is the max number of redirects. Set to 0 to allow 1 request with no redirects.
|
44
|
+
def initialize(app, maximum_hops = 3)
|
45
|
+
super(app)
|
46
|
+
|
47
|
+
@maximum_hops = maximum_hops
|
48
|
+
end
|
49
|
+
|
50
|
+
# The maximum number of hops which will limit the number of redirects until an error is thrown.
|
51
|
+
attr :maximum_hops
|
52
|
+
|
53
|
+
def redirect_with_get?(request, response)
|
54
|
+
# We only want to switch to GET if the request method is something other than get, e.g. POST.
|
55
|
+
if request.method != GET
|
56
|
+
# According to the RFC, we should only switch to GET if the response is a 301 or 302:
|
57
|
+
return response.status == 301 || response.status == 302
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Handle a redirect to a relative location.
|
62
|
+
#
|
63
|
+
# @parameter request [Protocol::HTTP::Request] The original request, which you can modify if you want to handle the redirect.
|
64
|
+
# @parameter location [String] The relative location to redirect to.
|
65
|
+
# @returns [Boolean] True if the redirect was handled, false if it was not.
|
66
|
+
def handle_redirect(request, location)
|
67
|
+
uri = URI.parse(location)
|
68
|
+
|
69
|
+
if uri.absolute?
|
70
|
+
return false
|
71
|
+
end
|
72
|
+
|
73
|
+
# Update the path of the request:
|
74
|
+
request.path = Reference[request.path] + location
|
75
|
+
|
76
|
+
# Follow the redirect:
|
77
|
+
return true
|
78
|
+
end
|
79
|
+
|
80
|
+
def call(request)
|
81
|
+
# We don't want to follow redirects for HEAD requests:
|
82
|
+
return super if request.head?
|
83
|
+
|
84
|
+
if body = request.body
|
85
|
+
if body.respond_to?(:rewind)
|
86
|
+
# The request body was already rewindable, so use it as is:
|
87
|
+
body = request.body
|
88
|
+
else
|
89
|
+
# The request body was not rewindable, and we might need to resubmit it if we get a response status of 307 or 308, so make it rewindable:
|
90
|
+
body = ::Protocol::HTTP::Body::Rewindable.new(body)
|
91
|
+
request.body = body
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
hops = 0
|
96
|
+
|
97
|
+
while hops <= @maximum_hops
|
98
|
+
response = super(request)
|
99
|
+
|
100
|
+
if response.redirection?
|
101
|
+
hops += 1
|
102
|
+
|
103
|
+
# Get the redirect location:
|
104
|
+
unless location = response.headers['location']
|
105
|
+
return response
|
106
|
+
end
|
107
|
+
|
108
|
+
response.finish
|
109
|
+
|
110
|
+
unless handle_redirect(request, location)
|
111
|
+
return response
|
112
|
+
end
|
113
|
+
|
114
|
+
# Ensure the request (body) is finished and set to nil before we manipulate the request:
|
115
|
+
request.finish
|
116
|
+
|
117
|
+
if request.method == GET or response.preserve_method?
|
118
|
+
# We (might) need to rewind the body so that it can be submitted again:
|
119
|
+
body&.rewind
|
120
|
+
request.body = body
|
121
|
+
else
|
122
|
+
# We are changing the method to GET:
|
123
|
+
request.method = GET
|
124
|
+
|
125
|
+
# We will no longer be submitting the body:
|
126
|
+
body = nil
|
127
|
+
|
128
|
+
# Remove any headers which are not allowed in a GET request:
|
129
|
+
PROHIBITED_GET_HEADERS.each do |header|
|
130
|
+
request.headers.delete(header)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
else
|
134
|
+
return response
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
raise TooManyRedirects, "Redirected #{hops} times, exceeded maximum!"
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -1,24 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
|
-
#
|
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.
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2024, by Samuel Williams.
|
22
5
|
|
23
6
|
require_relative '../protocol'
|
24
7
|
|
data/lib/async/http/mock.rb
CHANGED
@@ -1,23 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
|
-
#
|
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.
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2024, by Samuel Williams.
|
22
5
|
|
23
6
|
require_relative 'mock/endpoint'
|
@@ -31,7 +31,8 @@ module Async
|
|
31
31
|
@connection = connection
|
32
32
|
@reason = reason
|
33
33
|
|
34
|
-
|
34
|
+
# Technically, there should never be more than one value for the upgrade header, but we'll just take the first one to avoid complexity.
|
35
|
+
protocol = headers.delete(UPGRADE)&.first
|
35
36
|
|
36
37
|
super(version, status, headers, body, protocol)
|
37
38
|
end
|
@@ -7,6 +7,7 @@
|
|
7
7
|
# Copyright, 2024, by Anton Zhuravsky.
|
8
8
|
|
9
9
|
require_relative 'connection'
|
10
|
+
require 'console/event/failure'
|
10
11
|
|
11
12
|
module Async
|
12
13
|
module HTTP
|
@@ -17,8 +18,9 @@ module Async
|
|
17
18
|
@persistent = false
|
18
19
|
write_response(@version, status, {})
|
19
20
|
write_body(@version, nil)
|
20
|
-
rescue
|
21
|
-
#
|
21
|
+
rescue => error
|
22
|
+
# At this point, there is very little we can do to recover:
|
23
|
+
Console::Event::Failure.for(error).emit(self, "Failed to write failure response.", severity: :debug)
|
22
24
|
end
|
23
25
|
|
24
26
|
def next_request
|
@@ -33,13 +35,9 @@ module Async
|
|
33
35
|
end
|
34
36
|
|
35
37
|
return request
|
36
|
-
rescue
|
37
|
-
# For an interesting discussion about this behaviour, see https://trac.nginx.org/nginx/ticket/1005
|
38
|
-
# If you enable this, you will see some spec failures...
|
39
|
-
# fail_request(408)
|
40
|
-
raise
|
41
|
-
rescue
|
38
|
+
rescue ::Protocol::HTTP1::BadRequest
|
42
39
|
fail_request(400)
|
40
|
+
# Conceivably we could retry here, but we don't really know how bad the error is, so it's better to just fail:
|
43
41
|
raise
|
44
42
|
end
|
45
43
|
|
@@ -60,9 +58,9 @@ module Async
|
|
60
58
|
# If a response was generated, send it:
|
61
59
|
if response
|
62
60
|
trailer = response.headers.trailer!
|
63
|
-
|
61
|
+
|
64
62
|
write_response(@version, response.status, response.headers)
|
65
|
-
|
63
|
+
|
66
64
|
# Some operations in this method are long running, that is, it's expected that `body.call(stream)` could literally run indefinitely. In order to facilitate garbage collection, we want to nullify as many local variables before calling the streaming body. This ensures that the garbage collection can clean up as much state as possible during the long running operation, so we don't retain objects that are no longer needed.
|
67
65
|
|
68
66
|
if body and protocol = response.protocol
|
@@ -89,7 +87,7 @@ module Async
|
|
89
87
|
|
90
88
|
write_body(version, body, head, trailer)
|
91
89
|
end
|
92
|
-
|
90
|
+
|
93
91
|
# We are done with the body, you shouldn't need to call close on it:
|
94
92
|
body = nil
|
95
93
|
else
|
@@ -1,119 +1,18 @@
|
|
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
5
|
# Copyright, 2019-2020, by Brian Morearty.
|
6
6
|
|
7
|
-
require_relative '
|
8
|
-
require_relative 'endpoint'
|
9
|
-
require_relative 'reference'
|
7
|
+
require_relative 'middleware/location_redirector'
|
10
8
|
|
11
|
-
|
12
|
-
require 'protocol/http/body/rewindable'
|
9
|
+
warn "`Async::HTTP::RelativeLocation` is deprecated and will be removed in the next release. Please use `Async::HTTP::Middleware::LocationRedirector` instead.", uplevel: 1
|
13
10
|
|
14
11
|
module Async
|
15
12
|
module HTTP
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
# A client wrapper which transparently handles both relative and absolute redirects to a given maximum number of hops.
|
20
|
-
#
|
21
|
-
# The best reference for these semantics is defined by the [Fetch specification](https://fetch.spec.whatwg.org/#http-redirect-fetch).
|
22
|
-
#
|
23
|
-
# | Redirect using GET | Permanent | Temporary |
|
24
|
-
# |:-----------------------------------------:|:---------:|:---------:|
|
25
|
-
# | Allowed | 301 | 302 |
|
26
|
-
# | Preserve original method | 308 | 307 |
|
27
|
-
#
|
28
|
-
# For the specific details of the redirect handling, see:
|
29
|
-
# - <https://datatracker.ietf.org/doc/html/rfc7231#section-6-4-2> 301 Moved Permanently.
|
30
|
-
# - <https://datatracker.ietf.org/doc/html/rfc7231#section-6-4-3> 302 Found.
|
31
|
-
# - <https://datatracker.ietf.org/doc/html/rfc7538 308 Permanent Redirect.
|
32
|
-
# - <https://datatracker.ietf.org/doc/html/rfc7231#section-6-4-7> 307 Temporary Redirect.
|
33
|
-
#
|
34
|
-
class RelativeLocation < ::Protocol::HTTP::Middleware
|
35
|
-
# Header keys which should be deleted when changing a request from a POST to a GET as defined by <https://fetch.spec.whatwg.org/#request-body-header-name>.
|
36
|
-
PROHIBITED_GET_HEADERS = [
|
37
|
-
'content-encoding',
|
38
|
-
'content-language',
|
39
|
-
'content-location',
|
40
|
-
'content-type',
|
41
|
-
]
|
42
|
-
|
43
|
-
# maximum_hops is the max number of redirects. Set to 0 to allow 1 request with no redirects.
|
44
|
-
def initialize(app, maximum_hops = 3)
|
45
|
-
super(app)
|
46
|
-
|
47
|
-
@maximum_hops = maximum_hops
|
48
|
-
end
|
49
|
-
|
50
|
-
# The maximum number of hops which will limit the number of redirects until an error is thrown.
|
51
|
-
attr :maximum_hops
|
52
|
-
|
53
|
-
def redirect_with_get?(request, response)
|
54
|
-
# We only want to switch to GET if the request method is something other than get, e.g. POST.
|
55
|
-
if request.method != GET
|
56
|
-
# According to the RFC, we should only switch to GET if the response is a 301 or 302:
|
57
|
-
return response.status == 301 || response.status == 302
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def call(request)
|
62
|
-
# We don't want to follow redirects for HEAD requests:
|
63
|
-
return super if request.head?
|
64
|
-
|
65
|
-
if body = request.body
|
66
|
-
# We need to cache the body as it might be submitted multiple times if we get a response status of 307 or 308:
|
67
|
-
body = ::Protocol::HTTP::Body::Rewindable.new(body)
|
68
|
-
request.body = body
|
69
|
-
end
|
70
|
-
|
71
|
-
hops = 0
|
72
|
-
|
73
|
-
while hops <= @maximum_hops
|
74
|
-
response = super(request)
|
75
|
-
|
76
|
-
if response.redirection?
|
77
|
-
hops += 1
|
78
|
-
|
79
|
-
# Get the redirect location:
|
80
|
-
unless location = response.headers['location']
|
81
|
-
return response
|
82
|
-
end
|
83
|
-
|
84
|
-
response.finish
|
85
|
-
|
86
|
-
uri = URI.parse(location)
|
87
|
-
|
88
|
-
if uri.absolute?
|
89
|
-
return response
|
90
|
-
else
|
91
|
-
request.path = Reference[request.path] + location
|
92
|
-
end
|
93
|
-
|
94
|
-
if request.method == GET or response.preserve_method?
|
95
|
-
# We (might) need to rewind the body so that it can be submitted again:
|
96
|
-
body&.rewind
|
97
|
-
else
|
98
|
-
# We are changing the method to GET:
|
99
|
-
request.method = GET
|
100
|
-
|
101
|
-
# Clear the request body:
|
102
|
-
request.finish
|
103
|
-
body = nil
|
104
|
-
|
105
|
-
# Remove any headers which are not allowed in a GET request:
|
106
|
-
PROHIBITED_GET_HEADERS.each do |header|
|
107
|
-
request.headers.delete(header)
|
108
|
-
end
|
109
|
-
end
|
110
|
-
else
|
111
|
-
return response
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
raise TooManyRedirects, "Redirected #{hops} times, exceeded maximum!"
|
116
|
-
end
|
13
|
+
module Middleware
|
14
|
+
RelativeLocation = Middleware::LocationRedirector
|
15
|
+
TooManyRedirects = RelativeLocation::TooManyRedirects
|
117
16
|
end
|
118
17
|
end
|
119
18
|
end
|
data/lib/async/http/server.rb
CHANGED
data/lib/async/http/version.rb
CHANGED
data/lib/async/http.rb
CHANGED
data/readme.md
CHANGED
@@ -12,6 +12,14 @@ Please see the [project documentation](https://socketry.github.io/async-http/) f
|
|
12
12
|
|
13
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.
|
14
14
|
|
15
|
+
## See Also
|
16
|
+
|
17
|
+
- [benchmark-http](https://github.com/socketry/benchmark-http) — A benchmarking tool to report on web server concurrency.
|
18
|
+
- [falcon](https://github.com/socketry/falcon) — A rack compatible server built on top of `async-http`.
|
19
|
+
- [async-websocket](https://github.com/socketry/async-websocket) — Asynchronous client and server websockets.
|
20
|
+
- [async-rest](https://github.com/socketry/async-rest) — A RESTful resource layer built on top of `async-http`.
|
21
|
+
- [async-http-faraday](https://github.com/socketry/async-http-faraday) — A faraday adapter to use `async-http`.
|
22
|
+
|
15
23
|
## Contributing
|
16
24
|
|
17
25
|
We welcome contributions to this project.
|
@@ -24,16 +32,8 @@ We welcome contributions to this project.
|
|
24
32
|
|
25
33
|
### Developer Certificate of Origin
|
26
34
|
|
27
|
-
|
35
|
+
In order to protect users of this project, we require all contributors to comply with the [Developer Certificate of Origin](https://developercertificate.org/). This ensures that all contributions are properly licensed and attributed.
|
28
36
|
|
29
|
-
###
|
37
|
+
### Community Guidelines
|
30
38
|
|
31
|
-
This project is
|
32
|
-
|
33
|
-
## See Also
|
34
|
-
|
35
|
-
- [benchmark-http](https://github.com/socketry/benchmark-http) — A benchmarking tool to report on web server concurrency.
|
36
|
-
- [falcon](https://github.com/socketry/falcon) — A rack compatible server built on top of `async-http`.
|
37
|
-
- [async-websocket](https://github.com/socketry/async-websocket) — Asynchronous client and server websockets.
|
38
|
-
- [async-rest](https://github.com/socketry/async-rest) — A RESTful resource layer built on top of `async-http`.
|
39
|
-
- [async-http-faraday](https://github.com/socketry/async-http-faraday) — A faraday adapter to use `async-http`.
|
39
|
+
This project is best served by a collaborative and respectful environment. Treat each other professionally, respect differing viewpoints, and engage constructively. Harassment, discrimination, or harmful behavior is not tolerated. Communicate clearly, listen actively, and support one another. If any issues arise, please inform the project maintainers.
|
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.71.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
@@ -58,7 +58,7 @@ cert_chain:
|
|
58
58
|
Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
|
59
59
|
voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
|
60
60
|
-----END CERTIFICATE-----
|
61
|
-
date: 2024-
|
61
|
+
date: 2024-08-26 00:00:00.000000000 Z
|
62
62
|
dependencies:
|
63
63
|
- !ruby/object:Gem::Dependency
|
64
64
|
name: async
|
@@ -122,28 +122,28 @@ dependencies:
|
|
122
122
|
requirements:
|
123
123
|
- - "~>"
|
124
124
|
- !ruby/object:Gem::Version
|
125
|
-
version: '0.
|
125
|
+
version: '0.28'
|
126
126
|
type: :runtime
|
127
127
|
prerelease: false
|
128
128
|
version_requirements: !ruby/object:Gem::Requirement
|
129
129
|
requirements:
|
130
130
|
- - "~>"
|
131
131
|
- !ruby/object:Gem::Version
|
132
|
-
version: '0.
|
132
|
+
version: '0.28'
|
133
133
|
- !ruby/object:Gem::Dependency
|
134
134
|
name: protocol-http1
|
135
135
|
requirement: !ruby/object:Gem::Requirement
|
136
136
|
requirements:
|
137
137
|
- - "~>"
|
138
138
|
- !ruby/object:Gem::Version
|
139
|
-
version: '0.
|
139
|
+
version: '0.20'
|
140
140
|
type: :runtime
|
141
141
|
prerelease: false
|
142
142
|
version_requirements: !ruby/object:Gem::Requirement
|
143
143
|
requirements:
|
144
144
|
- - "~>"
|
145
145
|
- !ruby/object:Gem::Version
|
146
|
-
version: '0.
|
146
|
+
version: '0.20'
|
147
147
|
- !ruby/object:Gem::Dependency
|
148
148
|
name: protocol-http2
|
149
149
|
requirement: !ruby/object:Gem::Requirement
|
@@ -191,6 +191,7 @@ files:
|
|
191
191
|
- lib/async/http/endpoint.rb
|
192
192
|
- lib/async/http/internet.rb
|
193
193
|
- lib/async/http/internet/instance.rb
|
194
|
+
- lib/async/http/middleware/location_redirector.rb
|
194
195
|
- lib/async/http/mock.rb
|
195
196
|
- lib/async/http/mock/endpoint.rb
|
196
197
|
- lib/async/http/protocol.rb
|
metadata.gz.sig
CHANGED
Binary file
|