async-http 0.30.4 → 0.31.1

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: 988a06a8b08c2251198294138b4bd8a8ef679bf072633b0db804abb3b80424c8
4
- data.tar.gz: a923e52d7d7c4b38c32e7db4a49867daad1a6e0d3393fcd524a615f793ebcc95
3
+ metadata.gz: 20c4728c088a68663d0f336b2326d3e988a342c5d1f1cd2890a12e43cefda133
4
+ data.tar.gz: da0503f98a16003b0a2c6606e4419ebb311365dac4be7ea685b18bb994b3613c
5
5
  SHA512:
6
- metadata.gz: c03b5e2b487e5071447db19ebb738986a434e121fb26d98676ffd262262682d63e612963ba46b4716de649a3558ea843f92dd2fcceef721d499759343450f077
7
- data.tar.gz: 1191e5d82efd60553715e771cf13ddca15348dc7b241ecae2ba546147b6ca344518fef3588203f55f35e3b35886ac6049c1bef5b1e916e37a3c23112073c61bd
6
+ metadata.gz: 5ecdd9b68abcaf50db65f02ab8f1db4e8ce1e4a00af980510df5d1323a0d4c3b2c135d85fe3683dcf02f5e818b364fe659c44ab5df25459b687ba2a50d8db9e2
7
+ data.tar.gz: 68536e0f1ea03914f0e4b34116e7a7f738c5fbc7cf2baf9f2d87f7dd6675329c6c48b444ece69cf8a977f4184a525df1fc14b25bb3f7cb3a85c4983519551eed
@@ -17,9 +17,9 @@ Gem::Specification.new do |spec|
17
17
  spec.require_paths = ["lib"]
18
18
 
19
19
  spec.add_dependency("async", "~> 1.6")
20
- spec.add_dependency("async-io", "~> 1.15")
20
+ spec.add_dependency("async-io", "~> 1.16")
21
21
 
22
- spec.add_dependency("http-protocol", "~> 0.3.0")
22
+ spec.add_dependency("http-protocol", "~> 0.5.0")
23
23
 
24
24
  # spec.add_dependency("openssl")
25
25
 
@@ -19,6 +19,7 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  require_relative 'readable'
22
+ require 'async/io/stream'
22
23
 
23
24
  module Async
24
25
  module HTTP
@@ -19,6 +19,7 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  require 'async/io/endpoint'
22
+ require 'async/io/stream'
22
23
 
23
24
  require_relative 'protocol'
24
25
  require_relative 'body/streamable'
@@ -136,8 +136,8 @@ module Async
136
136
  # This is a linear search... not idea, but simple for now.
137
137
  @resources.each do |resource, count|
138
138
  if count < resource.multiplex
139
- # We want to use this resource... but is it good?
140
- if resource.good?
139
+ # We want to use this resource... but is it connected?
140
+ if resource.connected?
141
141
  @resources[resource] += 1
142
142
 
143
143
  return resource
@@ -20,3 +20,27 @@
20
20
 
21
21
  require_relative 'protocol/http1'
22
22
  require_relative 'protocol/https'
23
+
24
+ module Async
25
+ module HTTP
26
+ # A protocol specifies a way in which to communicate with a remote peer.
27
+ module Protocol
28
+ # A protocol must implement the following interface:
29
+ # class Protocol
30
+ # def client(stream) -> Connection
31
+ # def server(stream) -> Connection
32
+ # end
33
+
34
+ # A connection must implement the following interface:
35
+ # class Connection
36
+ # def multiplex -> can invoke call 1 or more times simultaneously.
37
+ # def reusable? -> can be used again/persistent connection.
38
+
39
+ # def connected? -> Boolean
40
+
41
+ # def call(request) -> Response
42
+ # def each -> (yield(request) -> Response)
43
+ # end
44
+ end
45
+ end
46
+ end
@@ -27,37 +27,28 @@ module Async
27
27
  module HTTP
28
28
  module Protocol
29
29
  # A server that supports both HTTP1.0 and HTTP1.1 semantics by detecting the version of the request.
30
- class HTTP1 < Async::IO::Protocol::Line
31
- HANDLERS = {
32
- "HTTP/1.0" => HTTP10,
33
- "HTTP/1.1" => HTTP11,
34
- }
35
-
36
- def initialize(stream)
37
- super(stream, HTTP11::CRLF)
38
- end
39
-
40
- class << self
41
- def client(*args)
42
- HTTP11.new(*args)
43
- end
44
-
45
- alias server new
46
- end
47
-
48
- def create_handler(version)
49
- if klass = HANDLERS[version]
50
- klass.server(@stream)
51
- else
52
- raise RuntimeError, "Unsupported protocol version #{version}"
53
- end
30
+ module HTTP1
31
+ def self.client(*args)
32
+ HTTP11.client(*args)
54
33
  end
55
34
 
56
- def receive_requests(&block)
57
- method, path, version = self.peek_line.split(/\s+/, 3)
58
-
59
- create_handler(version).receive_requests(&block)
35
+ def self.server(*args)
36
+ HTTP11.server(*args)
60
37
  end
