async-http 0.19.0 → 0.20.0

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: dd03fb46876ed4121fa2232b18de6ec00e7176a6c224adf8d5001ec53cf89f1c
4
- data.tar.gz: c42bf8f085ad74ee4fbc6b15c47bcb06915a41f42d3a48d58d39a43f695b267d
3
+ metadata.gz: b40fa9e658f1c38dbd0a8c6ecdfb201af8e6bc504dfb30697b776d6fdf001eac
4
+ data.tar.gz: 714670c4acf8f6b961558d3ea336e81e1d596db29ecbe4df66dbe8349d257975
5
5
  SHA512:
6
- metadata.gz: 7be71154d37d575a6c7b19c7d2c5b81b98e513d260a5d15c9d4c8e0a7fbaf3285d99ca7dc4030e9567af159ff73f4dc25c6144dd55e1745ae307898d0f6c8c9f
7
- data.tar.gz: bb5748a2d385ce8528b218246a5a701d1c8fb1b45d1b40c2d9ce8e559b023c9c032c1ef396cc930c101278e56ddd2a57280e9e16d77df4eca1bfbc09c99e4caf
6
+ metadata.gz: 740c053ba517dd857b7b612d9e10054edc0e63f3a16062cecde65f042a1fc8036e9c9c299c258f894de91fbb2fa1ab8ad1f27b4ecacce7527fb166c76774b5f8
7
+ data.tar.gz: fed266d04a67864ab917d61162b577bd2a24a8567eea6cbc59df5535558d735b0f5e5ef5e902a6b27bac7343ecfc0eec9654d105f346b2492f4d0edb3da5b55b
@@ -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.7")
20
+ spec.add_dependency("async-io", "~> 1.8")
21
21
 
22
- spec.add_dependency("http-2", "~> 0.8")
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"
@@ -36,7 +36,7 @@ module Async
36
36
 
37
37
  def read
38
38
  if @remaining > 0
39
- if chunk = @stream.read(@remaining)
39
+ if chunk = @stream.read_partial(@remaining)
40
40
  @remaining -= chunk.bytesize
41
41
 
42
42
  return chunk
@@ -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
@@ -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
- # The connection won't be released until the body is completely read/released.
68
- Body::Streamable.wrap(response) do
69
- @connections.release(connection)
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.each do |endpoint|
82
- peer = endpoint.connect
83
-
84
- peer.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
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 keep_alive?(headers)
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 keep_alive?(request.headers) && keep_alive?(headers)
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 = true)
57
+ def write_body(body, chunked = false)
58
58
  # We don't support chunked encoding.
59
- super(body, false)
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 !keep_alive?(headers)
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
- @keep_alive = true
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
- @keep_alive
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 keep_alive?(headers)
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 keep_alive?(request.headers) and keep_alive?(response.headers)
83
- @keep_alive = false
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 = keep_alive?(headers)
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
- buffer = Async::IO::BinaryString.new
92
+ nested_task.annotate("#{version} reading data")
89
93
 
90
- while data = @stream.io.read(1024*8, buffer)
91
- @controller << data
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
 
@@ -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.body = Body::Statistics.new(@start_time, response.body, block)
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
 
@@ -20,6 +20,6 @@
20
20
 
21
21
  module Async
22
22
  module HTTP
23
- VERSION = "0.19.0"
23
+ VERSION = "0.20.0"
24
24
  end
25
25
  end
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.19.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-17 00:00:00.000000000 Z
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.7'
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.7'
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: '0.8'
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: '0.8'
54
+ version: 0.9.0
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: async-rspec
57
57
  requirement: !ruby/object:Gem::Requirement