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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -4
  3. data/async-http.gemspec +3 -3
  4. data/examples/request.rb +3 -3
  5. data/examples/upload/client.rb +2 -2
  6. data/examples/upload/server.rb +3 -3
  7. data/lib/async/http.rb +1 -1
  8. data/lib/async/http/body.rb +2 -11
  9. data/lib/async/http/body/hijack.rb +3 -3
  10. data/lib/async/http/body/writable.rb +3 -2
  11. data/lib/async/http/client.rb +11 -10
  12. data/lib/async/http/{url_endpoint.rb → endpoint.rb} +1 -1
  13. data/lib/async/http/internet.rb +6 -6
  14. data/lib/async/http/pool.rb +1 -1
  15. data/lib/async/http/protocol/http1/client.rb +19 -8
  16. data/lib/async/http/protocol/http1/connection.rb +8 -25
  17. data/lib/async/http/protocol/http1/request.rb +10 -7
  18. data/lib/async/http/protocol/http1/response.rb +19 -3
  19. data/lib/async/http/protocol/http1/server.rb +14 -2
  20. data/lib/async/http/protocol/http10.rb +4 -0
  21. data/lib/async/http/protocol/http11.rb +4 -0
  22. data/lib/async/http/protocol/http2.rb +8 -0
  23. data/lib/async/http/protocol/http2/connection.rb +11 -2
  24. data/lib/async/http/protocol/http2/promise.rb +2 -2
  25. data/lib/async/http/protocol/http2/request.rb +17 -9
  26. data/lib/async/http/protocol/http2/response.rb +12 -6
  27. data/lib/async/http/protocol/http2/server.rb +12 -10
  28. data/lib/async/http/protocol/http2/stream.rb +1 -1
  29. data/lib/async/http/protocol/request.rb +6 -6
  30. data/lib/async/http/protocol/response.rb +5 -6
  31. data/lib/async/http/relative_location.rb +5 -4
  32. data/lib/async/http/server.rb +7 -5
  33. data/lib/async/http/statistics.rb +2 -2
  34. data/lib/async/http/version.rb +1 -1
  35. data/tasks/server.rake +4 -4
  36. metadata +10 -29
  37. data/lib/async/http/accept_encoding.rb +0 -65
  38. data/lib/async/http/body/buffered.rb +0 -89
  39. data/lib/async/http/body/chunked.rb +0 -76
  40. data/lib/async/http/body/deflate.rb +0 -113
  41. data/lib/async/http/body/file.rb +0 -98
  42. data/lib/async/http/body/fixed.rb +0 -72
  43. data/lib/async/http/body/inflate.rb +0 -59
  44. data/lib/async/http/body/readable.rb +0 -76
  45. data/lib/async/http/body/reader.rb +0 -83
  46. data/lib/async/http/body/remainder.rb +0 -56
  47. data/lib/async/http/body/rewindable.rb +0 -60
  48. data/lib/async/http/body/streamable.rb +0 -83
  49. data/lib/async/http/body/wrapper.rb +0 -65
  50. data/lib/async/http/content_encoding.rb +0 -76
  51. data/lib/async/http/headers.rb +0 -27
  52. data/lib/async/http/middleware.rb +0 -79
  53. data/lib/async/http/middleware/builder.rb +0 -61
  54. data/lib/async/http/request.rb +0 -71
  55. 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, response.body, head)
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, {}, nil)
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.
@@ -26,6 +26,10 @@ module Async
26
26
  module HTTP10
27
27
  VERSION = "HTTP/1.0"
28
28
 
29
+ def self.bidirectional?
30
+ false
31
+ end
32
+
29
33
  def self.client(stream)
30
34
  HTTP1::Client.new(stream, VERSION)
31
35
  end
@@ -26,6 +26,10 @@ module Async
26
26
  module HTTP11
27
27
  VERSION = "HTTP/1.1"
28
28
 
29
+ def self.bidirectional?
30
+ true
31
+ end
32
+
29
33
  def self.client(stream)
30
34
  HTTP1::Client.new(stream, VERSION)
31
35
  end
@@ -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
- VERSION = 'HTTP/2.0'.freeze
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
- @stream.close
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(protocol, stream_id)
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
- @protocol = protocol
34
- @stream = Stream.new(self, protocol, stream_id)
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
- @protocol.enable_push?
44
+ @connection.enable_push?
45
45
  end
46
46
 
47
47
  def create_promise_stream(headers, stream_id)
48
- request = self.class.new(@protocol, stream_id)
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
- @protocol.requests.enqueue self
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
- headers = Headers::Merged.new(
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(protocol, stream_id)
28
+ def initialize(connection, stream_id)
29
29
  @input = nil
30
30
  @length = nil
31
31
 
32
- super(protocol.version, nil, nil, Headers.new, nil)
32
+ super(connection.version, nil, nil, ::Protocol::HTTP::Headers.new)
33
33
 
34
- @protocol = protocol
35
- @stream = Stream.new(self, protocol, stream_id)
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(@protocol, headers, stream_id)
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
- headers = Headers::Merged.new(
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
- # We need to close the stream if the user code blows up while generating a response:
63
- response = begin
64
- response = yield(request)
65
- rescue
66
- request.stream.send_reset_stream(::Protocol::HTTP2::INTERNAL_ERROR)
67
-
68
- Async.logger.error(request) {$!}
69
- else
70
- request.send_response(response)
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
@@ -79,7 +79,7 @@ module Async
79
79
  end
80
80
 
81
81
  # @body.read above might take a while and a stream reset might be received in the mean time.
82
- unless closed?
82
+ unless closed? or @connection.closed?
83
83
  send_data(nil, ::Protocol::HTTP2::END_STREAM)
84
84
  end
85
85
 
@@ -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
- require_relative '../request'
22
- require_relative '../headers'
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 :protocol
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 @protocol
47
- @protocol.peer
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
- require_relative '../response'
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 :protocol
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 @protocol
39
- @protocol.peer
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 'url_endpoint'
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 = 'GET'.freeze
30
+ class RelativeLocation < ::Protocol::HTTP::Middleware
31
+ DEFAULT_METHOD = GET
31
32
 
32
33
  def initialize(app, maximum_hops = 4)
33
34
  super(app)
@@ -22,11 +22,11 @@ require 'async/io/endpoint'
22
22
  require 'async/io/stream'
23
23
 
24
24
  require_relative 'protocol'
25
- require_relative 'response'
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
- protocol = @protocol.server(stream)
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
- protocol.each do |request|
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
- require_relative 'body/wrapper'
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
 
@@ -20,6 +20,6 @@
20
20
 
21
21
  module Async
22
22
  module HTTP
23
- VERSION = "0.40.3"
23
+ VERSION = "0.41.0"
24
24
  end
25
25
  end
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/url_endpoint'
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::URLEndpoint.parse("https://www.google.com")
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 Async::HTTP::Response[200, {'content-type' => 'text/plain'}, ["Hello World"]]
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 Async::HTTP::Response[200, {'content-type' => 'text/plain'}, ["Hello World"]]
84
+ return Protocol::HTTP::Response[200, {'content-type' => 'text/plain'}, ["Hello World"]]
85
85
  end
86
86
 
87
87
  concurrency = 1