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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9f4a2ceef3a210332512cbe39f7bcac65cecaf23308f151534cfeb955fccb3e1
4
- data.tar.gz: a44a6b0f61b2bdbf81b868e1e05bfe7e45855bc92a3efdc266534688d9f84fd8
3
+ metadata.gz: 86bc918af980c024aca7370544452805f98f6b207eb6bc9e86f605ba21a7a612
4
+ data.tar.gz: 595e2f1ce10a7cf2864992380e27398602f7e7ff956f73fd2e733f50bd02ca31
5
5
  SHA512:
6
- metadata.gz: 22f65ce662e632e0d7836f13c12db69f86305ecae5e88ccf97ace1e0dc3c861e5af4fce7877dd489fec2be48031dfef99b1b697c1c0153df9670f095061465e4
7
- data.tar.gz: 9cfb68209dcff74007c239d94176e7a3b9ac76a6667066e55e1d230b54e77663389cb5f81743ca2ac0f40b5f832e2f4720d65fa55c69962f808a9860a1018bd8
6
+ metadata.gz: 0fa98ca844d1f7b956128b17b4587756df1b8d164effb82b6704fbc61f50106095429fd7cb6596f6658ebe050c605b819fcbf3121ebc9939f739c1435f6eaac1
7
+ data.tar.gz: 9489e703af232522f9db1f8d931a2a31c810538b5592cdd0868af102ee3585161a7f912a14b62dcfccd412d1cd06e26e03472bd033c5f4183f4974b3e8d7f6d4
checksums.yaml.gz.sig CHANGED
Binary file
@@ -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 = URI.scheme_list[scheme.upcase] || URI::HTTP
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<String>] the alpn protocols to negotiate.
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
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2021-2023, by Samuel Williams.
4
+ # Copyright, 2021-2024, by Samuel Williams.
5
5
 
6
6
  require_relative '../internet'
7
7
 
@@ -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
- # 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.
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2024, by Samuel Williams.
22
5
 
23
6
  require_relative '../protocol'
24
7
 
@@ -1,23 +1,6 @@
1
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.
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
- protocol = headers.delete(UPGRADE)
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 Errno::ECONNRESET, Errno::EPIPE
21
- # Nothing we can do...
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 Async::TimeoutError
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-2023, by Samuel Williams.
4
+ # Copyright, 2018-2024, by Samuel Williams.
5
5
  # Copyright, 2019-2020, by Brian Morearty.
6
6
 
7
- require_relative 'client'
8
- require_relative 'endpoint'
9
- require_relative 'reference'
7
+ require_relative 'middleware/location_redirector'
10
8
 
11
- require 'protocol/http/middleware'
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
- class TooManyRedirects < StandardError
17
- end
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
@@ -64,7 +64,7 @@ module Async
64
64
  connection&.close
65
65
  end
66
66
 
67
- # @returns [Array(Async::Task)] The task that is running the server.
67
+ # @returns [Async::Task] The task that is running the server.
68
68
  def run
69
69
  Async do |task|
70
70
  @endpoint.accept(&self.method(:accept))
@@ -5,6 +5,6 @@
5
5
 
6
6
  module Async
7
7
  module HTTP
8
- VERSION = "0.69.0"
8
+ VERSION = "0.71.0"
9
9
  end
10
10
  end
data/lib/async/http.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2017-2023, by Samuel Williams.
4
+ # Copyright, 2017-2024, by Samuel Williams.
5
5
 
6
6
  require_relative 'http/version'
7
7
 
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
- This project uses the [Developer Certificate of Origin](https://developercertificate.org/). All contributors to this project must agree to this document to have their contributions accepted.
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
- ### Contributor Covenant
37
+ ### Community Guidelines
30
38
 
31
- This project is governed by the [Contributor Covenant](https://www.contributor-covenant.org/). All contributors and participants agree to abide by its terms.
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.69.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-06-24 00:00:00.000000000 Z
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.26'
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.26'
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.19'
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.19'
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