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
@@ -66,10 +66,22 @@ module Async
|
|
66
66
|
request = nil
|
67
67
|
end
|
68
68
|
|
69
|
-
write_response(@version, response.status, response.headers
|
69
|
+
write_response(@version, response.status, response.headers)
|
70
|
+
|
71
|
+
if body = response.body and protocol = response.protocol
|
72
|
+
stream = write_upgrade_body(protocol)
|
73
|
+
|
74
|
+
# At this point, the request body is hijacked, so we don't want to call #finish below.
|
75
|
+
request = nil
|
76
|
+
|
77
|
+
body.call(stream)
|
78
|
+
else
|
79
|
+
write_body(@version, body, head)
|
80
|
+
end
|
70
81
|
else
|
71
82
|
# If the request failed to generate a response, it was an internal server error:
|
72
|
-
write_response(@version, 500, {}
|
83
|
+
write_response(@version, 500, {})
|
84
|
+
write_body(@version, nil)
|
73
85
|
end
|
74
86
|
|
75
87
|
# Gracefully finish reading the request body if it was not already done so.
|
@@ -25,11 +25,18 @@ module Async
|
|
25
25
|
module HTTP
|
26
26
|
module Protocol
|
27
27
|
module HTTP2
|
28
|
+
VERSION = "h2"
|
29
|
+
|
30
|
+
def self.bidirectional?
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
28
34
|
CLIENT_SETTINGS = {
|
29
35
|
::Protocol::HTTP2::Settings::ENABLE_PUSH => 0,
|
30
36
|
::Protocol::HTTP2::Settings::MAXIMUM_CONCURRENT_STREAMS => 256,
|
31
37
|
::Protocol::HTTP2::Settings::MAXIMUM_FRAME_SIZE => 0x100000,
|
32
38
|
::Protocol::HTTP2::Settings::INITIAL_WINDOW_SIZE => 0x7FFFFFFF,
|
39
|
+
::Protocol::HTTP2::Settings::ENABLE_CONNECT_PROTOCOL => 1,
|
33
40
|
}
|
34
41
|
|
35
42
|
SERVER_SETTINGS = {
|
@@ -38,6 +45,7 @@ module Async
|
|
38
45
|
::Protocol::HTTP2::Settings::MAXIMUM_CONCURRENT_STREAMS => 32,
|
39
46
|
::Protocol::HTTP2::Settings::MAXIMUM_FRAME_SIZE => 0x100000,
|
40
47
|
::Protocol::HTTP2::Settings::INITIAL_WINDOW_SIZE => 0x7FFFFFFF,
|
48
|
+
::Protocol::HTTP2::Settings::ENABLE_CONNECT_PROTOCOL => 1,
|
41
49
|
}
|
42
50
|
|
43
51
|
def self.client(stream, settings = CLIENT_SETTINGS)
|
@@ -31,7 +31,7 @@ module Async
|
|
31
31
|
AUTHORITY = ':authority'.freeze
|
32
32
|
REASON = 'reason'.freeze
|
33
33
|
STATUS = ':status'.freeze
|
34
|
-
|
34
|
+
PROTOCOL = ':protocol'.freeze
|
35
35
|
|
36
36
|
CONTENT_LENGTH = 'content-length'
|
37
37
|
|
@@ -45,6 +45,14 @@ module Async
|
|
45
45
|
|
46
46
|
attr :stream
|
47
47
|
|
48
|
+
def http1?
|
49
|
+
false
|
50
|
+
end
|
51
|
+
|
52
|
+
def http2?
|
53
|
+
true
|
54
|
+
end
|
55
|
+
|
48
56
|
def start_connection
|
49
57
|
@reader ||= read_in_background
|
50
58
|
end
|
@@ -97,7 +105,8 @@ module Async
|
|
97
105
|
Async.logger.debug(self) {"Closing connection"}
|
98
106
|
|
99
107
|
@reader.stop if @reader
|
100
|
-
|
108
|
+
|
109
|
+
super
|
101
110
|
end
|
102
111
|
end
|
103
112
|
end
|
@@ -35,8 +35,8 @@ module Async
|
|
35
35
|
attr :request
|
36
36
|
|
37
37
|
private def build_request(headers)
|
38
|
-
request = HTTP::Request.new
|
39
|
-
request.headers = Headers.new
|
38
|
+
request = ::Protocol::HTTP::Request.new
|
39
|
+
request.headers = ::Protocol::HTTP::Headers.new
|
40
40
|
|
41
41
|
headers.each do |key, value|
|
42
42
|
if key == SCHEME
|
@@ -26,12 +26,12 @@ module Async
|
|
26
26
|
module Protocol
|
27
27
|
module HTTP2
|
28
28
|
class Request < Protocol::Request
|
29
|
-
def initialize(
|
30
|
-
super(nil, nil, nil, nil, VERSION, Headers.new)
|
29
|
+
def initialize(connection, stream_id)
|
30
|
+
super(nil, nil, nil, nil, VERSION, ::Protocol::HTTP::Headers.new)
|
31
31
|
|
32
32
|
@input = nil
|
33
|
-
@
|
34
|
-
@stream = Stream.new(self,
|
33
|
+
@connection = connection
|
34
|
+
@stream = Stream.new(self, connection, stream_id)
|
35
35
|
end
|
36
36
|
|
37
37
|
attr :stream
|
@@ -41,11 +41,11 @@ module Async
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def push?
|
44
|
-
@
|
44
|
+
@connection.enable_push?
|
45
45
|
end
|
46
46
|
|
47
47
|
def create_promise_stream(headers, stream_id)
|
48
|
-
request = self.class.new(@
|
48
|
+
request = self.class.new(@connection, stream_id)
|
49
49
|
request.receive_headers(self, headers, false)
|
50
50
|
|
51
51
|
return request.stream
|
@@ -58,7 +58,7 @@ module Async
|
|
58
58
|
def push(path, headers = nil)
|
59
59
|
push_headers = [
|
60
60
|
[SCHEME, @scheme],
|
61
|
-
[METHOD, GET],
|
61
|
+
[METHOD, ::Protocol::HTTP::Methods::GET],
|
62
62
|
[PATH, path],
|
63
63
|
[AUTHORITY, @authority]
|
64
64
|
]
|
@@ -91,6 +91,10 @@ module Async
|
|
91
91
|
return @stream.send_failure(400, "Request path already specified") if @path
|
92
92
|
|
93
93
|
@path = value
|
94
|
+
elsif key == PROTOCOL
|
95
|
+
return @stream.send_failure(400, "Request protocol already specified") if @protocol
|
96
|
+
|
97
|
+
@protocol = value
|
94
98
|
else
|
95
99
|
@headers[key] = value
|
96
100
|
end
|
@@ -102,7 +106,7 @@ module Async
|
|
102
106
|
end
|
103
107
|
|
104
108
|
# We are ready for processing:
|
105
|
-
@
|
109
|
+
@connection.requests.enqueue self
|
106
110
|
end
|
107
111
|
|
108
112
|
def receive_data(stream, data, end_stream)
|
@@ -138,7 +142,11 @@ module Async
|
|
138
142
|
pseudo_headers << [CONTENT_LENGTH, length]
|
139
143
|
end
|
140
144
|
|
141
|
-
|
145
|
+
if protocol = response.protocol
|
146
|
+
pseudo_headers << [PROTOCOL, protocol]
|
147
|
+
end
|
148
|
+
|
149
|
+
headers = ::Protocol::HTTP::Headers::Merged.new(
|
142
150
|
pseudo_headers,
|
143
151
|
response.headers
|
144
152
|
)
|
@@ -25,14 +25,14 @@ module Async
|
|
25
25
|
module Protocol
|
26
26
|
module HTTP2
|
27
27
|
class Response < Protocol::Response
|
28
|
-
def initialize(
|
28
|
+
def initialize(connection, stream_id)
|
29
29
|
@input = nil
|
30
30
|
@length = nil
|
31
31
|
|
32
|
-
super(
|
32
|
+
super(connection.version, nil, nil, ::Protocol::HTTP::Headers.new)
|
33
33
|
|
34
|
-
@
|
35
|
-
@stream = Stream.new(self,
|
34
|
+
@connection = connection
|
35
|
+
@stream = Stream.new(self, connection, stream_id)
|
36
36
|
|
37
37
|
@notification = Async::Notification.new
|
38
38
|
@exception = nil
|
@@ -45,7 +45,7 @@ module Async
|
|
45
45
|
end
|
46
46
|
|
47
47
|
def create_promise_stream(headers, stream_id)
|
48
|
-
promise = Promise.new(@
|
48
|
+
promise = Promise.new(@connection, headers, stream_id)
|
49
49
|
|
50
50
|
self.promises.enqueue(promise)
|
51
51
|
|
@@ -89,6 +89,8 @@ module Async
|
|
89
89
|
@reason = value
|
90
90
|
elsif key == CONTENT_LENGTH
|
91
91
|
@length = Integer(value)
|
92
|
+
elsif key == PROTOCOL
|
93
|
+
@protocol = value
|
92
94
|
else
|
93
95
|
@headers[key] = value
|
94
96
|
end
|
@@ -142,7 +144,11 @@ module Async
|
|
142
144
|
pseudo_headers << [AUTHORITY, authority]
|
143
145
|
end
|
144
146
|
|
145
|
-
|
147
|
+
if protocol = request.protocol
|
148
|
+
pseudo_headers << [PROTOCOL, protocol]
|
149
|
+
end
|
150
|
+
|
151
|
+
headers = ::Protocol::HTTP::Headers::Merged.new(
|
146
152
|
pseudo_headers,
|
147
153
|
request.headers
|
148
154
|
)
|
@@ -55,19 +55,21 @@ module Async
|
|
55
55
|
@requests.enqueue nil
|
56
56
|
end
|
57
57
|
|
58
|
-
def each
|
58
|
+
def each(task: Task.current)
|
59
59
|
while request = @requests.dequeue
|
60
60
|
@count += 1
|
61
61
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
62
|
+
task.async do
|
63
|
+
begin
|
64
|
+
response = yield(request)
|
65
|
+
rescue
|
66
|
+
# We need to close the stream if the user code blows up while generating a response:
|
67
|
+
request.stream.send_reset_stream(::Protocol::HTTP2::INTERNAL_ERROR)
|
68
|
+
|
69
|
+
raise
|
70
|
+
else
|
71
|
+
request.send_response(response)
|
72
|
+
end
|
71
73
|
end
|
72
74
|
end
|
73
75
|
end
|
@@ -18,8 +18,8 @@
|
|
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/request'
|
22
|
+
require 'protocol/http/headers'
|
23
23
|
|
24
24
|
require_relative '../body/writable'
|
25
25
|
|
@@ -31,8 +31,8 @@ module Async
|
|
31
31
|
end
|
32
32
|
|
33
33
|
# This is generated by server protocols.
|
34
|
-
class Request < HTTP::Request
|
35
|
-
attr :
|
34
|
+
class Request < ::Protocol::HTTP::Request
|
35
|
+
attr :connection
|
36
36
|
|
37
37
|
def hijack?
|
38
38
|
false
|
@@ -43,8 +43,8 @@ module Async
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def peer
|
46
|
-
if @
|
47
|
-
@
|
46
|
+
if @connection
|
47
|
+
@connection.peer
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
@@ -18,8 +18,7 @@
|
|
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
|
-
require_relative '../headers'
|
21
|
+
require 'protocol/http/response'
|
23
22
|
|
24
23
|
require_relative '../body/writable'
|
25
24
|
|
@@ -27,16 +26,16 @@ module Async
|
|
27
26
|
module HTTP
|
28
27
|
module Protocol
|
29
28
|
# This is generated by client protocols.
|
30
|
-
class Response < HTTP::Response
|
31
|
-
attr :
|
29
|
+
class Response < ::Protocol::HTTP::Response
|
30
|
+
attr :connection
|
32
31
|
|
33
32
|
def hijack?
|
34
33
|
false
|
35
34
|
end
|
36
35
|
|
37
36
|
def peer
|
38
|
-
if @
|
39
|
-
@
|
37
|
+
if @connection
|
38
|
+
@connection.peer
|
40
39
|
end
|
41
40
|
end
|
42
41
|
|
@@ -19,15 +19,16 @@
|
|
19
19
|
# THE SOFTWARE.
|
20
20
|
|
21
21
|
require_relative 'client'
|
22
|
-
require_relative '
|
23
|
-
|
22
|
+
require_relative 'endpoint'
|
24
23
|
require_relative 'reference'
|
25
24
|
|
25
|
+
require 'protocol/http/middleware'
|
26
|
+
|
26
27
|
module Async
|
27
28
|
module HTTP
|
28
29
|
# A client wrapper which transparently handles both relative and absolute redirects to a given maximum number of hops.
|
29
|
-
class RelativeLocation < Middleware
|
30
|
-
DEFAULT_METHOD =
|
30
|
+
class RelativeLocation < ::Protocol::HTTP::Middleware
|
31
|
+
DEFAULT_METHOD = GET
|
31
32
|
|
32
33
|
def initialize(app, maximum_hops = 4)
|
33
34
|
super(app)
|
data/lib/async/http/server.rb
CHANGED
@@ -22,11 +22,11 @@ require 'async/io/endpoint'
|
|
22
22
|
require 'async/io/stream'
|
23
23
|
|
24
24
|
require_relative 'protocol'
|
25
|
-
|
25
|
+
require 'protocol/http/middleware'
|
26
26
|
|
27
27
|
module Async
|
28
28
|
module HTTP
|
29
|
-
class Server < Middleware
|
29
|
+
class Server < ::Protocol::HTTP::Middleware
|
30
30
|
def self.for(*args, &block)
|
31
31
|
self.new(block, *args)
|
32
32
|
end
|
@@ -39,15 +39,17 @@ module Async
|
|
39
39
|
@scheme = scheme
|
40
40
|
end
|
41
41
|
|
42
|
+
attr :endpoint
|
43
|
+
attr :protocol
|
42
44
|
attr :scheme
|
43
45
|
|
44
46
|
def accept(peer, address, task: Task.current)
|
45
47
|
stream = Async::IO::Stream.new(peer)
|
46
|
-
|
48
|
+
connection = @protocol.server(stream)
|
47
49
|
|
48
|
-
Async.logger.debug(self) {"Incoming connnection from #{address.inspect} to #{protocol}"}
|
50
|
+
Async.logger.debug(self) {"Incoming connnection from #{address.inspect} to #{@protocol}"}
|
49
51
|
|
50
|
-
|
52
|
+
connection.each do |request|
|
51
53
|
# We set the default scheme unless it was otherwise specified.
|
52
54
|
# https://tools.ietf.org/html/rfc7230#section-5.5
|
53
55
|
request.scheme ||= self.scheme
|
@@ -18,7 +18,7 @@
|
|
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/wrapper'
|
22
22
|
|
23
23
|
require 'async/clock'
|
24
24
|
|
@@ -44,7 +44,7 @@ module Async
|
|
44
44
|
|
45
45
|
module Body
|
46
46
|
# Invokes a callback once the body has finished reading.
|
47
|
-
class Statistics < Wrapper
|
47
|
+
class Statistics < ::Protocol::HTTP::Body::Wrapper
|
48
48
|
def initialize(start_time, body, callback)
|
49
49
|
super(body)
|
50
50
|
|
data/lib/async/http/version.rb
CHANGED
data/tasks/server.rake
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
|
2
2
|
task :http1 do
|
3
3
|
require 'async/http/protocol'
|
4
|
-
require 'async/http/
|
4
|
+
require 'async/http/endpoint'
|
5
5
|
require 'async/io/host_endpoint'
|
6
6
|
|
7
7
|
@protocol = Async::HTTP::Protocol::HTTP1
|
@@ -18,7 +18,7 @@ task :google do
|
|
18
18
|
require 'pry'
|
19
19
|
|
20
20
|
Async.run do
|
21
|
-
endpoint = Async::HTTP::
|
21
|
+
endpoint = Async::HTTP::Endpoint.parse("https://www.google.com")
|
22
22
|
peer = endpoint.connect
|
23
23
|
stream = Async::IO::Stream.new(peer)
|
24
24
|
|
@@ -44,7 +44,7 @@ task :server do
|
|
44
44
|
require 'async/http/server'
|
45
45
|
|
46
46
|
server = Async::HTTP::Server.for(Async::IO::Endpoint.tcp('127.0.0.1', 9294, reuse_port: true), @protocol) do |request|
|
47
|
-
return
|
47
|
+
return Protocol::HTTP::Response[200, {'content-type' => 'text/plain'}, ["Hello World"]]
|
48
48
|
end
|
49
49
|
|
50
50
|
container = Async::Container::Forked.new(concurrency: 1) do
|
@@ -81,7 +81,7 @@ task :wrk do
|
|
81
81
|
require 'async/container/forked'
|
82
82
|
|
83
83
|
server = Async::HTTP::Server.for(Async::IO::Endpoint.tcp('127.0.0.1', 9294, reuse_port: true), @protocol) do |request|
|
84
|
-
return
|
84
|
+
return Protocol::HTTP::Response[200, {'content-type' => 'text/plain'}, ["Hello World"]]
|
85
85
|
end
|
86
86
|
|
87
87
|
concurrency = 1
|