mt-uv-rays 2.4.7
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 +7 -0
- data/LICENSE +20 -0
- data/README.md +63 -0
- data/Rakefile +22 -0
- data/lib/faraday/adapter/mt-libuv.rb +89 -0
- data/lib/handsoap/http/drivers/mt-libuv_driver.rb +43 -0
- data/lib/httpi/adapter/mt-libuv.rb +69 -0
- data/lib/mt-uv-rays/abstract_tokenizer.rb +121 -0
- data/lib/mt-uv-rays/buffered_tokenizer.rb +176 -0
- data/lib/mt-uv-rays/connection.rb +190 -0
- data/lib/mt-uv-rays/http/encoding.rb +131 -0
- data/lib/mt-uv-rays/http/parser.rb +175 -0
- data/lib/mt-uv-rays/http/request.rb +262 -0
- data/lib/mt-uv-rays/http_endpoint.rb +336 -0
- data/lib/mt-uv-rays/ping.rb +189 -0
- data/lib/mt-uv-rays/scheduler/time.rb +307 -0
- data/lib/mt-uv-rays/scheduler.rb +386 -0
- data/lib/mt-uv-rays/tcp_server.rb +46 -0
- data/lib/mt-uv-rays/version.rb +5 -0
- data/lib/mt-uv-rays.rb +94 -0
- data/mt-uv-rays.gemspec +38 -0
- data/spec/abstract_tokenizer_spec.rb +129 -0
- data/spec/buffered_tokenizer_spec.rb +277 -0
- data/spec/connection_spec.rb +124 -0
- data/spec/http_endpoint_spec.rb +636 -0
- data/spec/ping_spec.rb +73 -0
- data/spec/scheduler_spec.rb +118 -0
- data/spec/scheduler_time_spec.rb +132 -0
- metadata +300 -0
@@ -0,0 +1,262 @@
|
|
1
|
+
# encoding: ASCII-8BIT
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'uri'
|
5
|
+
require 'cgi'
|
6
|
+
require 'rubyntlm'
|
7
|
+
require 'net/http/digest_auth'
|
8
|
+
|
9
|
+
module MTUV
|
10
|
+
module Http
|
11
|
+
class Request < ::MTLibuv::Q::DeferredPromise
|
12
|
+
include Encoding
|
13
|
+
|
14
|
+
|
15
|
+
COOKIE = 'cookie'
|
16
|
+
CONNECTION = :Connection
|
17
|
+
CRLF="\r\n"
|
18
|
+
|
19
|
+
|
20
|
+
attr_reader :path, :method, :options
|
21
|
+
|
22
|
+
|
23
|
+
def cookies_hash
|
24
|
+
@cookiejar.get_hash(@uri)
|
25
|
+
end
|
26
|
+
|
27
|
+
def set_cookie(value)
|
28
|
+
@cookiejar.set(@uri, value)
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
def initialize(endpoint, options)
|
33
|
+
super(endpoint.thread, endpoint.thread.defer)
|
34
|
+
|
35
|
+
@host = endpoint.host
|
36
|
+
@port = endpoint.port
|
37
|
+
@http_proxy = endpoint.http_proxy? ? endpoint.proxy : nil
|
38
|
+
@encoded_host = endpoint.encoded_host
|
39
|
+
|
40
|
+
path = options[:path]
|
41
|
+
if path.is_a?(::URI)
|
42
|
+
@path = path.to_s.split(@encoded_host)[1] || '/'
|
43
|
+
elsif path.include?("://")
|
44
|
+
@path = path.split(@encoded_host)[1] || '/'
|
45
|
+
else
|
46
|
+
@path = path
|
47
|
+
end
|
48
|
+
|
49
|
+
@method = options[:method]
|
50
|
+
@cookiejar = endpoint.cookiejar
|
51
|
+
@middleware = endpoint.middleware
|
52
|
+
@uri = "#{endpoint.scheme}://#{@encoded_host}#{@path}"
|
53
|
+
@path = @uri if @http_proxy
|
54
|
+
endpoint = nil
|
55
|
+
|
56
|
+
@options = options
|
57
|
+
@ntlm_creds = options[:ntlm]
|
58
|
+
@digest_creds = options[:digest]
|
59
|
+
@challenge_retries = 0
|
60
|
+
|
61
|
+
# Don't hold references to vars we don't require anymore
|
62
|
+
self.finally {
|
63
|
+
@host = @port = nil
|
64
|
+
@cookiejar = nil
|
65
|
+
@middleware = nil
|
66
|
+
}
|
67
|
+
@error = proc { |reason| reject(reason) }
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
def resolve(response, parser = nil)
|
73
|
+
if response.status == 401 && @challenge_retries == 0 && response[:"WWW-Authenticate"]
|
74
|
+
challenge = Array(response[:"WWW-Authenticate"]).reject { |auth| auth.downcase == 'negotiate' }[0]
|
75
|
+
|
76
|
+
begin
|
77
|
+
if @ntlm_creds && challenge[0..3] == 'NTLM'
|
78
|
+
@options[:headers] ||= {}
|
79
|
+
@options[:headers][:Authorization] = ntlm_auth_header(challenge)
|
80
|
+
@challenge_retries += 1
|
81
|
+
|
82
|
+
execute(@transport)
|
83
|
+
return false
|
84
|
+
elsif @digest_creds && challenge[0..5] == 'Digest'
|
85
|
+
@options[:headers] ||= {}
|
86
|
+
@options[:headers][:Authorization] = digest_auth_header(challenge)
|
87
|
+
@challenge_retries += 1
|
88
|
+
|
89
|
+
execute(@transport)
|
90
|
+
return false
|
91
|
+
end
|
92
|
+
rescue => e
|
93
|
+
reject e
|
94
|
+
true
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
@transport = nil
|
99
|
+
@defer.resolve(response)
|
100
|
+
true
|
101
|
+
end
|
102
|
+
|
103
|
+
def reject(reason)
|
104
|
+
@defer.reject(reason)
|
105
|
+
end
|
106
|
+
|
107
|
+
def execute(transport)
|
108
|
+
# configure ntlm request headers
|
109
|
+
if @options[:ntlm]
|
110
|
+
@options[:headers] ||= {}
|
111
|
+
@options[:headers][:Authorization] ||= ntlm_auth_header
|
112
|
+
end
|
113
|
+
|
114
|
+
head, body = build_request, @options[:body]
|
115
|
+
@transport = transport
|
116
|
+
|
117
|
+
@middleware.each do |m|
|
118
|
+
begin
|
119
|
+
head, body = m.request(self, head, body) if m.respond_to?(:request)
|
120
|
+
rescue => e
|
121
|
+
reject e
|
122
|
+
return
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
body = body.is_a?(Hash) ? form_encode_body(body) : body
|
127
|
+
file = @options[:file]
|
128
|
+
query = @options[:query]
|
129
|
+
|
130
|
+
# Set the Content-Length if file is given
|
131
|
+
head['content-length'] = File.size(file) if file
|
132
|
+
|
133
|
+
# Set the Content-Length if body is given,
|
134
|
+
# or we're doing an empty post or put
|
135
|
+
if body
|
136
|
+
head['content-length'] = body.bytesize
|
137
|
+
elsif method == :post or method == :put
|
138
|
+
# wont happen if body is set and we already set content-length above
|
139
|
+
head['content-length'] ||= 0
|
140
|
+
end
|
141
|
+
|
142
|
+
# Set content-type header if missing and body is a Ruby hash
|
143
|
+
if !head['content-type'] and @options[:body].is_a? Hash
|
144
|
+
head['content-type'] = 'application/x-www-form-urlencoded'
|
145
|
+
end
|
146
|
+
|
147
|
+
request_header = encode_request(method, @path, query)
|
148
|
+
if @http_proxy && (@http_proxy[:username] || @http_proxy[:password])
|
149
|
+
request_header << encode_auth('Proxy-Authorization', [@http_proxy[:username], @http_proxy[:password]])
|
150
|
+
end
|
151
|
+
request_header << encode_headers(head)
|
152
|
+
request_header << CRLF
|
153
|
+
|
154
|
+
if body
|
155
|
+
request_header << body
|
156
|
+
transport.write(request_header).catch &@error
|
157
|
+
elsif file
|
158
|
+
transport.write(request_header).catch &@error
|
159
|
+
|
160
|
+
# Send file
|
161
|
+
fileRef = @reactor.file file, File::RDONLY do
|
162
|
+
# File is open and available for reading
|
163
|
+
pSend = fileRef.send_file(transport, using: :raw, wait: :promise)
|
164
|
+
pSend.catch &@error
|
165
|
+
pSend.finally do
|
166
|
+
fileRef.close
|
167
|
+
end
|
168
|
+
end
|
169
|
+
fileRef.catch &@error
|
170
|
+
else
|
171
|
+
transport.write(request_header).catch &@error
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def notify(*args)
|
176
|
+
@defer.notify(*args)
|
177
|
+
end
|
178
|
+
|
179
|
+
def set_headers(head)
|
180
|
+
@headers_callback.call(head) if @headers_callback
|
181
|
+
end
|
182
|
+
|
183
|
+
def on_headers(&callback)
|
184
|
+
@headers_callback = callback
|
185
|
+
end
|
186
|
+
|
187
|
+
def streaming?
|
188
|
+
@options[:streaming]
|
189
|
+
end
|
190
|
+
|
191
|
+
|
192
|
+
protected
|
193
|
+
|
194
|
+
|
195
|
+
def build_request
|
196
|
+
head = @options[:headers] ? munge_header_keys(@options[:headers]) : {}
|
197
|
+
|
198
|
+
# Set the cookie header if provided
|
199
|
+
@cookies = @cookiejar.get(@uri)
|
200
|
+
if cookie = head[COOKIE]
|
201
|
+
@cookies << encode_cookie(cookie)
|
202
|
+
end
|
203
|
+
head[COOKIE] = @cookies.compact.uniq.join("; ").squeeze(";") unless @cookies.empty?
|
204
|
+
|
205
|
+
# Set connection close unless keep-alive
|
206
|
+
if !@options[:keepalive]
|
207
|
+
head['connection'] = 'close'
|
208
|
+
end
|
209
|
+
|
210
|
+
# Set the Host header if it hasn't been specified already
|
211
|
+
head['host'] ||= @encoded_host
|
212
|
+
|
213
|
+
# Set the User-Agent if it hasn't been specified
|
214
|
+
if !head.key?('user-agent')
|
215
|
+
head['user-agent'] = "MTUV HttpClient"
|
216
|
+
elsif head['user-agent'].nil?
|
217
|
+
head.delete('user-agent')
|
218
|
+
end
|
219
|
+
|
220
|
+
head
|
221
|
+
end
|
222
|
+
|
223
|
+
def ntlm_auth_header(challenge = nil)
|
224
|
+
if @ntlm_auth && challenge.nil?
|
225
|
+
return @ntlm_auth
|
226
|
+
elsif challenge
|
227
|
+
scheme, param_str = parse_ntlm_challenge_header(challenge)
|
228
|
+
if param_str.nil?
|
229
|
+
@ntlm_auth = nil
|
230
|
+
return ntlm_auth_header(@ntlm_creds)
|
231
|
+
else
|
232
|
+
t2 = Net::NTLM::Message.decode64(param_str)
|
233
|
+
t3 = t2.response(@ntlm_creds, ntlmv2: true)
|
234
|
+
@ntlm_auth = "NTLM #{t3.encode64}"
|
235
|
+
return @ntlm_auth
|
236
|
+
end
|
237
|
+
else
|
238
|
+
domain = @ntlm_creds[:domain]
|
239
|
+
t1 = Net::NTLM::Message::Type1.new()
|
240
|
+
t1.domain = domain if domain
|
241
|
+
@ntlm_auth = "NTLM #{t1.encode64}"
|
242
|
+
return @ntlm_auth
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def parse_ntlm_challenge_header(challenge)
|
247
|
+
scheme, param_str = challenge.scan(/\A(\S+)(?:\s+(.*))?\z/)[0]
|
248
|
+
return nil if scheme.nil?
|
249
|
+
return scheme, param_str
|
250
|
+
end
|
251
|
+
|
252
|
+
def digest_auth_header(challenge)
|
253
|
+
uri = URI.parse @uri
|
254
|
+
uri.userinfo = "#{CGI::escape(@digest_creds[:user])}:#{CGI::escape(@digest_creds[:password])}"
|
255
|
+
|
256
|
+
digest_auth = Net::HTTP::DigestAuth.new
|
257
|
+
auth = digest_auth.auth_header uri, challenge, method.to_s.upcase
|
258
|
+
auth
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
@@ -0,0 +1,336 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'uri'
|
4
|
+
require 'cookiejar' # Manages cookies
|
5
|
+
require 'http-parser' # Parses HTTP request / responses
|
6
|
+
require 'addressable/uri' # URI parser
|
7
|
+
require 'mt-uv-rays/http/encoding'
|
8
|
+
require 'mt-uv-rays/http/request'
|
9
|
+
require 'mt-uv-rays/http/parser'
|
10
|
+
|
11
|
+
module MTUV
|
12
|
+
class CookieJar
|
13
|
+
def initialize
|
14
|
+
@jar = ::CookieJar::Jar.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def set(uri, string)
|
18
|
+
@jar.set_cookie(uri, string) rescue nil # drop invalid cookies
|
19
|
+
end
|
20
|
+
|
21
|
+
def get(uri)
|
22
|
+
uri = URI.parse(uri) rescue nil
|
23
|
+
uri ? @jar.get_cookies(uri).map(&:to_s) : []
|
24
|
+
end
|
25
|
+
|
26
|
+
def get_hash(uri)
|
27
|
+
uri = URI.parse(uri) rescue nil
|
28
|
+
cookies = {}
|
29
|
+
if uri
|
30
|
+
@jar.get_cookies(uri).each do |cookie|
|
31
|
+
cookies[cookie.name.to_sym] = cookie.value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
cookies
|
35
|
+
end
|
36
|
+
|
37
|
+
def clear_cookies
|
38
|
+
@jar = ::CookieJar::Jar.new
|
39
|
+
end
|
40
|
+
end # CookieJar
|
41
|
+
|
42
|
+
# HTTPS Proxy - connect to proxy
|
43
|
+
# CONNECT #{target_host}:#{target_port} HTTP/1.0\r\n"
|
44
|
+
# Proxy-Authorization: Basic #{encoded_credentials}\r\n
|
45
|
+
# \r\n
|
46
|
+
# Parse response =~ %r{\AHTTP/1\.[01] 200 .*\r\n\r\n}m
|
47
|
+
# use_tls
|
48
|
+
# send requests as usual
|
49
|
+
|
50
|
+
# HTTP Proxy - connect to proxy
|
51
|
+
# GET #{url_with_host} HTTP/1.1\r\n"
|
52
|
+
# Proxy-Authorization: Basic #{encoded_credentials}\r\n
|
53
|
+
# \r\n
|
54
|
+
|
55
|
+
class HttpEndpoint
|
56
|
+
class Connection < OutboundConnection
|
57
|
+
def initialize(host, port, tls, proxy, client)
|
58
|
+
@target_host = host
|
59
|
+
@client = client
|
60
|
+
@request = nil
|
61
|
+
|
62
|
+
if proxy
|
63
|
+
super(proxy[:host], proxy[:port])
|
64
|
+
if tls
|
65
|
+
@negotiating = true
|
66
|
+
@proxy = proxy
|
67
|
+
@connect_host = host
|
68
|
+
@connect_port = port
|
69
|
+
end
|
70
|
+
else
|
71
|
+
super(host, port)
|
72
|
+
start_tls if tls
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def start_tls
|
77
|
+
opts = {host_name: @target_host}.merge(@client.tls_options)
|
78
|
+
use_tls(opts)
|
79
|
+
end
|
80
|
+
|
81
|
+
def connect_send_handshake(target_host, target_port, proxy)
|
82
|
+
header = String.new("CONNECT #{target_host}:#{target_port} HTTP/1.0\r\n")
|
83
|
+
if proxy[:username] || proxy[:password]
|
84
|
+
encoded_credentials = Base64.strict_encode64([proxy[:username], proxy[:password]].join(":"))
|
85
|
+
header << "Proxy-Authorization: Basic #{encoded_credentials}\r\n"
|
86
|
+
end
|
87
|
+
header << "\r\n"
|
88
|
+
write(header)
|
89
|
+
end
|
90
|
+
|
91
|
+
attr_accessor :request, :reason
|
92
|
+
|
93
|
+
def on_read(data, *args)
|
94
|
+
if @negotiating
|
95
|
+
@negotiating = false
|
96
|
+
if data =~ %r{\AHTTP/1\.[01] 200 .*\r\n\r\n}m
|
97
|
+
start_tls
|
98
|
+
@client.connection_ready
|
99
|
+
else
|
100
|
+
@reason = "Unexpected response from proxy: #{data}"
|
101
|
+
close_connection
|
102
|
+
end
|
103
|
+
else
|
104
|
+
@client.data_received(data)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def post_init(*args)
|
109
|
+
end
|
110
|
+
|
111
|
+
def on_connect(transport)
|
112
|
+
if @negotiating
|
113
|
+
connect_send_handshake(@connect_host, @connect_port, @proxy)
|
114
|
+
else
|
115
|
+
@client.connection_ready
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def on_close
|
120
|
+
@client.connection_closed(@request, @reason)
|
121
|
+
ensure
|
122
|
+
@request = nil
|
123
|
+
@client = nil
|
124
|
+
@reason = nil
|
125
|
+
end
|
126
|
+
|
127
|
+
def close_connection(request = nil)
|
128
|
+
if request.is_a? Http::Request
|
129
|
+
@request = request
|
130
|
+
super(:after_writing)
|
131
|
+
else
|
132
|
+
super(request)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
@@defaults = {
|
139
|
+
:path => '/',
|
140
|
+
:keepalive => true
|
141
|
+
}
|
142
|
+
|
143
|
+
|
144
|
+
def initialize(host, options = {})
|
145
|
+
@queue = []
|
146
|
+
@parser = Http::Parser.new
|
147
|
+
@thread = reactor
|
148
|
+
@connection = nil
|
149
|
+
|
150
|
+
@options = @@defaults.merge(options)
|
151
|
+
@tls_options = options[:tls_options] || {}
|
152
|
+
@inactivity_timeout = options[:inactivity_timeout] || 10000
|
153
|
+
|
154
|
+
uri = host.is_a?(::URI) ? host : ::URI.parse(host)
|
155
|
+
@port = uri.port
|
156
|
+
@host = uri.host
|
157
|
+
|
158
|
+
default_port = uri.port == uri.default_port
|
159
|
+
@encoded_host = default_port ? @host : "#{@host}:#{@port}"
|
160
|
+
@proxy = @options[:proxy]
|
161
|
+
|
162
|
+
@scheme = uri.scheme
|
163
|
+
@tls = @scheme == 'https'
|
164
|
+
@cookiejar = CookieJar.new
|
165
|
+
@middleware = []
|
166
|
+
|
167
|
+
@closing = false
|
168
|
+
@connecting = false
|
169
|
+
end
|
170
|
+
|
171
|
+
|
172
|
+
attr_accessor :inactivity_timeout
|
173
|
+
attr_reader :tls_options, :port, :host, :tls, :scheme, :encoded_host
|
174
|
+
attr_reader :cookiejar, :middleware, :thread, :proxy
|
175
|
+
|
176
|
+
|
177
|
+
def get(options = {}); request(:get, options); end
|
178
|
+
def head(options = {}); request(:head, options); end
|
179
|
+
def delete(options = {}); request(:delete, options); end
|
180
|
+
def put(options = {}); request(:put, options); end
|
181
|
+
def post(options = {}); request(:post, options); end
|
182
|
+
def patch(options = {}); request(:patch, options); end
|
183
|
+
def options(options = {}); request(:options, options); end
|
184
|
+
|
185
|
+
|
186
|
+
def request(method, options = {})
|
187
|
+
options = @options.merge(options)
|
188
|
+
options[:method] = method.to_sym
|
189
|
+
|
190
|
+
# Setup the request with callbacks
|
191
|
+
request = Http::Request.new(self, options)
|
192
|
+
request.then(proc { |response|
|
193
|
+
if response.keep_alive
|
194
|
+
restart_timer
|
195
|
+
else
|
196
|
+
# We might have already started processing the next request
|
197
|
+
# at this point. So don't want to disconnect if already
|
198
|
+
# disconnected.
|
199
|
+
close_connection unless @connecting
|
200
|
+
end
|
201
|
+
|
202
|
+
next_request
|
203
|
+
|
204
|
+
response
|
205
|
+
}, proc { |err|
|
206
|
+
# @parser.eof
|
207
|
+
close_connection unless @connecting
|
208
|
+
next_request
|
209
|
+
::MTLibuv::Q.reject(@thread, err)
|
210
|
+
})
|
211
|
+
|
212
|
+
@queue.unshift(request)
|
213
|
+
|
214
|
+
next_request
|
215
|
+
request
|
216
|
+
end
|
217
|
+
|
218
|
+
# Callbacks
|
219
|
+
def connection_ready
|
220
|
+
# A connection can be closed while still connecting
|
221
|
+
return if @closing
|
222
|
+
|
223
|
+
@connecting = false
|
224
|
+
if @queue.length > 0
|
225
|
+
restart_timer
|
226
|
+
next_request
|
227
|
+
else
|
228
|
+
close_connection
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
def connection_closed(request, reason)
|
233
|
+
# A connection might close due to a connection failure
|
234
|
+
awaiting_close = @closing
|
235
|
+
awaiting_connect = @connecting
|
236
|
+
@closing = false
|
237
|
+
@connecting = false
|
238
|
+
@connection = nil
|
239
|
+
|
240
|
+
# We may have closed a previous connection
|
241
|
+
if @parser.request && (request.nil? || request == @parser.request)
|
242
|
+
stop_timer
|
243
|
+
@parser.eof
|
244
|
+
elsif request.nil? && @parser.request.nil? && @queue.length > 0
|
245
|
+
req = @queue.pop
|
246
|
+
req.reject(reason || :connection_failure)
|
247
|
+
end
|
248
|
+
|
249
|
+
next_request if awaiting_close || awaiting_connect
|
250
|
+
end
|
251
|
+
|
252
|
+
def data_received(data)
|
253
|
+
restart_timer
|
254
|
+
close_connection if @parser.received(data)
|
255
|
+
end
|
256
|
+
|
257
|
+
def cancel_all
|
258
|
+
@queue.each do |request|
|
259
|
+
request.reject(:cancelled)
|
260
|
+
end
|
261
|
+
if @parser.request
|
262
|
+
@parser.request.reject(:cancelled)
|
263
|
+
@parser.eof
|
264
|
+
end
|
265
|
+
@queue = []
|
266
|
+
close_connection
|
267
|
+
end
|
268
|
+
|
269
|
+
def http_proxy?
|
270
|
+
@proxy && !@tls
|
271
|
+
end
|
272
|
+
|
273
|
+
|
274
|
+
private
|
275
|
+
|
276
|
+
|
277
|
+
def next_request
|
278
|
+
# Don't start a request while transitioning state
|
279
|
+
return if @closing || @connecting
|
280
|
+
return if @parser.request || @queue.length == 0
|
281
|
+
|
282
|
+
if @connection
|
283
|
+
req = @queue.pop
|
284
|
+
@connection.request = req
|
285
|
+
@parser.new_request(req)
|
286
|
+
|
287
|
+
req.execute(@connection)
|
288
|
+
else
|
289
|
+
new_connection
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
def new_connection
|
294
|
+
# no new connections while transitioning state
|
295
|
+
return if @closing || @connecting
|
296
|
+
if @queue.length > 0 && @connection.nil?
|
297
|
+
@connecting = true
|
298
|
+
@connection = Connection.new(@host, @port, @tls, @proxy, self)
|
299
|
+
start_timer
|
300
|
+
end
|
301
|
+
@connection
|
302
|
+
end
|
303
|
+
|
304
|
+
def close_connection
|
305
|
+
# Close connection can be called while connecting
|
306
|
+
return if @closing || @connection.nil?
|
307
|
+
@closing = true
|
308
|
+
@connection.close_connection
|
309
|
+
stop_timer
|
310
|
+
@connection = nil
|
311
|
+
end
|
312
|
+
|
313
|
+
def start_timer
|
314
|
+
# Only start the timer if there is a connection starting or in place
|
315
|
+
return if @closing || @connection.nil?
|
316
|
+
@timer.cancel if @timer
|
317
|
+
@timer = @thread.scheduler.in(@inactivity_timeout) do
|
318
|
+
@timer = nil
|
319
|
+
idle_timeout
|
320
|
+
end
|
321
|
+
end
|
322
|
+
alias_method :restart_timer, :start_timer
|
323
|
+
|
324
|
+
def stop_timer
|
325
|
+
@timer.cancel unless @timer.nil?
|
326
|
+
@timer = nil
|
327
|
+
end
|
328
|
+
|
329
|
+
def idle_timeout
|
330
|
+
connection = @connection
|
331
|
+
close_connection
|
332
|
+
@parser.reason = :timeout if @parser.request
|
333
|
+
connection.reason = :timeout if connection
|
334
|
+
end
|
335
|
+
end
|
336
|
+
end
|