httpx 0.7.0 → 0.10.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/LICENSE.txt +48 -0
- data/README.md +9 -5
- data/doc/release_notes/0_0_1.md +7 -0
- data/doc/release_notes/0_0_2.md +9 -0
- data/doc/release_notes/0_0_3.md +9 -0
- data/doc/release_notes/0_0_4.md +7 -0
- data/doc/release_notes/0_0_5.md +5 -0
- data/doc/release_notes/0_10_0.md +66 -0
- data/doc/release_notes/0_1_0.md +9 -0
- data/doc/release_notes/0_2_0.md +5 -0
- data/doc/release_notes/0_2_1.md +16 -0
- data/doc/release_notes/0_3_0.md +12 -0
- data/doc/release_notes/0_3_1.md +6 -0
- data/doc/release_notes/0_4_0.md +51 -0
- data/doc/release_notes/0_4_1.md +3 -0
- data/doc/release_notes/0_5_0.md +15 -0
- data/doc/release_notes/0_5_1.md +14 -0
- data/doc/release_notes/0_6_0.md +5 -0
- data/doc/release_notes/0_6_1.md +6 -0
- data/doc/release_notes/0_6_2.md +6 -0
- data/doc/release_notes/0_6_3.md +13 -0
- data/doc/release_notes/0_6_4.md +21 -0
- data/doc/release_notes/0_6_5.md +22 -0
- data/doc/release_notes/0_6_6.md +19 -0
- data/doc/release_notes/0_6_7.md +5 -0
- data/doc/release_notes/0_7_0.md +46 -0
- data/doc/release_notes/0_8_0.md +27 -0
- data/doc/release_notes/0_8_1.md +8 -0
- data/doc/release_notes/0_8_2.md +7 -0
- data/doc/release_notes/0_9_0.md +38 -0
- data/lib/httpx.rb +2 -0
- data/lib/httpx/adapters/faraday.rb +1 -1
- data/lib/httpx/altsvc.rb +18 -2
- data/lib/httpx/chainable.rb +9 -8
- data/lib/httpx/connection.rb +177 -72
- data/lib/httpx/connection/http1.rb +44 -13
- data/lib/httpx/connection/http2.rb +77 -34
- data/lib/httpx/domain_name.rb +440 -0
- data/lib/httpx/errors.rb +1 -0
- data/lib/httpx/extensions.rb +23 -3
- data/lib/httpx/headers.rb +2 -2
- data/lib/httpx/io/ssl.rb +11 -4
- data/lib/httpx/io/tcp.rb +16 -5
- data/lib/httpx/io/udp.rb +4 -1
- data/lib/httpx/loggable.rb +6 -6
- data/lib/httpx/options.rb +22 -15
- data/lib/httpx/parser/http1.rb +14 -17
- data/lib/httpx/plugins/compression.rb +49 -64
- data/lib/httpx/plugins/compression/brotli.rb +10 -14
- data/lib/httpx/plugins/compression/deflate.rb +7 -6
- data/lib/httpx/plugins/compression/gzip.rb +45 -17
- data/lib/httpx/plugins/cookies.rb +21 -60
- data/lib/httpx/plugins/cookies/cookie.rb +173 -0
- data/lib/httpx/plugins/cookies/jar.rb +74 -0
- data/lib/httpx/plugins/cookies/set_cookie_parser.rb +142 -0
- data/lib/httpx/plugins/digest_authentication.rb +2 -0
- data/lib/httpx/plugins/expect.rb +12 -1
- data/lib/httpx/plugins/follow_redirects.rb +20 -2
- data/lib/httpx/plugins/h2c.rb +1 -1
- data/lib/httpx/plugins/multipart.rb +0 -8
- data/lib/httpx/plugins/persistent.rb +6 -1
- data/lib/httpx/plugins/proxy.rb +16 -12
- data/lib/httpx/plugins/proxy/http.rb +7 -2
- data/lib/httpx/plugins/proxy/socks4.rb +4 -2
- data/lib/httpx/plugins/proxy/socks5.rb +5 -1
- data/lib/httpx/plugins/push_promise.rb +2 -2
- data/lib/httpx/plugins/rate_limiter.rb +51 -0
- data/lib/httpx/plugins/retries.rb +13 -6
- data/lib/httpx/plugins/stream.rb +109 -13
- data/lib/httpx/pool.rb +13 -15
- data/lib/httpx/registry.rb +2 -1
- data/lib/httpx/request.rb +14 -19
- data/lib/httpx/resolver.rb +7 -8
- data/lib/httpx/resolver/https.rb +22 -5
- data/lib/httpx/resolver/native.rb +27 -33
- data/lib/httpx/resolver/options.rb +2 -2
- data/lib/httpx/resolver/resolver_mixin.rb +1 -1
- data/lib/httpx/response.rb +22 -17
- data/lib/httpx/selector.rb +96 -97
- data/lib/httpx/session.rb +32 -24
- data/lib/httpx/timeout.rb +7 -1
- data/lib/httpx/transcoder/chunker.rb +0 -2
- data/lib/httpx/transcoder/form.rb +0 -6
- data/lib/httpx/transcoder/json.rb +0 -4
- data/lib/httpx/utils.rb +45 -0
- data/lib/httpx/version.rb +1 -1
- data/sig/buffer.rbs +24 -0
- data/sig/callbacks.rbs +14 -0
- data/sig/chainable.rbs +37 -0
- data/sig/connection.rbs +2 -0
- data/sig/connection/http2.rbs +4 -0
- data/sig/domain_name.rbs +17 -0
- data/sig/errors.rbs +3 -0
- data/sig/headers.rbs +42 -0
- data/sig/httpx.rbs +14 -0
- data/sig/loggable.rbs +11 -0
- data/sig/missing.rbs +12 -0
- data/sig/options.rbs +118 -0
- data/sig/parser/http1.rbs +50 -0
- data/sig/plugins/authentication.rbs +11 -0
- data/sig/plugins/basic_authentication.rbs +13 -0
- data/sig/plugins/compression.rbs +55 -0
- data/sig/plugins/compression/brotli.rbs +21 -0
- data/sig/plugins/compression/deflate.rbs +17 -0
- data/sig/plugins/compression/gzip.rbs +29 -0
- data/sig/plugins/cookies.rbs +26 -0
- data/sig/plugins/cookies/cookie.rbs +50 -0
- data/sig/plugins/cookies/jar.rbs +27 -0
- data/sig/plugins/digest_authentication.rbs +33 -0
- data/sig/plugins/expect.rbs +19 -0
- data/sig/plugins/follow_redirects.rbs +37 -0
- data/sig/plugins/h2c.rbs +26 -0
- data/sig/plugins/multipart.rbs +19 -0
- data/sig/plugins/persistent.rbs +17 -0
- data/sig/plugins/proxy.rbs +47 -0
- data/sig/plugins/proxy/http.rbs +14 -0
- data/sig/plugins/proxy/socks4.rbs +33 -0
- data/sig/plugins/proxy/socks5.rbs +36 -0
- data/sig/plugins/proxy/ssh.rbs +18 -0
- data/sig/plugins/push_promise.rbs +22 -0
- data/sig/plugins/rate_limiter.rbs +11 -0
- data/sig/plugins/retries.rbs +48 -0
- data/sig/plugins/stream.rbs +39 -0
- data/sig/pool.rbs +2 -0
- data/sig/registry.rbs +9 -0
- data/sig/request.rbs +61 -0
- data/sig/response.rbs +87 -0
- data/sig/session.rbs +49 -0
- data/sig/test.rbs +9 -0
- data/sig/timeout.rbs +29 -0
- data/sig/transcoder.rbs +16 -0
- data/sig/transcoder/body.rbs +18 -0
- data/sig/transcoder/chunker.rbs +32 -0
- data/sig/transcoder/form.rbs +16 -0
- data/sig/transcoder/json.rbs +14 -0
- metadata +120 -21
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HTTPX
|
|
4
|
+
module Plugins::Cookies
|
|
5
|
+
# The Cookie Jar
|
|
6
|
+
#
|
|
7
|
+
# It holds a bunch of cookies.
|
|
8
|
+
class Jar
|
|
9
|
+
using URIExtensions
|
|
10
|
+
|
|
11
|
+
include Enumerable
|
|
12
|
+
|
|
13
|
+
def initialize_dup(orig)
|
|
14
|
+
super
|
|
15
|
+
@cookies = orig.instance_variable_get(:@cookies).dup
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def initialize(cookies = nil)
|
|
19
|
+
@cookies = []
|
|
20
|
+
|
|
21
|
+
cookies.each do |elem|
|
|
22
|
+
cookie = case elem
|
|
23
|
+
when Cookie
|
|
24
|
+
elem
|
|
25
|
+
when Array
|
|
26
|
+
Cookie.new(*elem)
|
|
27
|
+
else
|
|
28
|
+
Cookie.new(elem)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
@cookies << cookie
|
|
32
|
+
end if cookies
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def parse(set_cookie)
|
|
36
|
+
SetCookieParser.call(set_cookie) do |name, value, attrs|
|
|
37
|
+
add(Cookie.new(name, value, attrs))
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def add(cookie, path = nil)
|
|
42
|
+
c = cookie.dup
|
|
43
|
+
|
|
44
|
+
c.path = path if path && c.path == "/"
|
|
45
|
+
|
|
46
|
+
@cookies << c
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def [](uri)
|
|
50
|
+
each(uri).sort
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def each(uri = nil, &blk)
|
|
54
|
+
return enum_for(__method__, uri) unless block_given?
|
|
55
|
+
|
|
56
|
+
return @store.each(&blk) unless uri
|
|
57
|
+
|
|
58
|
+
uri = URI(uri)
|
|
59
|
+
|
|
60
|
+
now = Time.now
|
|
61
|
+
tpath = uri.path
|
|
62
|
+
|
|
63
|
+
@cookies.delete_if do |cookie|
|
|
64
|
+
if cookie.expired?(now)
|
|
65
|
+
true
|
|
66
|
+
else
|
|
67
|
+
yield cookie if cookie.valid_for_uri?(uri) && Cookie.path_match?(cookie.path, tpath)
|
|
68
|
+
false
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "strscan"
|
|
4
|
+
require "time"
|
|
5
|
+
|
|
6
|
+
module HTTPX
|
|
7
|
+
module Plugins::Cookies
|
|
8
|
+
module SetCookieParser
|
|
9
|
+
using(RegexpExtensions) unless Regexp.method_defined?(:match?)
|
|
10
|
+
|
|
11
|
+
# Whitespace.
|
|
12
|
+
RE_WSP = /[ \t]+/.freeze
|
|
13
|
+
|
|
14
|
+
# A pattern that matches a cookie name or attribute name which may
|
|
15
|
+
# be empty, capturing trailing whitespace.
|
|
16
|
+
RE_NAME = /(?!#{RE_WSP})[^,;\\"=]*/.freeze
|
|
17
|
+
|
|
18
|
+
RE_BAD_CHAR = /([\x00-\x20\x7F",;\\])/.freeze
|
|
19
|
+
|
|
20
|
+
# A pattern that matches the comma in a (typically date) value.
|
|
21
|
+
RE_COOKIE_COMMA = /,(?=#{RE_WSP}?#{RE_NAME}=)/.freeze
|
|
22
|
+
|
|
23
|
+
module_function
|
|
24
|
+
|
|
25
|
+
def scan_dquoted(scanner)
|
|
26
|
+
s = +""
|
|
27
|
+
|
|
28
|
+
until scanner.eos?
|
|
29
|
+
break if scanner.skip(/"/)
|
|
30
|
+
|
|
31
|
+
if scanner.skip(/\\/)
|
|
32
|
+
s << scanner.getch
|
|
33
|
+
elsif scanner.scan(/[^"\\]+/)
|
|
34
|
+
s << scanner.matched
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
s
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def scan_value(scanner, comma_as_separator = false)
|
|
42
|
+
value = +""
|
|
43
|
+
|
|
44
|
+
until scanner.eos?
|
|
45
|
+
if scanner.scan(/[^,;"]+/)
|
|
46
|
+
value << scanner.matched
|
|
47
|
+
elsif scanner.skip(/"/)
|
|
48
|
+
# RFC 6265 2.2
|
|
49
|
+
# A cookie-value may be DQUOTE'd.
|
|
50
|
+
value << scan_dquoted(scanner)
|
|
51
|
+
elsif scanner.check(/;/)
|
|
52
|
+
break
|
|
53
|
+
elsif comma_as_separator && scanner.check(RE_COOKIE_COMMA)
|
|
54
|
+
break
|
|
55
|
+
else
|
|
56
|
+
value << scanner.getch
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
value.rstrip!
|
|
61
|
+
value
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def scan_name_value(scanner, comma_as_separator = false)
|
|
65
|
+
name = scanner.scan(RE_NAME)
|
|
66
|
+
name.rstrip! if name
|
|
67
|
+
|
|
68
|
+
if scanner.skip(/=/)
|
|
69
|
+
value = scan_value(scanner, comma_as_separator)
|
|
70
|
+
else
|
|
71
|
+
scan_value(scanner, comma_as_separator)
|
|
72
|
+
value = nil
|
|
73
|
+
end
|
|
74
|
+
[name, value]
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def call(set_cookie)
|
|
78
|
+
scanner = StringScanner.new(set_cookie)
|
|
79
|
+
|
|
80
|
+
# RFC 6265 4.1.1 & 5.2
|
|
81
|
+
until scanner.eos?
|
|
82
|
+
start = scanner.pos
|
|
83
|
+
len = nil
|
|
84
|
+
|
|
85
|
+
scanner.skip(RE_WSP)
|
|
86
|
+
|
|
87
|
+
name, value = scan_name_value(scanner, true)
|
|
88
|
+
value = nil if name.empty?
|
|
89
|
+
|
|
90
|
+
attrs = {}
|
|
91
|
+
|
|
92
|
+
until scanner.eos?
|
|
93
|
+
if scanner.skip(/,/)
|
|
94
|
+
# The comma is used as separator for concatenating multiple
|
|
95
|
+
# values of a header.
|
|
96
|
+
len = (scanner.pos - 1) - start
|
|
97
|
+
break
|
|
98
|
+
elsif scanner.skip(/;/)
|
|
99
|
+
scanner.skip(RE_WSP)
|
|
100
|
+
|
|
101
|
+
aname, avalue = scan_name_value(scanner, true)
|
|
102
|
+
|
|
103
|
+
next if aname.empty? || value.nil?
|
|
104
|
+
|
|
105
|
+
aname.downcase!
|
|
106
|
+
|
|
107
|
+
case aname
|
|
108
|
+
when "expires"
|
|
109
|
+
# RFC 6265 5.2.1
|
|
110
|
+
(avalue &&= Time.httpdate(avalue)) || next
|
|
111
|
+
when "max-age"
|
|
112
|
+
# RFC 6265 5.2.2
|
|
113
|
+
next unless /\A-?\d+\z/.match?(avalue)
|
|
114
|
+
|
|
115
|
+
avalue = Integer(avalue)
|
|
116
|
+
when "domain"
|
|
117
|
+
# RFC 6265 5.2.3
|
|
118
|
+
# An empty value SHOULD be ignored.
|
|
119
|
+
next if avalue.nil? || avalue.empty?
|
|
120
|
+
when "path"
|
|
121
|
+
# RFC 6265 5.2.4
|
|
122
|
+
# A relative path must be ignored rather than normalizing it
|
|
123
|
+
# to "/".
|
|
124
|
+
next unless avalue.start_with?("/")
|
|
125
|
+
when "secure", "httponly"
|
|
126
|
+
# RFC 6265 5.2.5, 5.2.6
|
|
127
|
+
avalue = true
|
|
128
|
+
end
|
|
129
|
+
attrs[aname] = avalue
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
len ||= scanner.pos - start
|
|
134
|
+
|
|
135
|
+
next if len > Cookie::MAX_LENGTH
|
|
136
|
+
|
|
137
|
+
yield(name, value, attrs) if name && !name.empty? && value
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
data/lib/httpx/plugins/expect.rb
CHANGED
|
@@ -18,14 +18,24 @@ module HTTPX
|
|
|
18
18
|
|
|
19
19
|
seconds
|
|
20
20
|
end
|
|
21
|
+
|
|
22
|
+
def_option(:expect_threshold_size) do |bytes|
|
|
23
|
+
bytes = Integer(bytes)
|
|
24
|
+
raise Error, ":expect_threshold_size must be positive" unless bytes.positive?
|
|
25
|
+
|
|
26
|
+
bytes
|
|
27
|
+
end
|
|
21
28
|
end.new(options).merge(expect_timeout: EXPECT_TIMEOUT)
|
|
22
29
|
end
|
|
23
30
|
|
|
24
31
|
module RequestBodyMethods
|
|
25
|
-
def initialize(
|
|
32
|
+
def initialize(*, options)
|
|
26
33
|
super
|
|
27
34
|
return if @body.nil?
|
|
28
35
|
|
|
36
|
+
threshold = options.expect_threshold_size
|
|
37
|
+
return if threshold && !unbounded_body? && @body.bytesize < threshold
|
|
38
|
+
|
|
29
39
|
@headers["expect"] = "100-continue"
|
|
30
40
|
end
|
|
31
41
|
end
|
|
@@ -50,6 +60,7 @@ module HTTPX
|
|
|
50
60
|
return unless response
|
|
51
61
|
|
|
52
62
|
if response.status == 417 && request.headers.key?("expect")
|
|
63
|
+
response.close
|
|
53
64
|
request.headers.delete("expect")
|
|
54
65
|
request.transition(:idle)
|
|
55
66
|
connection = find_connection(request, connections, options)
|
|
@@ -59,8 +59,26 @@ module HTTPX
|
|
|
59
59
|
return ErrorResponse.new(request, error, options)
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
retry_after = response.headers["retry-after"]
|
|
63
|
+
|
|
64
|
+
if retry_after
|
|
65
|
+
# Servers send the "Retry-After" header field to indicate how long the
|
|
66
|
+
# user agent ought to wait before making a follow-up request.
|
|
67
|
+
# When sent with any 3xx (Redirection) response, Retry-After indicates
|
|
68
|
+
# the minimum time that the user agent is asked to wait before issuing
|
|
69
|
+
# the redirected request.
|
|
70
|
+
#
|
|
71
|
+
retry_after = Utils.parse_retry_after(retry_after)
|
|
72
|
+
|
|
73
|
+
log { "redirecting after #{retry_after} secs..." }
|
|
74
|
+
pool.after(retry_after) do
|
|
75
|
+
connection = find_connection(retry_request, connections, options)
|
|
76
|
+
connection.send(retry_request)
|
|
77
|
+
end
|
|
78
|
+
else
|
|
79
|
+
connection = find_connection(retry_request, connections, options)
|
|
80
|
+
connection.send(retry_request)
|
|
81
|
+
end
|
|
64
82
|
nil
|
|
65
83
|
end
|
|
66
84
|
|
data/lib/httpx/plugins/h2c.rb
CHANGED
|
@@ -19,7 +19,12 @@ module HTTPX
|
|
|
19
19
|
#
|
|
20
20
|
module Persistent
|
|
21
21
|
def self.load_dependencies(klass)
|
|
22
|
-
klass.
|
|
22
|
+
max_retries = if klass.default_options.respond_to?(:max_retries)
|
|
23
|
+
[klass.default_options.max_retries, 1].max
|
|
24
|
+
else
|
|
25
|
+
1
|
|
26
|
+
end
|
|
27
|
+
klass.plugin(:retries, max_retries: max_retries, retry_change_requests: true)
|
|
23
28
|
end
|
|
24
29
|
|
|
25
30
|
def self.extra_options(options)
|
data/lib/httpx/plugins/proxy.rb
CHANGED
|
@@ -179,20 +179,9 @@ module HTTPX
|
|
|
179
179
|
super || @state == :connecting || @state == :connected
|
|
180
180
|
end
|
|
181
181
|
|
|
182
|
-
def to_io
|
|
183
|
-
return super unless @options.proxy
|
|
184
|
-
|
|
185
|
-
case @state
|
|
186
|
-
when :idle
|
|
187
|
-
transition(:connecting)
|
|
188
|
-
when :connected
|
|
189
|
-
transition(:open)
|
|
190
|
-
end
|
|
191
|
-
@io.to_io
|
|
192
|
-
end
|
|
193
|
-
|
|
194
182
|
def call
|
|
195
183
|
super
|
|
184
|
+
|
|
196
185
|
return unless @options.proxy
|
|
197
186
|
|
|
198
187
|
case @state
|
|
@@ -210,11 +199,26 @@ module HTTPX
|
|
|
210
199
|
emit(:close)
|
|
211
200
|
end
|
|
212
201
|
|
|
202
|
+
private
|
|
203
|
+
|
|
204
|
+
def connect
|
|
205
|
+
return super unless @options.proxy
|
|
206
|
+
|
|
207
|
+
case @state
|
|
208
|
+
when :idle
|
|
209
|
+
transition(:connecting)
|
|
210
|
+
when :connected
|
|
211
|
+
transition(:open)
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
213
215
|
def transition(nextstate)
|
|
214
216
|
return super unless @options.proxy
|
|
215
217
|
|
|
216
218
|
case nextstate
|
|
217
219
|
when :closing
|
|
220
|
+
# this is a hack so that we can use the super method
|
|
221
|
+
# and it'll thing that the current state is open
|
|
218
222
|
@state = :open if @state == :connecting
|
|
219
223
|
end
|
|
220
224
|
super
|
|
@@ -7,6 +7,10 @@ module HTTPX
|
|
|
7
7
|
module Proxy
|
|
8
8
|
module HTTP
|
|
9
9
|
module ConnectionMethods
|
|
10
|
+
def connecting?
|
|
11
|
+
super || @state == :connecting || @state == :connected
|
|
12
|
+
end
|
|
13
|
+
|
|
10
14
|
private
|
|
11
15
|
|
|
12
16
|
def transition(nextstate)
|
|
@@ -34,7 +38,6 @@ module HTTPX
|
|
|
34
38
|
when :idle
|
|
35
39
|
@parser = ProxyParser.new(@write_buffer, @options)
|
|
36
40
|
set_parser_callbacks(@parser)
|
|
37
|
-
@parser.on(:close) { transition(:closing) }
|
|
38
41
|
end
|
|
39
42
|
end
|
|
40
43
|
super
|
|
@@ -48,7 +51,7 @@ module HTTPX
|
|
|
48
51
|
#
|
|
49
52
|
if req.uri.scheme == "https"
|
|
50
53
|
connect_request = ConnectRequest.new(req.uri, @options)
|
|
51
|
-
|
|
54
|
+
@inflight += 1
|
|
52
55
|
parser.send(connect_request)
|
|
53
56
|
else
|
|
54
57
|
transition(:connected)
|
|
@@ -56,6 +59,7 @@ module HTTPX
|
|
|
56
59
|
end
|
|
57
60
|
|
|
58
61
|
def __http_on_connect(_, response)
|
|
62
|
+
@inflight -= 1
|
|
59
63
|
if response.status == 200
|
|
60
64
|
req = @pending.first
|
|
61
65
|
request_uri = req.uri
|
|
@@ -67,6 +71,7 @@ module HTTPX
|
|
|
67
71
|
while (req = pending.shift)
|
|
68
72
|
req.emit(:response, response)
|
|
69
73
|
end
|
|
74
|
+
reset
|
|
70
75
|
end
|
|
71
76
|
end
|
|
72
77
|
end
|
|
@@ -39,7 +39,7 @@ module HTTPX
|
|
|
39
39
|
|
|
40
40
|
@parser = nil
|
|
41
41
|
end
|
|
42
|
-
log(level: 1
|
|
42
|
+
log(level: 1) { "SOCKS4: #{nextstate}: #{@write_buffer.to_s.inspect}" } unless nextstate == :open
|
|
43
43
|
super
|
|
44
44
|
end
|
|
45
45
|
|
|
@@ -91,6 +91,8 @@ module HTTPX
|
|
|
91
91
|
end
|
|
92
92
|
|
|
93
93
|
module Packet
|
|
94
|
+
using(RegexpExtensions) unless Regexp.method_defined?(:match?)
|
|
95
|
+
|
|
94
96
|
module_function
|
|
95
97
|
|
|
96
98
|
def connect(parameters, uri)
|
|
@@ -101,7 +103,7 @@ module HTTPX
|
|
|
101
103
|
|
|
102
104
|
packet << [ip.to_i].pack("N")
|
|
103
105
|
rescue IPAddr::InvalidAddressError
|
|
104
|
-
if parameters.uri.scheme
|
|
106
|
+
if /^socks4a?$/.match?(parameters.uri.scheme)
|
|
105
107
|
# resolv defaults to IPv4, and socks4 doesn't support IPv6 otherwise
|
|
106
108
|
ip = IPAddr.new(Resolv.getaddress(uri.host))
|
|
107
109
|
packet << [ip.to_i].pack("N")
|