uv-rays 2.3.0 → 2.3.1
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/lib/uv-rays.rb +93 -94
- data/lib/uv-rays/abstract_tokenizer.rb +1 -1
- data/lib/uv-rays/buffered_tokenizer.rb +1 -1
- data/lib/uv-rays/connection.rb +188 -188
- data/lib/uv-rays/http_endpoint.rb +296 -296
- data/lib/uv-rays/scheduler.rb +409 -409
- data/lib/uv-rays/scheduler/time.rb +307 -308
- data/lib/uv-rays/tcp_server.rb +44 -44
- data/lib/uv-rays/version.rb +1 -1
- data/spec/scheduler_spec.rb +1 -1
- metadata +2 -2
@@ -1,296 +1,296 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'uri'
|
4
|
-
|
5
|
-
module UV
|
6
|
-
class CookieJar
|
7
|
-
def initialize
|
8
|
-
@jar = ::CookieJar::Jar.new
|
9
|
-
end
|
10
|
-
|
11
|
-
def set(uri, string)
|
12
|
-
@jar.set_cookie(uri, string) rescue nil # drop invalid cookies
|
13
|
-
end
|
14
|
-
|
15
|
-
def get(uri)
|
16
|
-
uri = URI.parse(uri) rescue nil
|
17
|
-
uri ? @jar.get_cookies(uri).map(&:to_s) : []
|
18
|
-
end
|
19
|
-
|
20
|
-
def get_hash(uri)
|
21
|
-
uri = URI.parse(uri) rescue nil
|
22
|
-
cookies = {}
|
23
|
-
if uri
|
24
|
-
@jar.get_cookies(uri).each do |cookie|
|
25
|
-
cookies[cookie.name.to_sym] = cookie.value
|
26
|
-
end
|
27
|
-
end
|
28
|
-
cookies
|
29
|
-
end
|
30
|
-
|
31
|
-
def clear_cookies
|
32
|
-
@jar = ::CookieJar::Jar.new
|
33
|
-
end
|
34
|
-
end # CookieJar
|
35
|
-
|
36
|
-
# HTTPS Proxy - connect to proxy
|
37
|
-
# CONNECT #{target_host}:#{target_port} HTTP/1.0\r\n"
|
38
|
-
# Proxy-Authorization: Basic #{encoded_credentials}\r\n
|
39
|
-
# \r\n
|
40
|
-
# Parse response =~ %r{\AHTTP/1\.[01] 200 .*\r\n\r\n}m
|
41
|
-
# use_tls
|
42
|
-
# send requests as usual
|
43
|
-
|
44
|
-
# HTTP Proxy - connect to proxy
|
45
|
-
# GET #{url_with_host} HTTP/1.1\r\n"
|
46
|
-
# Proxy-Authorization: Basic #{encoded_credentials}\r\n
|
47
|
-
# \r\n
|
48
|
-
|
49
|
-
class HttpEndpoint
|
50
|
-
class Connection < OutboundConnection
|
51
|
-
def initialize(host, port, tls, proxy, client)
|
52
|
-
@target_host = host
|
53
|
-
@client = client
|
54
|
-
@request = nil
|
55
|
-
|
56
|
-
if proxy
|
57
|
-
super(proxy[:host], proxy[:port])
|
58
|
-
connect_send_handshake(host, port, proxy) if tls
|
59
|
-
else
|
60
|
-
super(host, port)
|
61
|
-
start_tls if tls
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
def start_tls
|
66
|
-
opts = {host_name: @target_host}.merge(@client.tls_options)
|
67
|
-
use_tls(opts)
|
68
|
-
end
|
69
|
-
|
70
|
-
def connect_send_handshake(target_host, target_port, proxy)
|
71
|
-
@negotiating = true
|
72
|
-
header = String.new("CONNECT #{target_host}:#{target_port} HTTP/1.0\r\n")
|
73
|
-
if proxy[:username] || proxy[:password]
|
74
|
-
encoded_credentials = Base64.strict_encode64([proxy[:username], proxy[:password]].join(":"))
|
75
|
-
header << "Proxy-Authorization: Basic #{encoded_credentials}\r\n"
|
76
|
-
end
|
77
|
-
header << "\r\n"
|
78
|
-
write(header)
|
79
|
-
end
|
80
|
-
|
81
|
-
attr_accessor :request, :reason
|
82
|
-
|
83
|
-
def on_read(data, *args)
|
84
|
-
if @negotiating
|
85
|
-
@negotiating = false
|
86
|
-
if data =~ %r{\AHTTP/1\.[01] 200 .*\r\n\r\n}m
|
87
|
-
start_tls
|
88
|
-
@client.connection_ready
|
89
|
-
else
|
90
|
-
@reason = "Unexpected response from proxy: #{data}"
|
91
|
-
close_connection
|
92
|
-
end
|
93
|
-
else
|
94
|
-
@client.data_received(data)
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
def post_init(*args)
|
99
|
-
end
|
100
|
-
|
101
|
-
def on_connect(transport)
|
102
|
-
@client.connection_ready unless @negotiating
|
103
|
-
end
|
104
|
-
|
105
|
-
def on_close
|
106
|
-
@client.connection_closed(@request, @reason)
|
107
|
-
ensure
|
108
|
-
@request = nil
|
109
|
-
@client = nil
|
110
|
-
@reason = nil
|
111
|
-
end
|
112
|
-
|
113
|
-
def close_connection(request = nil)
|
114
|
-
if request.is_a? Http::Request
|
115
|
-
@request = request
|
116
|
-
super(:after_writing)
|
117
|
-
else
|
118
|
-
super(request)
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
|
124
|
-
@@defaults = {
|
125
|
-
:path => '/',
|
126
|
-
:keepalive => true
|
127
|
-
}
|
128
|
-
|
129
|
-
|
130
|
-
def initialize(host, options = {})
|
131
|
-
@queue = []
|
132
|
-
@parser = Http::Parser.new
|
133
|
-
@thread = reactor
|
134
|
-
@connection = nil
|
135
|
-
|
136
|
-
@options = @@defaults.merge(options)
|
137
|
-
@tls_options = options[:tls_options] || {}
|
138
|
-
@inactivity_timeout = options[:inactivity_timeout] || 10000
|
139
|
-
|
140
|
-
uri = host.is_a?(::URI) ? host : ::URI.parse(host)
|
141
|
-
@port = uri.port
|
142
|
-
@host = uri.host
|
143
|
-
|
144
|
-
default_port = uri.port == uri.default_port
|
145
|
-
@encoded_host = default_port ? @host : "#{@host}:#{@port}"
|
146
|
-
@proxy = @options[:proxy]
|
147
|
-
|
148
|
-
@scheme = uri.scheme
|
149
|
-
@tls = @scheme == 'https'
|
150
|
-
@cookiejar = CookieJar.new
|
151
|
-
@middleware = []
|
152
|
-
end
|
153
|
-
|
154
|
-
|
155
|
-
attr_accessor :inactivity_timeout
|
156
|
-
attr_reader :tls_options, :port, :host, :tls, :scheme, :encoded_host
|
157
|
-
attr_reader :cookiejar, :middleware, :thread, :proxy
|
158
|
-
|
159
|
-
|
160
|
-
def get(options = {}); request(:get, options); end
|
161
|
-
def head(options = {}); request(:head, options); end
|
162
|
-
def delete(options = {}); request(:delete, options); end
|
163
|
-
def put(options = {}); request(:put, options); end
|
164
|
-
def post(options = {}); request(:post, options); end
|
165
|
-
def patch(options = {}); request(:patch, options); end
|
166
|
-
def options(options = {}); request(:options, options); end
|
167
|
-
|
168
|
-
|
169
|
-
def request(method, options = {})
|
170
|
-
options = @options.merge(options)
|
171
|
-
options[:method] = method.to_sym
|
172
|
-
|
173
|
-
# Setup the request with callbacks
|
174
|
-
request = Http::Request.new(self, options)
|
175
|
-
request.then(proc { |response|
|
176
|
-
if response.keep_alive
|
177
|
-
restart_timer
|
178
|
-
else
|
179
|
-
close_connection
|
180
|
-
end
|
181
|
-
|
182
|
-
next_request
|
183
|
-
|
184
|
-
response
|
185
|
-
}, proc { |err|
|
186
|
-
@parser.eof
|
187
|
-
close_connection
|
188
|
-
next_request
|
189
|
-
::Libuv::Q.reject(@thread, err)
|
190
|
-
})
|
191
|
-
|
192
|
-
@queue.unshift(request)
|
193
|
-
|
194
|
-
next_request
|
195
|
-
request
|
196
|
-
end
|
197
|
-
|
198
|
-
# Callbacks
|
199
|
-
def connection_ready
|
200
|
-
if @queue.length > 0
|
201
|
-
restart_timer
|
202
|
-
next_request
|
203
|
-
else
|
204
|
-
close_connection
|
205
|
-
end
|
206
|
-
end
|
207
|
-
|
208
|
-
def connection_closed(request, reason)
|
209
|
-
# We may have closed a previous connection
|
210
|
-
if @parser.request && (request.nil? || request == @parser.request)
|
211
|
-
@connection = nil
|
212
|
-
stop_timer
|
213
|
-
|
214
|
-
@parser.eof
|
215
|
-
elsif request.nil? && @parser.request.nil? && @queue.length > 0
|
216
|
-
req = @queue.pop
|
217
|
-
req.reject(reason || :connection_failure)
|
218
|
-
end
|
219
|
-
end
|
220
|
-
|
221
|
-
def data_received(data)
|
222
|
-
restart_timer
|
223
|
-
close_connection if @parser.received(data)
|
224
|
-
end
|
225
|
-
|
226
|
-
def cancel_all
|
227
|
-
@queue.each do |request|
|
228
|
-
request.reject(:cancelled)
|
229
|
-
end
|
230
|
-
if @parser.request
|
231
|
-
@parser.request.reject(:cancelled)
|
232
|
-
@parser.eof
|
233
|
-
end
|
234
|
-
@queue = []
|
235
|
-
close_connection
|
236
|
-
end
|
237
|
-
|
238
|
-
def http_proxy?
|
239
|
-
@proxy && !@tls
|
240
|
-
end
|
241
|
-
|
242
|
-
|
243
|
-
private
|
244
|
-
|
245
|
-
|
246
|
-
def next_request
|
247
|
-
return if @parser.request || @queue.length == 0
|
248
|
-
|
249
|
-
if @connection
|
250
|
-
req = @queue.pop
|
251
|
-
@connection.request = req
|
252
|
-
@parser.new_request(req)
|
253
|
-
|
254
|
-
req.execute(@connection)
|
255
|
-
else
|
256
|
-
new_connection
|
257
|
-
end
|
258
|
-
end
|
259
|
-
|
260
|
-
def new_connection
|
261
|
-
if @queue.length > 0 && @connection.nil?
|
262
|
-
@connection = Connection.new(@host, @port, @tls, @proxy, self)
|
263
|
-
start_timer
|
264
|
-
end
|
265
|
-
@connection
|
266
|
-
end
|
267
|
-
|
268
|
-
def close_connection
|
269
|
-
return if @connection.nil?
|
270
|
-
@connection.close_connection(@parser.request)
|
271
|
-
stop_timer
|
272
|
-
@connection = nil
|
273
|
-
end
|
274
|
-
|
275
|
-
|
276
|
-
def start_timer
|
277
|
-
@timer.cancel if @timer
|
278
|
-
@timer = @thread.scheduler.in(@inactivity_timeout) do
|
279
|
-
@timer = nil
|
280
|
-
idle_timeout
|
281
|
-
end
|
282
|
-
end
|
283
|
-
alias_method :restart_timer, :start_timer
|
284
|
-
|
285
|
-
def stop_timer
|
286
|
-
@timer.cancel unless @timer.nil?
|
287
|
-
@timer = nil
|
288
|
-
end
|
289
|
-
|
290
|
-
def idle_timeout
|
291
|
-
@parser.reason = :timeout if @parser.request
|
292
|
-
@connection.reason = :timeout
|
293
|
-
close_connection
|
294
|
-
end
|
295
|
-
end
|
296
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
module UV
|
6
|
+
class CookieJar
|
7
|
+
def initialize
|
8
|
+
@jar = ::CookieJar::Jar.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def set(uri, string)
|
12
|
+
@jar.set_cookie(uri, string) rescue nil # drop invalid cookies
|
13
|
+
end
|
14
|
+
|
15
|
+
def get(uri)
|
16
|
+
uri = URI.parse(uri) rescue nil
|
17
|
+
uri ? @jar.get_cookies(uri).map(&:to_s) : []
|
18
|
+
end
|
19
|
+
|
20
|
+
def get_hash(uri)
|
21
|
+
uri = URI.parse(uri) rescue nil
|
22
|
+
cookies = {}
|
23
|
+
if uri
|
24
|
+
@jar.get_cookies(uri).each do |cookie|
|
25
|
+
cookies[cookie.name.to_sym] = cookie.value
|
26
|
+
end
|
27
|
+
end
|
28
|
+
cookies
|
29
|
+
end
|
30
|
+
|
31
|
+
def clear_cookies
|
32
|
+
@jar = ::CookieJar::Jar.new
|
33
|
+
end
|
34
|
+
end # CookieJar
|
35
|
+
|
36
|
+
# HTTPS Proxy - connect to proxy
|
37
|
+
# CONNECT #{target_host}:#{target_port} HTTP/1.0\r\n"
|
38
|
+
# Proxy-Authorization: Basic #{encoded_credentials}\r\n
|
39
|
+
# \r\n
|
40
|
+
# Parse response =~ %r{\AHTTP/1\.[01] 200 .*\r\n\r\n}m
|
41
|
+
# use_tls
|
42
|
+
# send requests as usual
|
43
|
+
|
44
|
+
# HTTP Proxy - connect to proxy
|
45
|
+
# GET #{url_with_host} HTTP/1.1\r\n"
|
46
|
+
# Proxy-Authorization: Basic #{encoded_credentials}\r\n
|
47
|
+
# \r\n
|
48
|
+
|
49
|
+
class HttpEndpoint
|
50
|
+
class Connection < OutboundConnection
|
51
|
+
def initialize(host, port, tls, proxy, client)
|
52
|
+
@target_host = host
|
53
|
+
@client = client
|
54
|
+
@request = nil
|
55
|
+
|
56
|
+
if proxy
|
57
|
+
super(proxy[:host], proxy[:port])
|
58
|
+
connect_send_handshake(host, port, proxy) if tls
|
59
|
+
else
|
60
|
+
super(host, port)
|
61
|
+
start_tls if tls
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def start_tls
|
66
|
+
opts = {host_name: @target_host}.merge(@client.tls_options)
|
67
|
+
use_tls(opts)
|
68
|
+
end
|
69
|
+
|
70
|
+
def connect_send_handshake(target_host, target_port, proxy)
|
71
|
+
@negotiating = true
|
72
|
+
header = String.new("CONNECT #{target_host}:#{target_port} HTTP/1.0\r\n")
|
73
|
+
if proxy[:username] || proxy[:password]
|
74
|
+
encoded_credentials = Base64.strict_encode64([proxy[:username], proxy[:password]].join(":"))
|
75
|
+
header << "Proxy-Authorization: Basic #{encoded_credentials}\r\n"
|
76
|
+
end
|
77
|
+
header << "\r\n"
|
78
|
+
write(header)
|
79
|
+
end
|
80
|
+
|
81
|
+
attr_accessor :request, :reason
|
82
|
+
|
83
|
+
def on_read(data, *args)
|
84
|
+
if @negotiating
|
85
|
+
@negotiating = false
|
86
|
+
if data =~ %r{\AHTTP/1\.[01] 200 .*\r\n\r\n}m
|
87
|
+
start_tls
|
88
|
+
@client.connection_ready
|
89
|
+
else
|
90
|
+
@reason = "Unexpected response from proxy: #{data}"
|
91
|
+
close_connection
|
92
|
+
end
|
93
|
+
else
|
94
|
+
@client.data_received(data)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def post_init(*args)
|
99
|
+
end
|
100
|
+
|
101
|
+
def on_connect(transport)
|
102
|
+
@client.connection_ready unless @negotiating
|
103
|
+
end
|
104
|
+
|
105
|
+
def on_close
|
106
|
+
@client.connection_closed(@request, @reason)
|
107
|
+
ensure
|
108
|
+
@request = nil
|
109
|
+
@client = nil
|
110
|
+
@reason = nil
|
111
|
+
end
|
112
|
+
|
113
|
+
def close_connection(request = nil)
|
114
|
+
if request.is_a? Http::Request
|
115
|
+
@request = request
|
116
|
+
super(:after_writing)
|
117
|
+
else
|
118
|
+
super(request)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
@@defaults = {
|
125
|
+
:path => '/',
|
126
|
+
:keepalive => true
|
127
|
+
}
|
128
|
+
|
129
|
+
|
130
|
+
def initialize(host, options = {})
|
131
|
+
@queue = []
|
132
|
+
@parser = Http::Parser.new
|
133
|
+
@thread = reactor
|
134
|
+
@connection = nil
|
135
|
+
|
136
|
+
@options = @@defaults.merge(options)
|
137
|
+
@tls_options = options[:tls_options] || {}
|
138
|
+
@inactivity_timeout = options[:inactivity_timeout] || 10000
|
139
|
+
|
140
|
+
uri = host.is_a?(::URI) ? host : ::URI.parse(host)
|
141
|
+
@port = uri.port
|
142
|
+
@host = uri.host
|
143
|
+
|
144
|
+
default_port = uri.port == uri.default_port
|
145
|
+
@encoded_host = default_port ? @host : "#{@host}:#{@port}"
|
146
|
+
@proxy = @options[:proxy]
|
147
|
+
|
148
|
+
@scheme = uri.scheme
|
149
|
+
@tls = @scheme == 'https'
|
150
|
+
@cookiejar = CookieJar.new
|
151
|
+
@middleware = []
|
152
|
+
end
|
153
|
+
|
154
|
+
|
155
|
+
attr_accessor :inactivity_timeout
|
156
|
+
attr_reader :tls_options, :port, :host, :tls, :scheme, :encoded_host
|
157
|
+
attr_reader :cookiejar, :middleware, :thread, :proxy
|
158
|
+
|
159
|
+
|
160
|
+
def get(options = {}); request(:get, options); end
|
161
|
+
def head(options = {}); request(:head, options); end
|
162
|
+
def delete(options = {}); request(:delete, options); end
|
163
|
+
def put(options = {}); request(:put, options); end
|
164
|
+
def post(options = {}); request(:post, options); end
|
165
|
+
def patch(options = {}); request(:patch, options); end
|
166
|
+
def options(options = {}); request(:options, options); end
|
167
|
+
|
168
|
+
|
169
|
+
def request(method, options = {})
|
170
|
+
options = @options.merge(options)
|
171
|
+
options[:method] = method.to_sym
|
172
|
+
|
173
|
+
# Setup the request with callbacks
|
174
|
+
request = Http::Request.new(self, options)
|
175
|
+
request.then(proc { |response|
|
176
|
+
if response.keep_alive
|
177
|
+
restart_timer
|
178
|
+
else
|
179
|
+
close_connection
|
180
|
+
end
|
181
|
+
|
182
|
+
next_request
|
183
|
+
|
184
|
+
response
|
185
|
+
}, proc { |err|
|
186
|
+
@parser.eof
|
187
|
+
close_connection
|
188
|
+
next_request
|
189
|
+
::Libuv::Q.reject(@thread, err)
|
190
|
+
})
|
191
|
+
|
192
|
+
@queue.unshift(request)
|
193
|
+
|
194
|
+
next_request
|
195
|
+
request
|
196
|
+
end
|
197
|
+
|
198
|
+
# Callbacks
|
199
|
+
def connection_ready
|
200
|
+
if @queue.length > 0
|
201
|
+
restart_timer
|
202
|
+
next_request
|
203
|
+
else
|
204
|
+
close_connection
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def connection_closed(request, reason)
|
209
|
+
# We may have closed a previous connection
|
210
|
+
if @parser.request && (request.nil? || request == @parser.request)
|
211
|
+
@connection = nil
|
212
|
+
stop_timer
|
213
|
+
|
214
|
+
@parser.eof
|
215
|
+
elsif request.nil? && @parser.request.nil? && @queue.length > 0
|
216
|
+
req = @queue.pop
|
217
|
+
req.reject(reason || :connection_failure)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def data_received(data)
|
222
|
+
restart_timer
|
223
|
+
close_connection if @parser.received(data)
|
224
|
+
end
|
225
|
+
|
226
|
+
def cancel_all
|
227
|
+
@queue.each do |request|
|
228
|
+
request.reject(:cancelled)
|
229
|
+
end
|
230
|
+
if @parser.request
|
231
|
+
@parser.request.reject(:cancelled)
|
232
|
+
@parser.eof
|
233
|
+
end
|
234
|
+
@queue = []
|
235
|
+
close_connection
|
236
|
+
end
|
237
|
+
|
238
|
+
def http_proxy?
|
239
|
+
@proxy && !@tls
|
240
|
+
end
|
241
|
+
|
242
|
+
|
243
|
+
private
|
244
|
+
|
245
|
+
|
246
|
+
def next_request
|
247
|
+
return if @parser.request || @queue.length == 0
|
248
|
+
|
249
|
+
if @connection
|
250
|
+
req = @queue.pop
|
251
|
+
@connection.request = req
|
252
|
+
@parser.new_request(req)
|
253
|
+
|
254
|
+
req.execute(@connection)
|
255
|
+
else
|
256
|
+
new_connection
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
def new_connection
|
261
|
+
if @queue.length > 0 && @connection.nil?
|
262
|
+
@connection = Connection.new(@host, @port, @tls, @proxy, self)
|
263
|
+
start_timer
|
264
|
+
end
|
265
|
+
@connection
|
266
|
+
end
|
267
|
+
|
268
|
+
def close_connection
|
269
|
+
return if @connection.nil?
|
270
|
+
@connection.close_connection(@parser.request)
|
271
|
+
stop_timer
|
272
|
+
@connection = nil
|
273
|
+
end
|
274
|
+
|
275
|
+
|
276
|
+
def start_timer
|
277
|
+
@timer.cancel if @timer
|
278
|
+
@timer = @thread.scheduler.in(@inactivity_timeout) do
|
279
|
+
@timer = nil
|
280
|
+
idle_timeout
|
281
|
+
end
|
282
|
+
end
|
283
|
+
alias_method :restart_timer, :start_timer
|
284
|
+
|
285
|
+
def stop_timer
|
286
|
+
@timer.cancel unless @timer.nil?
|
287
|
+
@timer = nil
|
288
|
+
end
|
289
|
+
|
290
|
+
def idle_timeout
|
291
|
+
@parser.reason = :timeout if @parser.request
|
292
|
+
@connection.reason = :timeout
|
293
|
+
close_connection
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|