async-http 0.40.3 → 0.41.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 +4 -4
- data/README.md +4 -4
- data/async-http.gemspec +3 -3
- data/examples/request.rb +3 -3
- data/examples/upload/client.rb +2 -2
- data/examples/upload/server.rb +3 -3
- data/lib/async/http.rb +1 -1
- data/lib/async/http/body.rb +2 -11
- data/lib/async/http/body/hijack.rb +3 -3
- data/lib/async/http/body/writable.rb +3 -2
- data/lib/async/http/client.rb +11 -10
- data/lib/async/http/{url_endpoint.rb → endpoint.rb} +1 -1
- data/lib/async/http/internet.rb +6 -6
- data/lib/async/http/pool.rb +1 -1
- data/lib/async/http/protocol/http1/client.rb +19 -8
- data/lib/async/http/protocol/http1/connection.rb +8 -25
- data/lib/async/http/protocol/http1/request.rb +10 -7
- data/lib/async/http/protocol/http1/response.rb +19 -3
- data/lib/async/http/protocol/http1/server.rb +14 -2
- data/lib/async/http/protocol/http10.rb +4 -0
- data/lib/async/http/protocol/http11.rb +4 -0
- data/lib/async/http/protocol/http2.rb +8 -0
- data/lib/async/http/protocol/http2/connection.rb +11 -2
- data/lib/async/http/protocol/http2/promise.rb +2 -2
- data/lib/async/http/protocol/http2/request.rb +17 -9
- data/lib/async/http/protocol/http2/response.rb +12 -6
- data/lib/async/http/protocol/http2/server.rb +12 -10
- data/lib/async/http/protocol/http2/stream.rb +1 -1
- data/lib/async/http/protocol/request.rb +6 -6
- data/lib/async/http/protocol/response.rb +5 -6
- data/lib/async/http/relative_location.rb +5 -4
- data/lib/async/http/server.rb +7 -5
- data/lib/async/http/statistics.rb +2 -2
- data/lib/async/http/version.rb +1 -1
- data/tasks/server.rake +4 -4
- metadata +10 -29
- data/lib/async/http/accept_encoding.rb +0 -65
- data/lib/async/http/body/buffered.rb +0 -89
- data/lib/async/http/body/chunked.rb +0 -76
- data/lib/async/http/body/deflate.rb +0 -113
- data/lib/async/http/body/file.rb +0 -98
- data/lib/async/http/body/fixed.rb +0 -72
- data/lib/async/http/body/inflate.rb +0 -59
- data/lib/async/http/body/readable.rb +0 -76
- data/lib/async/http/body/reader.rb +0 -83
- data/lib/async/http/body/remainder.rb +0 -56
- data/lib/async/http/body/rewindable.rb +0 -60
- data/lib/async/http/body/streamable.rb +0 -83
- data/lib/async/http/body/wrapper.rb +0 -65
- data/lib/async/http/content_encoding.rb +0 -76
- data/lib/async/http/headers.rb +0 -27
- data/lib/async/http/middleware.rb +0 -79
- data/lib/async/http/middleware/builder.rb +0 -61
- data/lib/async/http/request.rb +0 -71
- data/lib/async/http/response.rb +0 -90
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 35e99a53d484c9c4e0ee98db95695ea2a238dc71e03c958425c37f4a2c6560dd
|
4
|
+
data.tar.gz: 7a6bcefcc687b98652d0fa1b23d234f6223ca8c5dd80fd88c44624883e871322
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 16b58d9b615e7ddb4fe12cda38dd48c3f4a598e27e4175cc14063faad08a8aab42e75e66ff8e7d47207af882f03fe5339b11ac28d281e02f91ca6278da77a3d5
|
7
|
+
data.tar.gz: 388048425c3444cc34d69712be5d33e6854023be0339a91b2b1f748a74e703339beb5c03de90cdaa693af51d3aa512b6591b57a161c94e6151dbbb327c7fc349
|
data/README.md
CHANGED
@@ -97,13 +97,13 @@ Here is a basic example of a client/server running in the same reactor:
|
|
97
97
|
require 'async/http/server'
|
98
98
|
require 'async/http/client'
|
99
99
|
require 'async/reactor'
|
100
|
-
require 'async/http/
|
100
|
+
require 'async/http/endpoint'
|
101
101
|
require 'async/http/response'
|
102
102
|
|
103
|
-
endpoint = Async::HTTP::
|
103
|
+
endpoint = Async::HTTP::Endpoint.parse('http://127.0.0.1:9294')
|
104
104
|
|
105
105
|
app = lambda do |request|
|
106
|
-
|
106
|
+
Protocol::HTTP::Response[200, {}, ["Hello World"]]
|
107
107
|
end
|
108
108
|
|
109
109
|
server = Async::HTTP::Server.new(app, endpoint)
|
@@ -140,7 +140,7 @@ trusted_fingerprints = {
|
|
140
140
|
}
|
141
141
|
|
142
142
|
Async do
|
143
|
-
endpoint = Async::HTTP::
|
143
|
+
endpoint = Async::HTTP::Endpoint.parse("https://www.codeotaku.com/index")
|
144
144
|
|
145
145
|
# This is a quick hack/POC:
|
146
146
|
ssl_context = endpoint.ssl_context
|
data/async-http.gemspec
CHANGED
@@ -19,9 +19,9 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.add_dependency("async", "~> 1.14")
|
20
20
|
spec.add_dependency("async-io", "~> 1.18")
|
21
21
|
|
22
|
-
spec.add_dependency("protocol-http", "~> 0.
|
23
|
-
spec.add_dependency("protocol-http1", "~> 0.
|
24
|
-
spec.add_dependency("protocol-http2", "~> 0.
|
22
|
+
spec.add_dependency("protocol-http", "~> 0.5.0")
|
23
|
+
spec.add_dependency("protocol-http1", "~> 0.6.0")
|
24
|
+
spec.add_dependency("protocol-http2", "~> 0.3.0")
|
25
25
|
|
26
26
|
# spec.add_dependency("openssl")
|
27
27
|
|
data/examples/request.rb
CHANGED
@@ -6,12 +6,12 @@
|
|
6
6
|
require 'async'
|
7
7
|
require 'async/logger'
|
8
8
|
require 'async/http/client'
|
9
|
-
require 'async/http/
|
9
|
+
require 'async/http/endpoint'
|
10
10
|
|
11
11
|
# Async.logger.level = Logger::DEBUG
|
12
12
|
|
13
13
|
Async.run do |task|
|
14
|
-
endpoint = Async::HTTP::
|
14
|
+
endpoint = Async::HTTP::Endpoint.parse("https://www.google.com")
|
15
15
|
|
16
16
|
client = Async::HTTP::Client.new(endpoint)
|
17
17
|
|
@@ -19,7 +19,7 @@ Async.run do |task|
|
|
19
19
|
'accept' => 'text/html',
|
20
20
|
}
|
21
21
|
|
22
|
-
request =
|
22
|
+
request = Protocol::HTTP::Request.new(client.scheme, "www.google.com", "GET", "/search?q=cats", headers)
|
23
23
|
|
24
24
|
puts "Sending request..."
|
25
25
|
response = client.call(request)
|
data/examples/upload/client.rb
CHANGED
@@ -6,10 +6,10 @@ require 'async'
|
|
6
6
|
require 'async/http/body/file'
|
7
7
|
require 'async/http/body/delayed'
|
8
8
|
require 'async/http/client'
|
9
|
-
require 'async/http/
|
9
|
+
require 'async/http/endpoint'
|
10
10
|
|
11
11
|
Async.run do
|
12
|
-
endpoint = Async::HTTP::
|
12
|
+
endpoint = Async::HTTP::Endpoint.parse("http://localhost:9222")
|
13
13
|
client = Async::HTTP::Client.new(endpoint, Async::HTTP::Protocol::HTTP2)
|
14
14
|
|
15
15
|
headers = [
|
data/examples/upload/server.rb
CHANGED
@@ -3,16 +3,16 @@ $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
|
|
3
3
|
|
4
4
|
require 'async'
|
5
5
|
require 'async/http/server'
|
6
|
-
require 'async/http/
|
6
|
+
require 'async/http/endpoint'
|
7
7
|
|
8
8
|
protocol = Async::HTTP::Protocol::HTTP2
|
9
|
-
endpoint = Async::HTTP::
|
9
|
+
endpoint = Async::HTTP::Endpoint.parse('http://127.0.0.1:9222', reuse_port: true)
|
10
10
|
|
11
11
|
Async.logger.level = Logger::DEBUG
|
12
12
|
|
13
13
|
Async.run do
|
14
14
|
server = Async::HTTP::Server.for(endpoint, protocol) do |request|
|
15
|
-
|
15
|
+
Protocol::HTTP::Response[200, {}, request.body]
|
16
16
|
end
|
17
17
|
|
18
18
|
server.run
|
data/lib/async/http.rb
CHANGED
data/lib/async/http/body.rb
CHANGED
@@ -18,22 +18,13 @@
|
|
18
18
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
19
|
# THE SOFTWARE.
|
20
20
|
|
21
|
+
require 'protocol/http/body/buffered'
|
21
22
|
require_relative 'body/writable'
|
22
|
-
require_relative 'body/buffered'
|
23
23
|
|
24
24
|
module Async
|
25
25
|
module HTTP
|
26
|
-
# These classes implement a general IO model for streaming HTTP bodies.
|
27
26
|
module Body
|
28
|
-
|
29
|
-
# class Body
|
30
|
-
# def each -> yield(String | nil)
|
31
|
-
# def read -> String | nil
|
32
|
-
# def join -> String
|
33
|
-
|
34
|
-
# def finish -> buffer the stream and close it.
|
35
|
-
# def close(error = nil) -> close the stream immediately.
|
36
|
-
# end
|
27
|
+
include ::Protocol::HTTP::Body
|
37
28
|
end
|
38
29
|
end
|
39
30
|
end
|
@@ -18,16 +18,16 @@
|
|
18
18
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
19
|
# THE SOFTWARE.
|
20
20
|
|
21
|
-
|
21
|
+
require 'protocol/http/body/readable'
|
22
22
|
require_relative 'stream'
|
23
23
|
|
24
24
|
module Async
|
25
25
|
module HTTP
|
26
26
|
module Body
|
27
27
|
# A body which is designed for hijacked connections.
|
28
|
-
class Hijack < Readable
|
28
|
+
class Hijack < ::Protocol::HTTP::Body::Readable
|
29
29
|
def self.response(request, status, headers, &block)
|
30
|
-
|
30
|
+
::Protocol::HTTP::Response[status, headers, self.wrap(request, &block)]
|
31
31
|
end
|
32
32
|
|
33
33
|
def self.wrap(request, &block)
|
@@ -18,13 +18,14 @@
|
|
18
18
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
19
|
# THE SOFTWARE.
|
20
20
|
|
21
|
-
|
22
|
-
|
21
|
+
require 'protocol/http/body/readable'
|
23
22
|
require 'async/queue'
|
24
23
|
|
25
24
|
module Async
|
26
25
|
module HTTP
|
27
26
|
module Body
|
27
|
+
include ::Protocol::HTTP::Body
|
28
|
+
|
28
29
|
# A dynamic body which you can write to and read from.
|
29
30
|
class Writable < Readable
|
30
31
|
class Closed < StandardError
|
data/lib/async/http/client.rb
CHANGED
@@ -21,13 +21,14 @@
|
|
21
21
|
require 'async/io/endpoint'
|
22
22
|
require 'async/io/stream'
|
23
23
|
|
24
|
+
require 'protocol/http/body/streamable'
|
25
|
+
require 'protocol/http/methods'
|
26
|
+
|
24
27
|
require_relative 'protocol'
|
25
|
-
require_relative 'body/streamable'
|
26
|
-
require_relative 'middleware'
|
27
28
|
|
28
29
|
module Async
|
29
30
|
module HTTP
|
30
|
-
class Client
|
31
|
+
class Client < ::Protocol::HTTP::Methods
|
31
32
|
# Provides a robust interface to a server.
|
32
33
|
# * If there are no connections, it will create one.
|
33
34
|
# * If there are already connections, it will reuse it.
|
@@ -53,6 +54,10 @@ module Async
|
|
53
54
|
attr :scheme
|
54
55
|
attr :authority
|
55
56
|
|
57
|
+
def secure?
|
58
|
+
@endpoint.secure?
|
59
|
+
end
|
60
|
+
|
56
61
|
def self.open(*args, &block)
|
57
62
|
client = self.new(*args)
|
58
63
|
|
@@ -69,8 +74,6 @@ module Async
|
|
69
74
|
@pool.close
|
70
75
|
end
|
71
76
|
|
72
|
-
include Methods
|
73
|
-
|
74
77
|
def call(request)
|
75
78
|
request.scheme ||= self.scheme
|
76
79
|
request.authority ||= self.authority
|
@@ -84,10 +87,10 @@ module Async
|
|
84
87
|
# As we cache pool, it's possible these pool go bad (e.g. closed by remote host). In this case, we need to try again. It's up to the caller to impose a timeout on this. If this is the last attempt, we force a new connection.
|
85
88
|
connection = @pool.acquire
|
86
89
|
|
87
|
-
response =
|
90
|
+
response = request.call(connection)
|
88
91
|
|
89
92
|
# The connection won't be released until the body is completely read/released.
|
90
|
-
Body::Streamable.wrap(response) do
|
93
|
+
::Protocol::HTTP::Body::Streamable.wrap(response) do
|
91
94
|
@pool.release(connection)
|
92
95
|
end
|
93
96
|
|
@@ -118,9 +121,7 @@ module Async
|
|
118
121
|
Pool.new(connection_limit) do
|
119
122
|
Async.logger.debug(self) {"Making connection to #{@endpoint.inspect}"}
|
120
123
|
|
121
|
-
|
122
|
-
|
123
|
-
@protocol.client(IO::Stream.new(peer))
|
124
|
+
@protocol.client(IO::Stream.new(@endpoint.connect))
|
124
125
|
end
|
125
126
|
end
|
126
127
|
end
|
data/lib/async/http/internet.rb
CHANGED
@@ -19,9 +19,9 @@
|
|
19
19
|
# THE SOFTWARE.
|
20
20
|
|
21
21
|
require_relative 'client'
|
22
|
-
require_relative '
|
23
|
-
|
24
|
-
|
22
|
+
require_relative 'endpoint'
|
23
|
+
require 'protocol/http/middleware'
|
24
|
+
require 'protocol/http/body/buffered'
|
25
25
|
|
26
26
|
module Async
|
27
27
|
module HTTP
|
@@ -31,7 +31,7 @@ module Async
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def call(method, url, headers = [], body = nil)
|
34
|
-
endpoint =
|
34
|
+
endpoint = Endpoint.parse(url)
|
35
35
|
|
36
36
|
client = @clients.fetch(endpoint) do
|
37
37
|
@clients[endpoint] = Client.new(endpoint)
|
@@ -39,7 +39,7 @@ module Async
|
|
39
39
|
|
40
40
|
body = Body::Buffered.wrap(body)
|
41
41
|
|
42
|
-
request = Request.new(client.scheme, endpoint.authority, method, endpoint.path, nil, headers, body)
|
42
|
+
request = ::Protocol::HTTP::Request.new(client.scheme, endpoint.authority, method, endpoint.path, nil, headers, body)
|
43
43
|
|
44
44
|
return client.call(request)
|
45
45
|
end
|
@@ -49,7 +49,7 @@ module Async
|
|
49
49
|
@clients.clear
|
50
50
|
end
|
51
51
|
|
52
|
-
|
52
|
+
::Protocol::HTTP::Methods.each do |name, verb|
|
53
53
|
define_method(verb.downcase) do |url, headers = [], body = nil|
|
54
54
|
self.call(verb, url.to_str, headers, body)
|
55
55
|
end
|
data/lib/async/http/pool.rb
CHANGED
@@ -31,23 +31,34 @@ module Async
|
|
31
31
|
|
32
32
|
# We carefully interpret https://tools.ietf.org/html/rfc7230#section-6.3.1 to implement this correctly.
|
33
33
|
begin
|
34
|
-
|
34
|
+
write_request(request.authority, request.method, request.path, @version, request.headers)
|
35
35
|
rescue
|
36
36
|
# If we fail to fully write the request and body, we can retry this request.
|
37
|
-
raise RequestFailed
|
37
|
+
raise RequestFailed
|
38
38
|
end
|
39
39
|
|
40
40
|
if request.body?
|
41
|
-
|
42
|
-
|
43
|
-
|
41
|
+
body = request.body
|
42
|
+
|
43
|
+
if protocol = request.protocol
|
44
|
+
# This is a very tricky apect of handling HTTP/1 upgrade connections. In theory, this approach is a bit inefficient, because we spin up a task just to handle writing to the underlying stream when we could be writing to the stream directly. But we need to maintain some level of compatibility with HTTP/2. Additionally, we don't know if the upgrade request will be accepted, so starting to write the body at this point needs to be handled with care.
|
45
|
+
task.async do
|
46
|
+
# If this fails, this connection will be closed.
|
47
|
+
write_upgrade_body(protocol, body)
|
48
|
+
end
|
49
|
+
else
|
50
|
+
task.async do
|
51
|
+
# Once we start writing the body, we can't recover if the request fails. That's because the body might be generated dynamically, streaming, etc.
|
52
|
+
write_body(@version, body)
|
53
|
+
end
|
44
54
|
end
|
55
|
+
elsif protocol = request.protocol
|
56
|
+
write_upgrade_body(protocol)
|
45
57
|
else
|
46
|
-
|
58
|
+
write_empty_body(request.body)
|
47
59
|
end
|
48
60
|
|
49
|
-
|
50
|
-
return Response.new(self, request)
|
61
|
+
return Response.read(self, request)
|
51
62
|
rescue
|
52
63
|
# This will ensure that #reusable? returns false.
|
53
64
|
@stream.close
|
@@ -23,10 +23,6 @@ require 'protocol/http1'
|
|
23
23
|
require_relative 'request'
|
24
24
|
require_relative 'response'
|
25
25
|
|
26
|
-
require_relative '../../body/chunked'
|
27
|
-
require_relative '../../body/fixed'
|
28
|
-
require_relative '../../body/remainder'
|
29
|
-
|
30
26
|
module Async
|
31
27
|
module HTTP
|
32
28
|
module Protocol
|
@@ -40,17 +36,16 @@ module Async
|
|
40
36
|
|
41
37
|
attr :version
|
42
38
|
|
43
|
-
def
|
44
|
-
|
39
|
+
def http1?
|
40
|
+
true
|
45
41
|
end
|
46
42
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
return @stream.io
|
43
|
+
def http2?
|
44
|
+
false
|
45
|
+
end
|
46
|
+
|
47
|
+
def read_line?
|
48
|
+
@stream.read_until(CRLF)
|
54
49
|
end
|
55
50
|
|
56
51
|
def peer
|
@@ -78,18 +73,6 @@ module Async
|
|
78
73
|
|
79
74
|
@stream.close
|
80
75
|
end
|
81
|
-
|
82
|
-
def read_chunked_body
|
83
|
-
Body::Chunked.new(self)
|
84
|
-
end
|
85
|
-
|
86
|
-
def read_fixed_body(length)
|
87
|
-
Body::Fixed.new(@stream, length)
|
88
|
-
end
|
89
|
-
|
90
|
-
def read_remainder_body
|
91
|
-
Body::Remainder.new(@stream)
|
92
|
-
end
|
93
76
|
end
|
94
77
|
end
|
95
78
|
end
|
@@ -25,15 +25,18 @@ module Async
|
|
25
25
|
module Protocol
|
26
26
|
module HTTP1
|
27
27
|
class Request < Protocol::Request
|
28
|
-
def self.read(
|
29
|
-
if parts =
|
30
|
-
self.new(
|
28
|
+
def self.read(connection)
|
29
|
+
if parts = connection.read_request
|
30
|
+
self.new(connection, *parts)
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
-
def initialize(
|
35
|
-
|
36
|
-
|
34
|
+
def initialize(connection, authority, method, path, version, headers, body)
|
35
|
+
@connection = connection
|
36
|
+
|
37
|
+
protocol = connection.upgrade?(headers)
|
38
|
+
|
39
|
+
super(nil, authority, method, path, version, headers, body, protocol)
|
37
40
|
end
|
38
41
|
|
39
42
|
def hijack?
|
@@ -41,7 +44,7 @@ module Async
|
|
41
44
|
end
|
42
45
|
|
43
46
|
def hijack!
|
44
|
-
@
|
47
|
+
@connection.hijack!
|
45
48
|
end
|
46
49
|
end
|
47
50
|
end
|
@@ -25,10 +25,26 @@ module Async
|
|
25
25
|
module Protocol
|
26
26
|
module HTTP1
|
27
27
|
class Response < Protocol::Response
|
28
|
-
def
|
29
|
-
|
28
|
+
def self.read(connection, request)
|
29
|
+
if parts = connection.read_response(request.method)
|
30
|
+
self.new(connection, *parts)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def initialize(connection, version, status, reason, headers, body)
|
35
|
+
@connection = connection
|
36
|
+
|
37
|
+
protocol = connection.upgrade?(headers)
|
30
38
|
|
31
|
-
|
39
|
+
super(version, status, reason, headers, body, protocol)
|
40
|
+
end
|
41
|
+
|
42
|
+
def hijack?
|
43
|
+
@body.nil?
|
44
|
+
end
|
45
|
+
|
46
|
+
def hijack!
|
47
|
+
@connection.hijack!
|
32
48
|
end
|
33
49
|
end
|
34
50
|
end
|