async-http 0.40.3 → 0.41.0

Sign up to get free protection for your applications and to get access to all the features.
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