httpx 0.18.4 → 0.19.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/doc/release_notes/0_18_3.md +1 -1
- data/doc/release_notes/0_18_4.md +2 -2
- data/doc/release_notes/0_18_5.md +10 -0
- data/doc/release_notes/0_18_6.md +5 -0
- data/doc/release_notes/0_18_7.md +5 -0
- data/doc/release_notes/0_19_0.md +39 -0
- data/lib/httpx/adapters/faraday.rb +7 -3
- data/lib/httpx/connection/http1.rb +6 -6
- data/lib/httpx/connection/http2.rb +7 -5
- data/lib/httpx/connection.rb +22 -10
- data/lib/httpx/extensions.rb +16 -0
- data/lib/httpx/headers.rb +0 -2
- data/lib/httpx/io/tcp.rb +27 -6
- data/lib/httpx/options.rb +44 -11
- data/lib/httpx/plugins/cookies.rb +5 -7
- data/lib/httpx/plugins/internal_telemetry.rb +1 -1
- data/lib/httpx/plugins/multipart/mime_type_detector.rb +7 -1
- data/lib/httpx/plugins/proxy/http.rb +10 -23
- data/lib/httpx/plugins/proxy/socks4.rb +1 -1
- data/lib/httpx/plugins/proxy/socks5.rb +1 -1
- data/lib/httpx/plugins/proxy.rb +35 -15
- data/lib/httpx/plugins/retries.rb +1 -1
- data/lib/httpx/pool.rb +40 -20
- data/lib/httpx/resolver/https.rb +32 -42
- data/lib/httpx/resolver/multi.rb +79 -0
- data/lib/httpx/resolver/native.rb +28 -36
- data/lib/httpx/resolver/resolver.rb +92 -0
- data/lib/httpx/resolver/system.rb +175 -19
- data/lib/httpx/resolver.rb +37 -11
- data/lib/httpx/response.rb +4 -2
- data/lib/httpx/session.rb +1 -15
- data/lib/httpx/session_extensions.rb +26 -0
- data/lib/httpx/timers.rb +1 -1
- data/lib/httpx/transcoder/chunker.rb +0 -1
- data/lib/httpx/version.rb +1 -1
- data/lib/httpx.rb +3 -0
- data/sig/connection/http1.rbs +0 -2
- data/sig/connection/http2.rbs +2 -2
- data/sig/connection.rbs +1 -0
- data/sig/errors.rbs +8 -0
- data/sig/headers.rbs +0 -2
- data/sig/httpx.rbs +4 -0
- data/sig/options.rbs +10 -7
- data/sig/parser/http1.rbs +14 -5
- data/sig/pool.rbs +17 -9
- data/sig/registry.rbs +3 -0
- data/sig/request.rbs +11 -0
- data/sig/resolver/https.rbs +15 -27
- data/sig/resolver/multi.rbs +7 -0
- data/sig/resolver/native.rbs +3 -12
- data/sig/resolver/resolver.rbs +36 -0
- data/sig/resolver/system.rbs +3 -9
- data/sig/resolver.rbs +12 -10
- data/sig/response.rbs +15 -5
- data/sig/selector.rbs +3 -3
- data/sig/timers.rbs +5 -2
- data/sig/transcoder/chunker.rbs +16 -5
- data/sig/transcoder/json.rbs +5 -0
- data/sig/transcoder.rbs +3 -1
- metadata +15 -4
- data/lib/httpx/resolver/resolver_mixin.rb +0 -75
- data/sig/resolver/resolver_mixin.rbs +0 -26
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1d2af1a18388c76620d8a5b28e0f1d2dd90a4d2d0ee2c514be85ea7ba1b05c45
|
4
|
+
data.tar.gz: ca44331c601c46d51813cd5ccab8b258e94cf517913e26b78716885234e6cf86
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eac8b6a85fdd44dda2bcaf000cc7dc5dfa19b075165b52fa709dd8b575e9b4a919293e724242d2449794b74e28cf34c00687c6f99e23baee563c45626429b3c7
|
7
|
+
data.tar.gz: 564f2e8f6d580070eaffa3cda012f0efb032948ce4851b96f947cd5a004d1e8fde9496f29031b341709ccc44040ea3473304697e5d5c7c659b7c2cd286702b78
|
data/doc/release_notes/0_18_3.md
CHANGED
@@ -4,4 +4,4 @@
|
|
4
4
|
|
5
5
|
* request bodies eager-loaded from enumerables yield duped partial chunks.
|
6
6
|
|
7
|
-
An error was observed while looking at webmock integration, where requests formed via the multipart plugin
|
7
|
+
An error was observed while looking at webmock integration, where requests formed via the multipart plugin were returning an empty string as body. The issue was caused by an optimization on multipart encoder, which reuses the same buffer when reading chunks. Unfortunately, these cannot be yielded the same way via IO.copy_stream, as the same (cleared) buffer will be used to generate the eager-loaded body chunks.
|
data/doc/release_notes/0_18_4.md
CHANGED
@@ -4,11 +4,11 @@
|
|
4
4
|
|
5
5
|
* faraday adapter: added support for `#on_data` callback in order to support [faraday streaming](https://lostisland.github.io/faraday/usage/streaming).
|
6
6
|
|
7
|
-
* multipart plugin: removed support for file mime type detection using `mime-types`. The reasoning behind it was that `mime-types` uses the filename, which is a very
|
7
|
+
* multipart plugin: removed support for file mime type detection using `mime-types`. The reasoning behind it was that `mime-types` uses the filename, which is a very inaccurate detection strategy (ex: an mp4 video will be identified as `application/mp4`, instead of the correct `video/mp4`).
|
8
8
|
* multipart plugin: supported for file mime type detection using `marcel` and `filemagic` was added. Both use the magic header bytes, which is a more accurate strategy for file type detection.
|
9
9
|
|
10
10
|
## Bugfixes
|
11
11
|
|
12
12
|
* webmock adapter has been reimplemented to work with `httpx` plugins (such as the `:retries` plugin). Some other fixes were applied to make it work better under `vcr` (a common `webmock` extension).
|
13
13
|
|
14
|
-
* fixed the URI-related bug which was making requests stall under ruby 3.1 (still not officially
|
14
|
+
* fixed the URI-related bug which was making requests stall under ruby 3.1 (still not officially testing against it).
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# 0.18.5
|
2
|
+
|
3
|
+
## Improvements
|
4
|
+
|
5
|
+
* ruby 3.1 is now officially supported.
|
6
|
+
* when a user sets a `Host` header for an HTTP/2 request, this will be used in the `:authority` HTTP/2 pseudo-header, instead of silently ignored (mimicking what "curl" does).
|
7
|
+
|
8
|
+
## Bugfixes
|
9
|
+
|
10
|
+
* fixed "throw outside of catch block" error happening when pipelining requests on an HTTP/1 connection and resulting in a timeout.
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# 0.19.0
|
2
|
+
|
3
|
+
## Features
|
4
|
+
|
5
|
+
### Happy Eyeballs v2
|
6
|
+
|
7
|
+
When the system supports dual-stack networking, `httpx` implements the Happy Eyeballs v2 algorithm (RFC 8305) to resolve hostnames to both IPv6 and IPv4 addresses while privileging IPv6 connectivity. This is implemented by `httpx` both for the `:native` as well as the `:https` (DoH) resolver (which do not perform address sorting, thereby being "DNS-based load-balancing" friendly), and "outsourced" to `getaddrinfo` when using the `:system` resolver.
|
8
|
+
|
9
|
+
IPv6 connectivity will also be privileged for `/etc/hosts` local DNS (i.e. `localhost` connections will connec to `::1`).
|
10
|
+
|
11
|
+
A new option, `:ip_families`, will also be available (`[Socket::AF_INET6, Socket::AF_INET]` in dual-stack systems). If you'd like to i.e. force IPv4 connectivity, you can do use it (`client = HTTPX.with(ip_families: [Socket::AF_INET])`).
|
12
|
+
|
13
|
+
## Improvements
|
14
|
+
|
15
|
+
### DNS: :system resolver uses getaddrinfo (instead of the resolver lib)
|
16
|
+
|
17
|
+
The `:system` resolver switched to using the `getaddinfo` system function to perform DNS requests. Not only is this call **not** blocking the session event loop anymore (unlike pre-0.19.0 `:system` resolver), it adds a lot of functionality that the stdlib `resolv` library just doesn't support at the moment (such as SRV records).
|
18
|
+
|
19
|
+
### HTTP/2 proxy support
|
20
|
+
|
21
|
+
The `:proxy` plugin handles "prior-knowledge" HTTP/2 proxies.
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
HTTPX.plugin(:proxy, fallback_protocol: "h2").with_proxy(uri: "http://http2-proxy:3128").get(...
|
25
|
+
```
|
26
|
+
|
27
|
+
Connection coalescing has also been enabled for proxied connections (also `CONNECT`-tunneled connections).
|
28
|
+
|
29
|
+
### curl-to-httpx
|
30
|
+
|
31
|
+
widget in [project website](https://honeyryderchuck.gitlab.io/httpx/) to turn curl commands into the equivalent `httpx` code.
|
32
|
+
|
33
|
+
## Bugfixes
|
34
|
+
|
35
|
+
* faraday adapter now supports passing session options.
|
36
|
+
* proxy: several fixes which enabled env-var (`HTTP(S)_PROXY`) defined proxy support.
|
37
|
+
* proxy: fixed graceful recovery from proxy tcp connect errors.
|
38
|
+
* several fixes around CNAMEs timeouts with the native resolver.
|
39
|
+
* https resolver is now closed when wrapping session closes (it was left open).
|
@@ -81,6 +81,9 @@ module Faraday
|
|
81
81
|
|
82
82
|
def response=(response)
|
83
83
|
super
|
84
|
+
|
85
|
+
return if response.is_a?(::HTTPX::ErrorResponse)
|
86
|
+
|
84
87
|
response.body.on_data = @response_on_data
|
85
88
|
end
|
86
89
|
end
|
@@ -136,7 +139,7 @@ module Faraday
|
|
136
139
|
|
137
140
|
def on_response(&blk)
|
138
141
|
if blk
|
139
|
-
@on_response =
|
142
|
+
@on_response = ->(response) do
|
140
143
|
blk.call(response)
|
141
144
|
end
|
142
145
|
self
|
@@ -200,9 +203,9 @@ module Faraday
|
|
200
203
|
end
|
201
204
|
end
|
202
205
|
|
203
|
-
def initialize(app)
|
206
|
+
def initialize(app, options = {})
|
204
207
|
super(app)
|
205
|
-
@session = Session.new
|
208
|
+
@session = Session.new(options)
|
206
209
|
end
|
207
210
|
|
208
211
|
def call(env)
|
@@ -210,6 +213,7 @@ module Faraday
|
|
210
213
|
if parallel?(env)
|
211
214
|
handler = env[:parallel_manager].enqueue(env)
|
212
215
|
handler.on_response do |response|
|
216
|
+
response.raise_for_status
|
213
217
|
save_response(env, response.status, response.body.to_s, response.headers, response.reason) do |response_headers|
|
214
218
|
response_headers.merge!(response.headers)
|
215
219
|
end
|
@@ -184,7 +184,7 @@ module HTTPX
|
|
184
184
|
end
|
185
185
|
|
186
186
|
if @pipelining
|
187
|
-
disable
|
187
|
+
catch(:called) { disable }
|
188
188
|
else
|
189
189
|
@requests.each do |request|
|
190
190
|
emit(:error, request, ex)
|
@@ -297,10 +297,6 @@ module HTTPX
|
|
297
297
|
extra_headers
|
298
298
|
end
|
299
299
|
|
300
|
-
def headline_uri(request)
|
301
|
-
request.path
|
302
|
-
end
|
303
|
-
|
304
300
|
def handle(request)
|
305
301
|
catch(:buffer_full) do
|
306
302
|
request.transition(:headers)
|
@@ -314,8 +310,12 @@ module HTTPX
|
|
314
310
|
end
|
315
311
|
end
|
316
312
|
|
313
|
+
def join_headline(request)
|
314
|
+
"#{request.verb.to_s.upcase} #{request.path} HTTP/#{@version.join(".")}"
|
315
|
+
end
|
316
|
+
|
317
317
|
def join_headers(request)
|
318
|
-
headline =
|
318
|
+
headline = join_headline(request)
|
319
319
|
@buffer << headline << CRLF
|
320
320
|
log(color: :yellow) { "<- HEADLINE: #{headline.chomp.inspect}" }
|
321
321
|
extra_headers = set_protocol_headers(request)
|
@@ -158,10 +158,6 @@ module HTTPX
|
|
158
158
|
end
|
159
159
|
end
|
160
160
|
|
161
|
-
def headline_uri(request)
|
162
|
-
request.path
|
163
|
-
end
|
164
|
-
|
165
161
|
def handle(request, stream)
|
166
162
|
catch(:buffer_full) do
|
167
163
|
request.transition(:headers)
|
@@ -213,13 +209,19 @@ module HTTPX
|
|
213
209
|
{
|
214
210
|
":scheme" => request.scheme,
|
215
211
|
":method" => request.verb.to_s.upcase,
|
216
|
-
":path" =>
|
212
|
+
":path" => request.path,
|
217
213
|
":authority" => request.authority,
|
218
214
|
}
|
219
215
|
end
|
220
216
|
|
221
217
|
def join_headers(stream, request)
|
222
218
|
extra_headers = set_protocol_headers(request)
|
219
|
+
|
220
|
+
if request.headers.key?("host")
|
221
|
+
log { "forbidden \"host\" header found (#{request.headers["host"]}), will use it as authority..." }
|
222
|
+
extra_headers[":authority"] = request.headers["host"]
|
223
|
+
end
|
224
|
+
|
223
225
|
log(level: 1, color: :yellow) do
|
224
226
|
request.headers.merge(extra_headers).each.map { |k, v| "#{stream.id}: -> HEADER: #{k}: #{v}" }.join("\n")
|
225
227
|
end
|
data/lib/httpx/connection.rb
CHANGED
@@ -44,7 +44,7 @@ module HTTPX
|
|
44
44
|
|
45
45
|
def_delegator :@write_buffer, :empty?
|
46
46
|
|
47
|
-
attr_reader :origin, :state, :pending, :options
|
47
|
+
attr_reader :io, :origin, :origins, :state, :pending, :options
|
48
48
|
|
49
49
|
attr_writer :timers
|
50
50
|
|
@@ -78,7 +78,11 @@ module HTTPX
|
|
78
78
|
# this is a semi-private method, to be used by the resolver
|
79
79
|
# to initiate the io object.
|
80
80
|
def addresses=(addrs)
|
81
|
-
@io
|
81
|
+
if @io
|
82
|
+
@io.add_addresses(addrs)
|
83
|
+
else
|
84
|
+
@io = IO.registry(@type).new(@origin, addrs, @options)
|
85
|
+
end
|
82
86
|
end
|
83
87
|
|
84
88
|
def addresses
|
@@ -489,6 +493,18 @@ module HTTPX
|
|
489
493
|
end
|
490
494
|
|
491
495
|
def transition(nextstate)
|
496
|
+
handle_transition(nextstate)
|
497
|
+
rescue Errno::ECONNREFUSED,
|
498
|
+
Errno::EADDRNOTAVAIL,
|
499
|
+
Errno::EHOSTUNREACH,
|
500
|
+
TLSError => e
|
501
|
+
# connect errors, exit gracefully
|
502
|
+
handle_error(e)
|
503
|
+
@state = :closed
|
504
|
+
emit(:close)
|
505
|
+
end
|
506
|
+
|
507
|
+
def handle_transition(nextstate)
|
492
508
|
case nextstate
|
493
509
|
when :idle
|
494
510
|
@timeout = @current_timeout = @options.timeout[:connect_timeout]
|
@@ -525,14 +541,6 @@ module HTTPX
|
|
525
541
|
emit(:activate)
|
526
542
|
end
|
527
543
|
@state = nextstate
|
528
|
-
rescue Errno::ECONNREFUSED,
|
529
|
-
Errno::EADDRNOTAVAIL,
|
530
|
-
Errno::EHOSTUNREACH,
|
531
|
-
TLSError => e
|
532
|
-
# connect errors, exit gracefully
|
533
|
-
handle_error(e)
|
534
|
-
@state = :closed
|
535
|
-
emit(:close)
|
536
544
|
end
|
537
545
|
|
538
546
|
def purge_after_closed
|
@@ -550,6 +558,10 @@ module HTTPX
|
|
550
558
|
ex.set_backtrace(error.backtrace)
|
551
559
|
error = ex
|
552
560
|
else
|
561
|
+
# inactive connections do not contribute to the select loop, therefore
|
562
|
+
# they should fail due to such errors.
|
563
|
+
return if @state == :inactive
|
564
|
+
|
553
565
|
if @timeout
|
554
566
|
@timeout -= error.timeout
|
555
567
|
return unless @timeout <= 0
|
data/lib/httpx/extensions.rb
CHANGED
@@ -54,6 +54,22 @@ module HTTPX
|
|
54
54
|
Numeric.__send__(:include, NegMethods)
|
55
55
|
end
|
56
56
|
|
57
|
+
module StringExtensions
|
58
|
+
refine String do
|
59
|
+
def delete_suffix!(suffix)
|
60
|
+
suffix = Backports.coerce_to_str(suffix)
|
61
|
+
chomp! if frozen?
|
62
|
+
len = suffix.length
|
63
|
+
if len > 0 && index(suffix, -len)
|
64
|
+
self[-len..-1] = ''
|
65
|
+
self
|
66
|
+
else
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
end unless String.method_defined?(:delete_suffix!)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
57
73
|
module HashExtensions
|
58
74
|
refine Hash do
|
59
75
|
def compact
|
data/lib/httpx/headers.rb
CHANGED
data/lib/httpx/io/tcp.rb
CHANGED
@@ -15,6 +15,7 @@ module HTTPX
|
|
15
15
|
|
16
16
|
def initialize(origin, addresses, options)
|
17
17
|
@state = :idle
|
18
|
+
@addresses = []
|
18
19
|
@hostname = origin.host
|
19
20
|
@options = Options.new(options)
|
20
21
|
@fallback_protocol = @options.fallback_protocol
|
@@ -30,15 +31,29 @@ module HTTPX
|
|
30
31
|
raise Error, "Given IO objects do not match the request authority" unless @io
|
31
32
|
|
32
33
|
_, _, _, @ip = @io.addr
|
33
|
-
@addresses
|
34
|
-
@ip_index = @addresses.size - 1
|
34
|
+
@addresses << @ip
|
35
35
|
@keep_open = true
|
36
36
|
@state = :connected
|
37
37
|
else
|
38
|
-
|
38
|
+
add_addresses(addresses)
|
39
39
|
end
|
40
40
|
@ip_index = @addresses.size - 1
|
41
|
-
@io ||= build_socket
|
41
|
+
# @io ||= build_socket
|
42
|
+
end
|
43
|
+
|
44
|
+
def add_addresses(addrs)
|
45
|
+
return if addrs.empty?
|
46
|
+
|
47
|
+
addrs = addrs.map { |addr| addr.is_a?(IPAddr) ? addr : IPAddr.new(addr) }
|
48
|
+
|
49
|
+
ip_index = @ip_index || (@addresses.size - 1)
|
50
|
+
if addrs.first.ipv6?
|
51
|
+
# should be the next in line
|
52
|
+
@addresses = [*@addresses[0, ip_index], *addrs, *@addresses[ip_index..-1]]
|
53
|
+
else
|
54
|
+
@addresses.unshift(*addrs)
|
55
|
+
@ip_index += addrs.size if @ip_index
|
56
|
+
end
|
42
57
|
end
|
43
58
|
|
44
59
|
def to_io
|
@@ -52,20 +67,26 @@ module HTTPX
|
|
52
67
|
def connect
|
53
68
|
return unless closed?
|
54
69
|
|
55
|
-
if @io.closed?
|
70
|
+
if !@io || @io.closed?
|
56
71
|
transition(:idle)
|
57
72
|
@io = build_socket
|
58
73
|
end
|
59
74
|
try_connect
|
60
|
-
rescue Errno::
|
75
|
+
rescue Errno::ECONNREFUSED,
|
76
|
+
Errno::EADDRNOTAVAIL,
|
77
|
+
Errno::EHOSTUNREACH => e
|
61
78
|
raise e if @ip_index <= 0
|
62
79
|
|
80
|
+
log { "failed connecting to #{@ip} (#{e.message}), trying next..." }
|
63
81
|
@ip_index -= 1
|
82
|
+
@io = build_socket
|
64
83
|
retry
|
65
84
|
rescue Errno::ETIMEDOUT => e
|
66
85
|
raise ConnectTimeoutError.new(@options.timeout[:connect_timeout], e.message) if @ip_index <= 0
|
67
86
|
|
87
|
+
log { "failed connecting to #{@ip} (#{e.message}), trying next..." }
|
68
88
|
@ip_index -= 1
|
89
|
+
@io = build_socket
|
69
90
|
retry
|
70
91
|
end
|
71
92
|
|
data/lib/httpx/options.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "socket"
|
4
|
+
|
3
5
|
module HTTPX
|
4
6
|
class Options
|
5
7
|
WINDOW_SIZE = 1 << 14 # 16K
|
@@ -9,6 +11,18 @@ module HTTPX
|
|
9
11
|
KEEP_ALIVE_TIMEOUT = 20
|
10
12
|
SETTINGS_TIMEOUT = 10
|
11
13
|
|
14
|
+
# https://github.com/ruby/resolv/blob/095f1c003f6073730500f02acbdbc55f83d70987/lib/resolv.rb#L408
|
15
|
+
ip_address_families = begin
|
16
|
+
list = Socket.ip_address_list
|
17
|
+
if list.any? { |a| a.ipv6? && !a.ipv6_loopback? && !a.ipv6_linklocal? }
|
18
|
+
[Socket::AF_INET6, Socket::AF_INET]
|
19
|
+
else
|
20
|
+
[Socket::AF_INET]
|
21
|
+
end
|
22
|
+
rescue NotImplementedError
|
23
|
+
[Socket::AF_INET]
|
24
|
+
end
|
25
|
+
|
12
26
|
DEFAULT_OPTIONS = {
|
13
27
|
:debug => ENV.key?("HTTPX_DEBUG") ? $stderr : nil,
|
14
28
|
:debug_level => (ENV["HTTPX_DEBUG"] || 1).to_i,
|
@@ -37,6 +51,7 @@ module HTTPX
|
|
37
51
|
:persistent => false,
|
38
52
|
:resolver_class => (ENV["HTTPX_RESOLVER"] || :native).to_sym,
|
39
53
|
:resolver_options => { cache: true },
|
54
|
+
:ip_families => ip_address_families,
|
40
55
|
}.freeze
|
41
56
|
|
42
57
|
begin
|
@@ -110,20 +125,18 @@ module HTTPX
|
|
110
125
|
end
|
111
126
|
|
112
127
|
def initialize(options = {})
|
113
|
-
|
114
|
-
defaults.each do |k, v|
|
115
|
-
next if v.nil?
|
116
|
-
|
117
|
-
begin
|
118
|
-
value = __send__(:"option_#{k}", v)
|
119
|
-
instance_variable_set(:"@#{k}", value)
|
120
|
-
rescue NoMethodError
|
121
|
-
raise Error, "unknown option: #{k}"
|
122
|
-
end
|
123
|
-
end
|
128
|
+
__initialize__(options)
|
124
129
|
freeze
|
125
130
|
end
|
126
131
|
|
132
|
+
def freeze
|
133
|
+
super
|
134
|
+
@origin.freeze
|
135
|
+
@timeout.freeze
|
136
|
+
@headers.freeze
|
137
|
+
@addresses.freeze
|
138
|
+
end
|
139
|
+
|
127
140
|
def option_origin(value)
|
128
141
|
URI(value)
|
129
142
|
end
|
@@ -174,6 +187,10 @@ module HTTPX
|
|
174
187
|
Array(value)
|
175
188
|
end
|
176
189
|
|
190
|
+
def option_ip_families(value)
|
191
|
+
Array(value)
|
192
|
+
end
|
193
|
+
|
177
194
|
%i[
|
178
195
|
params form json body ssl http2_settings
|
179
196
|
request_class response_class headers_class request_body_class
|
@@ -249,5 +266,21 @@ module HTTPX
|
|
249
266
|
end
|
250
267
|
end
|
251
268
|
end
|
269
|
+
|
270
|
+
private
|
271
|
+
|
272
|
+
def __initialize__(options = {})
|
273
|
+
defaults = DEFAULT_OPTIONS.merge(options)
|
274
|
+
defaults.each do |k, v|
|
275
|
+
next if v.nil?
|
276
|
+
|
277
|
+
begin
|
278
|
+
value = __send__(:"option_#{k}", v)
|
279
|
+
instance_variable_set(:"@#{k}", value)
|
280
|
+
rescue NoMethodError
|
281
|
+
raise Error, "unknown option: #{k}"
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
252
285
|
end
|
253
286
|
end
|
@@ -18,12 +18,6 @@ module HTTPX
|
|
18
18
|
require "httpx/plugins/cookies/set_cookie_parser"
|
19
19
|
end
|
20
20
|
|
21
|
-
module OptionsMethods
|
22
|
-
def option_cookies(value)
|
23
|
-
value.is_a?(Jar) ? value : Jar.new(value)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
21
|
module InstanceMethods
|
28
22
|
extend Forwardable
|
29
23
|
|
@@ -77,7 +71,7 @@ module HTTPX
|
|
77
71
|
end
|
78
72
|
|
79
73
|
module OptionsMethods
|
80
|
-
def
|
74
|
+
def __initialize__(*)
|
81
75
|
super
|
82
76
|
|
83
77
|
return unless @headers.key?("cookie")
|
@@ -89,6 +83,10 @@ module HTTPX
|
|
89
83
|
end
|
90
84
|
end
|
91
85
|
end
|
86
|
+
|
87
|
+
def option_cookies(value)
|
88
|
+
value.is_a?(Jar) ? value : Jar.new(value)
|
89
|
+
end
|
92
90
|
end
|
93
91
|
end
|
94
92
|
register_plugin :cookies, Cookies
|
@@ -9,12 +9,18 @@ module HTTPX
|
|
9
9
|
|
10
10
|
# inspired by https://github.com/shrinerb/shrine/blob/master/lib/shrine/plugins/determine_mime_type.rb
|
11
11
|
if defined?(FileMagic)
|
12
|
+
MAGIC_NUMBER = 256 * 1024
|
13
|
+
|
12
14
|
def call(file, _)
|
13
15
|
return nil if file.eof? # FileMagic returns "application/x-empty" for empty files
|
14
16
|
|
15
|
-
FileMagic.open(FileMagic::MAGIC_MIME_TYPE) do |filemagic|
|
17
|
+
mime = FileMagic.open(FileMagic::MAGIC_MIME_TYPE) do |filemagic|
|
16
18
|
filemagic.buffer(file.read(MAGIC_NUMBER))
|
17
19
|
end
|
20
|
+
|
21
|
+
file.rewind
|
22
|
+
|
23
|
+
mime
|
18
24
|
end
|
19
25
|
elsif defined?(Marcel)
|
20
26
|
def call(file, filename)
|
@@ -13,7 +13,7 @@ module HTTPX
|
|
13
13
|
|
14
14
|
private
|
15
15
|
|
16
|
-
def
|
16
|
+
def handle_transition(nextstate)
|
17
17
|
return super unless @options.proxy && @options.proxy.uri.scheme == "http"
|
18
18
|
|
19
19
|
case nextstate
|
@@ -23,7 +23,8 @@ module HTTPX
|
|
23
23
|
@io.connect
|
24
24
|
return unless @io.connected?
|
25
25
|
|
26
|
-
@parser =
|
26
|
+
@parser = registry(@io.protocol).new(@write_buffer, @options.merge(max_concurrent_requests: 1))
|
27
|
+
@parser.extend(ProxyParser)
|
27
28
|
@parser.once(:response, &method(:__http_on_connect))
|
28
29
|
@parser.on(:close) { transition(:closing) }
|
29
30
|
__http_proxy_connect
|
@@ -36,7 +37,7 @@ module HTTPX
|
|
36
37
|
@parser.close
|
37
38
|
@parser = nil
|
38
39
|
when :idle
|
39
|
-
@parser
|
40
|
+
@parser.callbacks.clear
|
40
41
|
set_parser_callbacks(@parser)
|
41
42
|
end
|
42
43
|
end
|
@@ -54,7 +55,7 @@ module HTTPX
|
|
54
55
|
@inflight += 1
|
55
56
|
parser.send(connect_request)
|
56
57
|
else
|
57
|
-
|
58
|
+
handle_transition(:connected)
|
58
59
|
end
|
59
60
|
end
|
60
61
|
|
@@ -76,9 +77,11 @@ module HTTPX
|
|
76
77
|
end
|
77
78
|
end
|
78
79
|
|
79
|
-
|
80
|
-
def
|
81
|
-
request.
|
80
|
+
module ProxyParser
|
81
|
+
def join_headline(request)
|
82
|
+
return super if request.verb == :connect
|
83
|
+
|
84
|
+
"#{request.verb.to_s.upcase} #{request.uri} HTTP/#{@version.join(".")}"
|
82
85
|
end
|
83
86
|
|
84
87
|
def set_protocol_headers(request)
|
@@ -91,22 +94,6 @@ module HTTPX
|
|
91
94
|
end
|
92
95
|
end
|
93
96
|
|
94
|
-
class ConnectProxyParser < ProxyParser
|
95
|
-
attr_reader :pending
|
96
|
-
|
97
|
-
def headline_uri(request)
|
98
|
-
return super unless request.verb == :connect
|
99
|
-
|
100
|
-
tunnel = request.path
|
101
|
-
log { "establishing HTTP proxy tunnel to #{tunnel}" }
|
102
|
-
tunnel
|
103
|
-
end
|
104
|
-
|
105
|
-
def empty?
|
106
|
-
@requests.reject { |r| r.verb == :connect }.empty? || @requests.all? { |request| !request.response.nil? }
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
97
|
class ConnectRequest < Request
|
111
98
|
def initialize(uri, _options)
|
112
99
|
super(:connect, uri, {})
|