38
+ #
39
+ # def create_handler(version)
40
+ # if klass = HANDLERS[version]
41
+ # klass.server(@stream)
42
+ # else
43
+ # raise RuntimeError, "Unsupported protocol version #{version}"
44
+ # end
45
+ # end
46
+ #
47
+ # def receive_requests(&block)
48
+ # method, path, version = self.peek_line.split(/\s+/, 3)
49
+ #
50
+ # create_handler(version).receive_requests(&block)
51
+ # end
61
52
  end
62
53
  end
63
54
  end
@@ -0,0 +1,55 @@
1
+ # Copyright, 2017, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require 'http/protocol/http11/connection'
22
+ require_relative '../http1/connection'
23
+
24
+ module Async
25
+ module HTTP
26
+ module Protocol
27
+ module HTTP1
28
+ module Client
29
+ # Used by the client to send requests to the remote server.
30
+ def call(request)
31
+ Async.logger.debug(self) {"#{request.method} #{request.path} #{request.headers.inspect}"}
32
+
33
+ # We carefully interpret https://tools.ietf.org/html/rfc7230#section-6.3.1 to implement this correctly.
34
+ begin
35
+ self.write_request(request.authority, request.method, request.path, self.version, request.headers)
36
+ rescue
37
+ # If we fail to fully write the request and body, we can retry this request.
38
+ raise RequestFailed.new
39
+ end
40
+
41
+ # 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.
42
+ self.write_body(request.body)
43
+
44
+ return Response.new(self, request)
45
+ rescue
46
+ # This will ensure that #reusable? returns false.
47
+ @stream.close
48
+
49
+ raise
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,94 @@
1
+ # Copyright, 2017, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require_relative 'request'
22
+ require_relative 'response'
23
+
24
+ require_relative '../../body/chunked'
25
+ require_relative '../../body/fixed'
26
+
27
+ module Async
28
+ module HTTP
29
+ module Protocol
30
+ module HTTP1
31
+ module Connection
32
+ CRLF = "\r\n"
33
+
34
+ attr :stream
35
+
36
+ def read_line
37
+ @stream.read_until(CRLF) or raise EOFError
38
+ end
39
+
40
+ # @return [Async::Wrapper] the underlying non-blocking IO.
41
+ def hijack
42
+ @persistent = false
43
+
44
+ @stream.flush
45
+
46
+ return @stream.io
47
+ end
48
+
49
+ def peer
50
+ @stream.io
51
+ end
52
+
53
+ attr :count
54
+
55
+ def multiplex
56
+ 1
57
+ end
58
+
59
+ # Can we use this connection to make requests?
60
+ def connected?
61
+ @stream.connected?
62
+ end
63
+
64
+ def reusable?
65
+ !@stream.closed?
66
+ # !(self.closed? || @stream.closed?)
67
+ end
68
+
69
+ def close
70
+ Async.logger.debug(self) {"Closing connection"}
71
+
72
+ @stream.close
73
+ end
74
+
75
+ def read_chunked_body
76
+ Body::Chunked.new(self)
77
+ end
78
+
79
+ def read_fixed_body(length)
80
+ Body::Fixed.new(@stream, length)
81
+ end
82
+
83
+ def read_tunnel_body
84
+ read_remainder_body
85
+ end
86
+
87
+ def read_remainder_body
88
+ Body::Remainder.new(@stream)
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,45 @@
1
+ # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require_relative '../request'
22
+
23
+ module Async
24
+ module HTTP
25
+ module Protocol
26
+ module HTTP1
27
+ class Request < Protocol::Request
28
+ def initialize(protocol)
29
+ super(*protocol.read_request)
30
+
31
+ @protocol = protocol
32
+ end
33
+
34
+ def hijack?
35
+ true
36
+ end
37
+
38
+ def hijack
39
+ @protocol.hijack
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,37 @@
1
+ # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require_relative '../response'
22
+
23
+ module Async
24
+ module HTTP
25
+ module Protocol
26
+ module HTTP1
27
+ class Response < Protocol::Response
28
+ def initialize(protocol, request)
29
+ super(*protocol.read_response(request.method))
30
+
31
+ @protocol = protocol
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,72 @@
1
+ # Copyright, 2017, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require 'http/protocol/http11/connection'
22
+ require_relative '../http1/connection'
23
+
24
+ module Async
25
+ module HTTP
26
+ module Protocol
27
+ module HTTP1
28
+ module Server
29
+ def next_request
30
+ # The default is true.
31
+ return nil unless @persistent
32
+
33
+ request = Request.new(self)
34
+
35
+ unless persistent?(request.headers)
36
+ @persistent = false
37
+ end
38
+
39
+ return request
40
+ rescue
41
+ # Bad Request
42
+ write_response(self.version, 400, {}, nil)
43
+
44
+ raise
45
+ end
46
+
47
+ # Server loop.
48
+ def each(task: Task.current)
49
+ while request = next_request
50
+ response = yield(request, self)
51
+
52
+ return if @stream.closed?
53
+
54
+ if response
55
+ write_response(self.version, response.status, response.headers, response.body, request.head?)
56
+ else
57
+ # If the request failed to generate a response, it was an internal server error:
58
+ write_response(self.version, 500, {}, nil)
59
+ end
60
+
61
+ # Gracefully finish reading the request body if it was not already done so.
62
+ request.finish
63
+
64
+ # This ensures we yield at least once every iteration of the loop and allow other fibers to execute.
65
+ task.yield
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end