async-http 0.19.0 → 0.20.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/async-http.gemspec +2 -2
- data/lib/async/http/body/fixed.rb +1 -1
- data/lib/async/http/body/writable.rb +4 -1
- data/lib/async/http/client.rb +19 -17
- data/lib/async/http/protocol/http10.rb +5 -5
- data/lib/async/http/protocol/http11.rb +15 -8
- data/lib/async/http/protocol/http2.rb +15 -6
- data/lib/async/http/server.rb +2 -3
- data/lib/async/http/statistics.rb +3 -1
- data/lib/async/http/version.rb +1 -1
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b40fa9e658f1c38dbd0a8c6ecdfb201af8e6bc504dfb30697b776d6fdf001eac
|
4
|
+
data.tar.gz: 714670c4acf8f6b961558d3ea336e81e1d596db29ecbe4df66dbe8349d257975
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 740c053ba517dd857b7b612d9e10054edc0e63f3a16062cecde65f042a1fc8036e9c9c299c258f894de91fbb2fa1ab8ad1f27b4ecacce7527fb166c76774b5f8
|
7
|
+
data.tar.gz: fed266d04a67864ab917d61162b577bd2a24a8567eea6cbc59df5535558d735b0f5e5ef5e902a6b27bac7343ecfc0eec9654d105f346b2492f4d0edb3da5b55b
|
data/async-http.gemspec
CHANGED
@@ -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.
|
20
|
+
spec.add_dependency("async-io", "~> 1.8")
|
21
21
|
|
22
|
-
spec.add_dependency("http-2", "~> 0.
|
22
|
+
spec.add_dependency("http-2", "~> 0.9.0")
|
23
23
|
# spec.add_dependency("openssl")
|
24
24
|
|
25
25
|
spec.add_development_dependency "async-rspec", "~> 1.1"
|
@@ -51,6 +51,7 @@ module Async
|
|
51
51
|
return chunk
|
52
52
|
end
|
53
53
|
|
54
|
+
# Cause the next call to write to fail with the given error.
|
54
55
|
def stop(error)
|
55
56
|
@stopped = error
|
56
57
|
end
|
@@ -67,13 +68,15 @@ module Async
|
|
67
68
|
@queue.enqueue(chunk)
|
68
69
|
end
|
69
70
|
|
71
|
+
alias << write
|
72
|
+
|
70
73
|
# Signal that output has finished.
|
71
74
|
def finish
|
72
75
|
@queue.enqueue(nil)
|
73
76
|
end
|
74
77
|
|
75
78
|
def inspect
|
76
|
-
"\#<#{self.class} #{@count} chunks written>"
|
79
|
+
"\#<#{self.class} #{@count} chunks written#{@finished ? ', finished' : ''}>"
|
77
80
|
end
|
78
81
|
end
|
79
82
|
end
|
data/lib/async/http/client.rb
CHANGED
@@ -59,17 +59,24 @@ module Async
|
|
59
59
|
include Verbs
|
60
60
|
|
61
61
|
def call(request)
|
62
|
-
connection = @connections.acquire
|
63
|
-
|
64
62
|
request.authority ||= @authority
|
65
|
-
response = connection.call(request)
|
66
63
|
|
67
|
-
#
|
68
|
-
|
69
|
-
@connections.
|
64
|
+
# As we cache connections, it's possible these connections 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.
|
65
|
+
while true
|
66
|
+
connection = @connections.acquire
|
67
|
+
|
68
|
+
if response = connection.call(request)
|
69
|
+
# The connection won't be released until the body is completely read/released.
|
70
|
+
Body::Streamable.wrap(response) do
|
71
|
+
@connections.release(connection)
|
72
|
+
end
|
73
|
+
|
74
|
+
return response
|
75
|
+
else
|
76
|
+
# The connection failed for some reason, we close it.
|
77
|
+
connection.close
|
78
|
+
end
|
70
79
|
end
|
71
|
-
|
72
|
-
return response
|
73
80
|
end
|
74
81
|
|
75
82
|
protected
|
@@ -78,15 +85,10 @@ module Async
|
|
78
85
|
Pool.new(connection_limit) do
|
79
86
|
Async.logger.debug(self) {"Making connection to #{@endpoint.inspect}"}
|
80
87
|
|
81
|
-
@endpoint.
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
stream = IO::Stream.new(peer)
|
87
|
-
|
88
|
-
break @protocol.client(stream)
|
89
|
-
end
|
88
|
+
peer = @endpoint.connect
|
89
|
+
peer.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
90
|
+
|
91
|
+
@protocol.client(IO::Stream.new(peer))
|
90
92
|
end
|
91
93
|
end
|
92
94
|
end
|
@@ -31,7 +31,7 @@ module Async
|
|
31
31
|
VERSION
|
32
32
|
end
|
33
33
|
|
34
|
-
def
|
34
|
+
def persistent?(headers)
|
35
35
|
headers['connection'] == KEEP_ALIVE
|
36
36
|
end
|
37
37
|
|
@@ -44,7 +44,7 @@ module Async
|
|
44
44
|
|
45
45
|
write_response(response.version, response.status, response.headers, response.body)
|
46
46
|
|
47
|
-
unless
|
47
|
+
unless persistent?(request.headers) && persistent?(headers)
|
48
48
|
@keep_alive = false
|
49
49
|
|
50
50
|
break
|
@@ -54,9 +54,9 @@ module Async
|
|
54
54
|
return false
|
55
55
|
end
|
56
56
|
|
57
|
-
def write_body(body, chunked =
|
57
|
+
def write_body(body, chunked = false)
|
58
58
|
# We don't support chunked encoding.
|
59
|
-
super(body,
|
59
|
+
super(body, chunked)
|
60
60
|
end
|
61
61
|
|
62
62
|
def read_body(headers)
|
@@ -65,7 +65,7 @@ module Async
|
|
65
65
|
end
|
66
66
|
|
67
67
|
# Technically, with HTTP/1.0, if no content-length is specified, we just need to read everything until the connection is closed.
|
68
|
-
if !
|
68
|
+
if !persistent?(headers)
|
69
69
|
return Body::Remainder.new(@stream)
|
70
70
|
end
|
71
71
|
end
|
@@ -36,16 +36,19 @@ module Async
|
|
36
36
|
def initialize(stream)
|
37
37
|
super(stream, CRLF)
|
38
38
|
|
39
|
-
@
|
39
|
+
@persistent = true
|
40
|
+
@count = 0
|
40
41
|
end
|
41
42
|
|
43
|
+
attr :count
|
44
|
+
|
42
45
|
# Only one simultaneous connection at a time.
|
43
46
|
def multiplex
|
44
47
|
1
|
45
48
|
end
|
46
49
|
|
47
50
|
def reusable?
|
48
|
-
@
|
51
|
+
@persistent
|
49
52
|
end
|
50
53
|
|
51
54
|
class << self
|
@@ -62,7 +65,7 @@ module Async
|
|
62
65
|
VERSION
|
63
66
|
end
|
64
67
|
|
65
|
-
def
|
68
|
+
def persistent?(headers)
|
66
69
|
headers['connection'] != CLOSE
|
67
70
|
end
|
68
71
|
|
@@ -70,6 +73,7 @@ module Async
|
|
70
73
|
def receive_requests(task: Task.current)
|
71
74
|
while true
|
72
75
|
request = Request.new(*read_request)
|
76
|
+
@count += 1
|
73
77
|
|
74
78
|
response = yield request
|
75
79
|
|
@@ -79,8 +83,8 @@ module Async
|
|
79
83
|
|
80
84
|
request.finish
|
81
85
|
|
82
|
-
unless
|
83
|
-
@
|
86
|
+
unless persistent?(request.headers) and persistent?(response.headers)
|
87
|
+
@persistent = false
|
84
88
|
|
85
89
|
break
|
86
90
|
end
|
@@ -91,6 +95,8 @@ module Async
|
|
91
95
|
end
|
92
96
|
|
93
97
|
def call(request)
|
98
|
+
@count += 1
|
99
|
+
|
94
100
|
request.version ||= self.version
|
95
101
|
|
96
102
|
Async.logger.debug(self) {"#{request.method} #{request.path} #{request.headers.inspect}"}
|
@@ -98,6 +104,7 @@ module Async
|
|
98
104
|
|
99
105
|
return Response.new(*read_response)
|
100
106
|
rescue EOFError
|
107
|
+
Async.logger.debug(self) {"Connection failed with EOFError after #{@count} requests."}
|
101
108
|
return nil
|
102
109
|
end
|
103
110
|
|
@@ -118,7 +125,7 @@ module Async
|
|
118
125
|
headers = read_headers
|
119
126
|
body = read_body(headers)
|
120
127
|
|
121
|
-
@keep_alive =
|
128
|
+
@keep_alive = persistent?(headers)
|
122
129
|
|
123
130
|
return version, Integer(status), reason, headers, body
|
124
131
|
end
|
@@ -163,9 +170,9 @@ module Async
|
|
163
170
|
end
|
164
171
|
|
165
172
|
def write_body(body, chunked = true)
|
166
|
-
if body.empty?
|
173
|
+
if body.nil? or body.empty?
|
167
174
|
@stream.write("Content-Length: 0\r\n\r\n")
|
168
|
-
body.read
|
175
|
+
body.read if body
|
169
176
|
elsif chunked
|
170
177
|
@stream.write("Transfer-Encoding: chunked\r\n\r\n")
|
171
178
|
|
@@ -64,8 +64,12 @@ module Async
|
|
64
64
|
@controller.on(:frame_received) do |frame|
|
65
65
|
Async.logger.debug(self) {"Received frame: #{frame.inspect}"}
|
66
66
|
end
|
67
|
+
|
68
|
+
@count = 0
|
67
69
|
end
|
68
70
|
|
71
|
+
attr :count
|
72
|
+
|
69
73
|
# Multiple requests can be processed at the same time.
|
70
74
|
def multiplex
|
71
75
|
@controller.remote_settings[:settings_max_concurrent_streams]
|
@@ -85,10 +89,10 @@ module Async
|
|
85
89
|
|
86
90
|
def read_in_background(task: Task.current)
|
87
91
|
task.async do |nested_task|
|
88
|
-
|
92
|
+
nested_task.annotate("#{version} reading data")
|
89
93
|
|
90
|
-
while
|
91
|
-
@controller <<
|
94
|
+
while buffer = @stream.read_partial
|
95
|
+
@controller << buffer
|
92
96
|
end
|
93
97
|
|
94
98
|
Async.logger.debug(self) {"Connection reset by peer!"}
|
@@ -104,6 +108,8 @@ module Async
|
|
104
108
|
def receive_requests(task: Task.current, &block)
|
105
109
|
# emits new streams opened by the client
|
106
110
|
@controller.on(:stream) do |stream|
|
111
|
+
@count += 1
|
112
|
+
|
107
113
|
request = Request.new
|
108
114
|
request.version = self.version
|
109
115
|
request.headers = {}
|
@@ -143,9 +149,9 @@ module Async
|
|
143
149
|
headers.update(response.headers)
|
144
150
|
|
145
151
|
# puts "Sending headers #{headers}"
|
146
|
-
if response.body.empty?
|
152
|
+
if response.body.nil? or response.body.empty?
|
147
153
|
stream.headers(headers, end_stream: true)
|
148
|
-
response.body.read
|
154
|
+
response.body.read if response.body
|
149
155
|
else
|
150
156
|
stream.headers(headers, end_stream: false)
|
151
157
|
|
@@ -166,6 +172,8 @@ module Async
|
|
166
172
|
end
|
167
173
|
|
168
174
|
def call(request)
|
175
|
+
@count += 1
|
176
|
+
|
169
177
|
request.version ||= self.version
|
170
178
|
|
171
179
|
stream = @controller.new_stream
|
@@ -207,8 +215,9 @@ module Async
|
|
207
215
|
body.finish
|
208
216
|
end
|
209
217
|
|
210
|
-
if request.body.empty?
|
218
|
+
if request.body.nil? or request.body.empty?
|
211
219
|
stream.headers(headers, end_stream: true)
|
220
|
+
request.body.read if request.body
|
212
221
|
else
|
213
222
|
stream.headers(headers, end_stream: false)
|
214
223
|
|
data/lib/async/http/server.rb
CHANGED
@@ -42,8 +42,9 @@ module Async
|
|
42
42
|
|
43
43
|
def accept(peer, address, task: Task.current)
|
44
44
|
peer.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
45
|
+
block_size = peer.getsockopt(Socket::SOL_SOCKET, Socket::SO_RCVBUF).int
|
45
46
|
|
46
|
-
stream = Async::IO::Stream.new(peer)
|
47
|
+
stream = Async::IO::Stream.new(peer, block_size: block_size)
|
47
48
|
protocol = @protocol_class.server(stream)
|
48
49
|
|
49
50
|
# Async.logger.debug(self) {"Incoming connnection from #{address.inspect}"}
|
@@ -63,8 +64,6 @@ module Async
|
|
63
64
|
end
|
64
65
|
rescue EOFError, Errno::ECONNRESET, Errno::EPIPE
|
65
66
|
# Sometimes client will disconnect without completing a result or reading the entire buffer.
|
66
|
-
ensure
|
67
|
-
peer.close
|
68
67
|
end
|
69
68
|
|
70
69
|
def run
|
@@ -34,7 +34,9 @@ module Async
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def wrap(response, &block)
|
37
|
-
response
|
37
|
+
if response and response.body
|
38
|
+
response.body = Body::Statistics.new(@start_time, response.body, block)
|
39
|
+
end
|
38
40
|
end
|
39
41
|
end
|
40
42
|
|
data/lib/async/http/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: async-http
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.20.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-04-
|
11
|
+
date: 2018-04-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: async
|
@@ -30,28 +30,28 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '1.
|
33
|
+
version: '1.8'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '1.
|
40
|
+
version: '1.8'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: http-2
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 0.9.0
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: 0.9.0
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: async-rspec
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|