httpx 0.21.0 → 0.22.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +53 -35
- data/doc/release_notes/0_10_0.md +2 -2
- data/doc/release_notes/0_11_0.md +3 -5
- data/doc/release_notes/0_12_0.md +5 -5
- data/doc/release_notes/0_13_0.md +4 -4
- data/doc/release_notes/0_14_0.md +2 -2
- data/doc/release_notes/0_16_0.md +3 -3
- data/doc/release_notes/0_17_0.md +1 -1
- data/doc/release_notes/0_18_0.md +4 -4
- data/doc/release_notes/0_18_2.md +1 -1
- data/doc/release_notes/0_19_0.md +1 -1
- data/doc/release_notes/0_20_0.md +1 -1
- data/doc/release_notes/0_21_0.md +7 -5
- data/doc/release_notes/0_21_1.md +12 -0
- data/doc/release_notes/0_22_0.md +13 -0
- data/doc/release_notes/0_22_1.md +11 -0
- data/doc/release_notes/0_22_2.md +5 -0
- data/doc/release_notes/0_22_3.md +55 -0
- data/doc/release_notes/0_22_4.md +6 -0
- data/lib/httpx/adapters/datadog.rb +1 -1
- data/lib/httpx/adapters/faraday.rb +12 -4
- data/lib/httpx/adapters/sentry.rb +6 -2
- data/lib/httpx/connection/http2.rb +1 -1
- data/lib/httpx/connection.rb +34 -6
- data/lib/httpx/errors.rb +2 -0
- data/lib/httpx/extensions.rb +10 -1
- data/lib/httpx/io/tcp.rb +2 -1
- data/lib/httpx/io/udp.rb +4 -5
- data/lib/httpx/options.rb +2 -2
- data/lib/httpx/plugins/authentication.rb +1 -1
- data/lib/httpx/plugins/aws_sigv4.rb +2 -2
- data/lib/httpx/plugins/basic_authentication.rb +1 -1
- data/lib/httpx/plugins/circuit_breaker/circuit.rb +1 -1
- data/lib/httpx/plugins/circuit_breaker.rb +1 -1
- data/lib/httpx/plugins/compression/gzip.rb +1 -1
- data/lib/httpx/plugins/compression.rb +4 -4
- data/lib/httpx/plugins/cookies.rb +1 -1
- data/lib/httpx/plugins/digest_authentication.rb +3 -1
- data/lib/httpx/plugins/expect.rb +4 -3
- data/lib/httpx/plugins/follow_redirects.rb +8 -1
- data/lib/httpx/plugins/grpc/message.rb +1 -1
- data/lib/httpx/plugins/grpc.rb +1 -1
- data/lib/httpx/plugins/h2c.rb +1 -1
- data/lib/httpx/plugins/multipart/decoder.rb +7 -57
- data/lib/httpx/plugins/multipart.rb +2 -2
- data/lib/httpx/plugins/ntlm_authentication.rb +3 -1
- data/lib/httpx/plugins/persistent.rb +1 -1
- data/lib/httpx/plugins/proxy/http.rb +4 -2
- data/lib/httpx/plugins/proxy.rb +1 -1
- data/lib/httpx/plugins/push_promise.rb +1 -1
- data/lib/httpx/plugins/rate_limiter.rb +3 -1
- data/lib/httpx/plugins/response_cache.rb +1 -1
- data/lib/httpx/plugins/retries.rb +3 -2
- data/lib/httpx/plugins/stream.rb +0 -2
- data/lib/httpx/plugins/upgrade.rb +3 -1
- data/lib/httpx/plugins/webdav.rb +2 -0
- data/lib/httpx/pool.rb +41 -2
- data/lib/httpx/request.rb +1 -1
- data/lib/httpx/resolver/https.rb +16 -2
- data/lib/httpx/resolver/native.rb +28 -26
- data/lib/httpx/resolver/resolver.rb +9 -8
- data/lib/httpx/resolver.rb +8 -0
- data/lib/httpx/response.rb +8 -0
- data/lib/httpx/transcoder/xml.rb +0 -2
- data/lib/httpx/transcoder.rb +1 -1
- data/lib/httpx/utils.rb +30 -0
- data/lib/httpx/version.rb +1 -1
- data/lib/httpx.rb +6 -0
- data/sig/connection.rbs +3 -1
- data/sig/plugins/multipart.rbs +0 -2
- data/sig/pool.rbs +3 -1
- data/sig/resolver/https.rbs +3 -2
- data/sig/resolver/native.rbs +2 -1
- data/sig/resolver/resolver.rbs +3 -1
- data/sig/resolver.rbs +1 -1
- data/sig/response.rbs +4 -1
- data/sig/utils.rbs +2 -0
- metadata +21 -9
@@ -63,7 +63,11 @@ module HTTPX::Plugins
|
|
63
63
|
|
64
64
|
request_info = extract_request_info(req)
|
65
65
|
sentry_span.set_description("#{request_info[:method]} #{request_info[:url]}")
|
66
|
-
|
66
|
+
if res.is_a?(HTTPX::ErrorResponse)
|
67
|
+
sentry_span.set_data(:error, res.message)
|
68
|
+
else
|
69
|
+
sentry_span.set_data(:status, res.status)
|
70
|
+
end
|
67
71
|
sentry_span.set_timestamp(::Sentry.utc_now.to_f)
|
68
72
|
end
|
69
73
|
|
@@ -95,7 +99,7 @@ module HTTPX::Plugins
|
|
95
99
|
end
|
96
100
|
|
97
101
|
Sentry.register_patch do
|
98
|
-
sentry_session =
|
102
|
+
sentry_session = HTTPX.plugin(HTTPX::Plugins::Sentry)
|
99
103
|
|
100
104
|
HTTPX.send(:remove_const, :Session)
|
101
105
|
HTTPX.send(:const_set, :Session, sentry_session.class)
|
data/lib/httpx/connection.rb
CHANGED
@@ -45,10 +45,12 @@ module HTTPX
|
|
45
45
|
|
46
46
|
def_delegator :@write_buffer, :empty?
|
47
47
|
|
48
|
-
attr_reader :io, :origin, :origins, :state, :pending, :options
|
48
|
+
attr_reader :type, :io, :origin, :origins, :state, :pending, :options
|
49
49
|
|
50
50
|
attr_writer :timers
|
51
51
|
|
52
|
+
attr_accessor :family
|
53
|
+
|
52
54
|
def initialize(type, uri, options)
|
53
55
|
@type = type
|
54
56
|
@origins = [uri.origin]
|
@@ -114,7 +116,10 @@ module HTTPX
|
|
114
116
|
|
115
117
|
return false unless connection.addresses
|
116
118
|
|
117
|
-
|
119
|
+
(
|
120
|
+
(open? && @origin == connection.origin) ||
|
121
|
+
!(@io.addresses & connection.addresses).empty?
|
122
|
+
) && @options == connection.options
|
118
123
|
end
|
119
124
|
|
120
125
|
# coalescable connections need to be mergeable!
|
@@ -219,6 +224,14 @@ module HTTPX
|
|
219
224
|
@parser.close if @parser
|
220
225
|
end
|
221
226
|
|
227
|
+
# bypasses the state machine to force closing of connections still connecting.
|
228
|
+
# **only** used for Happy Eyeballs v2.
|
229
|
+
def force_reset
|
230
|
+
@state = :closing
|
231
|
+
transition(:closed)
|
232
|
+
emit(:close)
|
233
|
+
end
|
234
|
+
|
222
235
|
def reset
|
223
236
|
transition(:closing)
|
224
237
|
transition(:closed)
|
@@ -309,7 +322,7 @@ module HTTPX
|
|
309
322
|
# * the number of inflight requests
|
310
323
|
# * the number of pending requests
|
311
324
|
# * whether the write buffer has bytes (i.e. for close handshake)
|
312
|
-
if @pending.
|
325
|
+
if @pending.empty? && @inflight.zero? && @write_buffer.empty?
|
313
326
|
log(level: 3) { "NO MORE REQUESTS..." }
|
314
327
|
return
|
315
328
|
end
|
@@ -353,7 +366,7 @@ module HTTPX
|
|
353
366
|
break if @state == :closing || @state == :closed
|
354
367
|
|
355
368
|
# exit #consume altogether if all outstanding requests have been dealt with
|
356
|
-
return if @pending.
|
369
|
+
return if @pending.empty? && @inflight.zero?
|
357
370
|
end unless ((ints = interests).nil? || ints == :w || @state == :closing) && !epiped
|
358
371
|
|
359
372
|
#
|
@@ -512,10 +525,23 @@ module HTTPX
|
|
512
525
|
|
513
526
|
def transition(nextstate)
|
514
527
|
handle_transition(nextstate)
|
515
|
-
rescue Errno::
|
528
|
+
rescue Errno::ECONNABORTED,
|
529
|
+
Errno::ECONNREFUSED,
|
530
|
+
Errno::ECONNRESET,
|
516
531
|
Errno::EADDRNOTAVAIL,
|
517
532
|
Errno::EHOSTUNREACH,
|
518
|
-
|
533
|
+
Errno::EINVAL,
|
534
|
+
Errno::ENETUNREACH,
|
535
|
+
Errno::EPIPE,
|
536
|
+
Errno::ENOENT,
|
537
|
+
SocketError => e
|
538
|
+
# connect errors, exit gracefully
|
539
|
+
error = ConnectionError.new(e.message)
|
540
|
+
error.set_backtrace(e.backtrace)
|
541
|
+
connecting? && callbacks(:connect_error).any? ? emit(:connect_error, error) : handle_error(error)
|
542
|
+
@state = :closed
|
543
|
+
emit(:close)
|
544
|
+
rescue TLSError => e
|
519
545
|
# connect errors, exit gracefully
|
520
546
|
handle_error(e)
|
521
547
|
@state = :closed
|
@@ -531,6 +557,8 @@ module HTTPX
|
|
531
557
|
return if @state == :closed
|
532
558
|
|
533
559
|
@io.connect
|
560
|
+
emit(:tcp_open, self) if @io.state == :connected
|
561
|
+
|
534
562
|
return unless @io.connected?
|
535
563
|
|
536
564
|
@connected_at = Utils.now
|
data/lib/httpx/errors.rb
CHANGED
data/lib/httpx/extensions.rb
CHANGED
@@ -55,6 +55,7 @@ module HTTPX
|
|
55
55
|
end
|
56
56
|
|
57
57
|
module NumericExtensions
|
58
|
+
# Ruby 2.4 backport
|
58
59
|
refine Numeric do
|
59
60
|
def infinite?
|
60
61
|
self == Float::INFINITY
|
@@ -64,6 +65,7 @@ module HTTPX
|
|
64
65
|
|
65
66
|
module StringExtensions
|
66
67
|
refine String do
|
68
|
+
# Ruby 2.5 backport
|
67
69
|
def delete_suffix!(suffix)
|
68
70
|
suffix = Backports.coerce_to_str(suffix)
|
69
71
|
chomp! if frozen?
|
@@ -80,6 +82,7 @@ module HTTPX
|
|
80
82
|
|
81
83
|
module HashExtensions
|
82
84
|
refine Hash do
|
85
|
+
# Ruby 2.4 backport
|
83
86
|
def compact
|
84
87
|
h = {}
|
85
88
|
each do |key, value|
|
@@ -93,7 +96,7 @@ module HTTPX
|
|
93
96
|
module ArrayExtensions
|
94
97
|
module FilterMap
|
95
98
|
refine Array do
|
96
|
-
|
99
|
+
# Ruby 2.7 backport
|
97
100
|
def filter_map
|
98
101
|
return to_enum(:filter_map) unless block_given?
|
99
102
|
|
@@ -107,6 +110,7 @@ module HTTPX
|
|
107
110
|
|
108
111
|
module Sum
|
109
112
|
refine Array do
|
113
|
+
# Ruby 2.6 backport
|
110
114
|
def sum(accumulator = 0, &block)
|
111
115
|
values = block_given? ? map(&block) : self
|
112
116
|
values.inject(accumulator, :+)
|
@@ -116,6 +120,7 @@ module HTTPX
|
|
116
120
|
|
117
121
|
module Intersect
|
118
122
|
refine Array do
|
123
|
+
# Ruby 3.1 backport
|
119
124
|
def intersect?(arr)
|
120
125
|
if size < arr.size
|
121
126
|
smaller = self
|
@@ -130,6 +135,7 @@ module HTTPX
|
|
130
135
|
|
131
136
|
module IOExtensions
|
132
137
|
refine IO do
|
138
|
+
# Ruby 2.3 backport
|
133
139
|
# provides a fallback for rubies where IO#wait isn't implemented,
|
134
140
|
# but IO#wait_readable and IO#wait_writable are.
|
135
141
|
def wait(timeout = nil, _mode = :read_write)
|
@@ -144,6 +150,7 @@ module HTTPX
|
|
144
150
|
|
145
151
|
module RegexpExtensions
|
146
152
|
refine(Regexp) do
|
153
|
+
# Ruby 2.4 backport
|
147
154
|
def match?(*args)
|
148
155
|
!match(*args).nil?
|
149
156
|
end
|
@@ -151,7 +158,9 @@ module HTTPX
|
|
151
158
|
end
|
152
159
|
|
153
160
|
module URIExtensions
|
161
|
+
# uri 0.11 backport, ships with ruby 3.1
|
154
162
|
refine URI::Generic do
|
163
|
+
|
155
164
|
def non_ascii_hostname
|
156
165
|
@non_ascii_hostname
|
157
166
|
end
|
data/lib/httpx/io/tcp.rb
CHANGED
@@ -74,7 +74,8 @@ module HTTPX
|
|
74
74
|
try_connect
|
75
75
|
rescue Errno::ECONNREFUSED,
|
76
76
|
Errno::EADDRNOTAVAIL,
|
77
|
-
Errno::EHOSTUNREACH
|
77
|
+
Errno::EHOSTUNREACH,
|
78
|
+
SocketError => e
|
78
79
|
raise e if @ip_index <= 0
|
79
80
|
|
80
81
|
log { "failed connecting to #{@ip} (#{e.message}), trying next..." }
|
data/lib/httpx/io/udp.rb
CHANGED
@@ -6,11 +6,10 @@ module HTTPX
|
|
6
6
|
class UDP
|
7
7
|
include Loggable
|
8
8
|
|
9
|
-
def initialize(
|
10
|
-
|
11
|
-
@
|
12
|
-
@
|
13
|
-
@io = UDPSocket.new(ip.family)
|
9
|
+
def initialize(ip, port, options)
|
10
|
+
@host = ip
|
11
|
+
@port = port
|
12
|
+
@io = UDPSocket.new(IPAddr.new(ip).family)
|
14
13
|
@options = options
|
15
14
|
end
|
16
15
|
|
data/lib/httpx/options.rb
CHANGED
@@ -15,7 +15,7 @@ module HTTPX
|
|
15
15
|
# https://github.com/ruby/resolv/blob/095f1c003f6073730500f02acbdbc55f83d70987/lib/resolv.rb#L408
|
16
16
|
ip_address_families = begin
|
17
17
|
list = Socket.ip_address_list
|
18
|
-
if list.any? { |a| a.ipv6? && !a.ipv6_loopback? && !a.ipv6_linklocal? }
|
18
|
+
if list.any? { |a| a.ipv6? && !a.ipv6_loopback? && !a.ipv6_linklocal? && !a.ipv6_unique_local? }
|
19
19
|
[Socket::AF_INET6, Socket::AF_INET]
|
20
20
|
else
|
21
21
|
[Socket::AF_INET]
|
@@ -100,7 +100,7 @@ module HTTPX
|
|
100
100
|
end
|
101
101
|
|
102
102
|
def def_option(optname, *args, &block)
|
103
|
-
if args.
|
103
|
+
if args.empty? && !block
|
104
104
|
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
105
105
|
def option_#{optname}(v); v; end # def option_smth(v); v; end
|
106
106
|
OUT
|
@@ -6,7 +6,7 @@ module HTTPX
|
|
6
6
|
# This plugin adds a shim +authentication+ method to the session, which will fill
|
7
7
|
# the HTTP Authorization header.
|
8
8
|
#
|
9
|
-
# https://gitlab.com/
|
9
|
+
# https://gitlab.com/os85/httpx/wikis/Authentication#authentication
|
10
10
|
#
|
11
11
|
module Authentication
|
12
12
|
module InstanceMethods
|
@@ -7,7 +7,7 @@ module HTTPX
|
|
7
7
|
#
|
8
8
|
# https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html
|
9
9
|
#
|
10
|
-
# https://gitlab.com/
|
10
|
+
# https://gitlab.com/os85/httpx/wikis/AWS-SigV4
|
11
11
|
#
|
12
12
|
module AWSSigV4
|
13
13
|
Credentials = Struct.new(:username, :password, :security_token)
|
@@ -115,7 +115,7 @@ module HTTPX
|
|
115
115
|
elsif value.respond_to?(:each)
|
116
116
|
digest = OpenSSL::Digest.new(@algorithm)
|
117
117
|
|
118
|
-
mb_buffer = value.each.
|
118
|
+
mb_buffer = value.each.with_object("".b) do |chunk, buffer|
|
119
119
|
buffer << chunk
|
120
120
|
break if buffer.bytesize >= 1024 * 1024
|
121
121
|
end
|
@@ -5,7 +5,7 @@ module HTTPX
|
|
5
5
|
#
|
6
6
|
# This plugin adds helper methods to implement HTTP Basic Auth (https://tools.ietf.org/html/rfc7617)
|
7
7
|
#
|
8
|
-
# https://gitlab.com/
|
8
|
+
# https://gitlab.com/os85/httpx/wikis/Authentication#basic-authentication
|
9
9
|
#
|
10
10
|
module BasicAuth
|
11
11
|
class << self
|
@@ -5,7 +5,7 @@ module HTTPX
|
|
5
5
|
#
|
6
6
|
# This plugin implements a circuit breaker around connection errors.
|
7
7
|
#
|
8
|
-
# https://gitlab.com/
|
8
|
+
# https://gitlab.com/os85/httpx/wikis/Circuit-Breaker
|
9
9
|
#
|
10
10
|
module CircuitBreaker
|
11
11
|
using URIExtensions
|
@@ -10,7 +10,7 @@ module HTTPX
|
|
10
10
|
#
|
11
11
|
# It supports both *gzip* and *deflate*.
|
12
12
|
#
|
13
|
-
# https://gitlab.com/
|
13
|
+
# https://gitlab.com/os85/httpx/wikis/Compression
|
14
14
|
#
|
15
15
|
module Compression
|
16
16
|
class << self
|
@@ -30,7 +30,7 @@ module HTTPX
|
|
30
30
|
module OptionsMethods
|
31
31
|
def option_compression_threshold_size(value)
|
32
32
|
bytes = Integer(value)
|
33
|
-
raise TypeError, ":
|
33
|
+
raise TypeError, ":compression_threshold_size must be positive" unless bytes.positive?
|
34
34
|
|
35
35
|
bytes
|
36
36
|
end
|
@@ -138,7 +138,7 @@ module HTTPX
|
|
138
138
|
def each(&blk)
|
139
139
|
return enum_for(__method__) unless blk
|
140
140
|
|
141
|
-
return deflate(&blk) if @buffer.size.zero?
|
141
|
+
return deflate(&blk) if @buffer.size.zero? # rubocop:disable Style/ZeroLengthPredicate
|
142
142
|
|
143
143
|
@buffer.rewind
|
144
144
|
@buffer.each(&blk)
|
@@ -152,7 +152,7 @@ module HTTPX
|
|
152
152
|
private
|
153
153
|
|
154
154
|
def deflate(&blk)
|
155
|
-
return unless @buffer.size.zero?
|
155
|
+
return unless @buffer.size.zero? # rubocop:disable Style/ZeroLengthPredicate
|
156
156
|
|
157
157
|
@body.rewind
|
158
158
|
@deflater.deflate(@body, @buffer, chunk_size: 16_384, &blk)
|
@@ -9,7 +9,7 @@ module HTTPX
|
|
9
9
|
#
|
10
10
|
# It also adds a *#cookies* helper, so that you can pre-fill the cookies of a session.
|
11
11
|
#
|
12
|
-
# https://gitlab.com/
|
12
|
+
# https://gitlab.com/os85/httpx/wikis/Cookies
|
13
13
|
#
|
14
14
|
module Cookies
|
15
15
|
def self.load_dependencies(*)
|
@@ -5,7 +5,7 @@ module HTTPX
|
|
5
5
|
#
|
6
6
|
# This plugin adds helper methods to implement HTTP Digest Auth (https://tools.ietf.org/html/rfc7616)
|
7
7
|
#
|
8
|
-
# https://gitlab.com/
|
8
|
+
# https://gitlab.com/os85/httpx/wikis/Authentication#authentication
|
9
9
|
#
|
10
10
|
module DigestAuth
|
11
11
|
DigestError = Class.new(Error)
|
@@ -46,6 +46,8 @@ module HTTPX
|
|
46
46
|
|
47
47
|
probe_response = wrap { super(request).first }
|
48
48
|
|
49
|
+
return probe_response unless probe_response.is_a?(Response)
|
50
|
+
|
49
51
|
if probe_response.status == 401 && digest.can_authenticate?(probe_response.headers["www-authenticate"])
|
50
52
|
request.transition(:idle)
|
51
53
|
request.headers["authorization"] = digest.authenticate(request, probe_response.headers["www-authenticate"])
|
data/lib/httpx/plugins/expect.rb
CHANGED
@@ -5,7 +5,7 @@ module HTTPX
|
|
5
5
|
#
|
6
6
|
# This plugin makes all HTTP/1.1 requests with a body send the "Expect: 100-continue".
|
7
7
|
#
|
8
|
-
# https://gitlab.com/
|
8
|
+
# https://gitlab.com/os85/httpx/wikis/Expect#expect
|
9
9
|
#
|
10
10
|
module Expect
|
11
11
|
EXPECT_TIMEOUT = 2
|
@@ -50,7 +50,8 @@ module HTTPX
|
|
50
50
|
end
|
51
51
|
|
52
52
|
def response=(response)
|
53
|
-
if response
|
53
|
+
if response.is_a?(Response) &&
|
54
|
+
response.status == 100 &&
|
54
55
|
!@headers.key?("expect") &&
|
55
56
|
(@state == :body || @state == :done)
|
56
57
|
|
@@ -92,7 +93,7 @@ module HTTPX
|
|
92
93
|
response = @responses.delete(request)
|
93
94
|
return unless response
|
94
95
|
|
95
|
-
if response.status == 417 && request.headers.key?("expect")
|
96
|
+
if response.is_a?(Response) && response.status == 417 && request.headers.key?("expect")
|
96
97
|
response.close
|
97
98
|
request.headers.delete("expect")
|
98
99
|
request.transition(:idle)
|
@@ -11,7 +11,7 @@ module HTTPX
|
|
11
11
|
#
|
12
12
|
# It also doesn't follow insecure redirects (https -> http) by default (see *follow_insecure_redirects*).
|
13
13
|
#
|
14
|
-
# https://gitlab.com/
|
14
|
+
# https://gitlab.com/os85/httpx/wikis/Follow-Redirects
|
15
15
|
#
|
16
16
|
module FollowRedirects
|
17
17
|
MAX_REDIRECTS = 3
|
@@ -44,6 +44,7 @@ module HTTPX
|
|
44
44
|
|
45
45
|
max_redirects = redirect_request.max_redirects
|
46
46
|
|
47
|
+
return response unless response.is_a?(Response)
|
47
48
|
return response unless REDIRECT_STATUS.include?(response.status) && response.headers.key?("location")
|
48
49
|
return response unless max_redirects.positive?
|
49
50
|
|
@@ -121,6 +122,12 @@ module HTTPX
|
|
121
122
|
@redirect_request || self
|
122
123
|
end
|
123
124
|
|
125
|
+
def response
|
126
|
+
return super unless @redirect_request
|
127
|
+
|
128
|
+
@redirect_request.response
|
129
|
+
end
|
130
|
+
|
124
131
|
def max_redirects
|
125
132
|
@options.max_redirects || MAX_REDIRECTS
|
126
133
|
end
|
data/lib/httpx/plugins/grpc.rb
CHANGED
data/lib/httpx/plugins/h2c.rb
CHANGED
@@ -6,7 +6,7 @@ module HTTPX
|
|
6
6
|
# This plugin adds support for upgrading a plaintext HTTP/1.1 connection to HTTP/2
|
7
7
|
# (https://tools.ietf.org/html/rfc7540#section-3.2)
|
8
8
|
#
|
9
|
-
# https://gitlab.com/
|
9
|
+
# https://gitlab.com/os85/httpx/wikis/Upgrade#h2c
|
10
10
|
#
|
11
11
|
module H2C
|
12
12
|
VALID_H2C_VERBS = %i[get options head].freeze
|
@@ -5,10 +5,6 @@ require "delegate"
|
|
5
5
|
|
6
6
|
module HTTPX::Plugins
|
7
7
|
module Multipart
|
8
|
-
using HTTPX::RegexpExtensions unless Regexp.method_defined?(:match?)
|
9
|
-
|
10
|
-
CRLF = "\r\n"
|
11
|
-
|
12
8
|
class FilePart < SimpleDelegator
|
13
9
|
attr_reader :original_filename, :content_type
|
14
10
|
|
@@ -20,32 +16,14 @@ module HTTPX::Plugins
|
|
20
16
|
end
|
21
17
|
end
|
22
18
|
|
23
|
-
TOKEN = %r{[^\s()<>,;:\\"/\[\]?=]+}.freeze
|
24
|
-
VALUE = /"(?:\\"|[^"])*"|#{TOKEN}/.freeze
|
25
|
-
CONDISP = /Content-Disposition:\s*#{TOKEN}\s*/i.freeze
|
26
|
-
BROKEN_QUOTED = /^#{CONDISP}.*;\s*filename="(.*?)"(?:\s*$|\s*;\s*#{TOKEN}=)/i.freeze
|
27
|
-
BROKEN_UNQUOTED = /^#{CONDISP}.*;\s*filename=(#{TOKEN})/i.freeze
|
28
|
-
MULTIPART_CONTENT_TYPE = /Content-Type: (.*)#{CRLF}/ni.freeze
|
29
|
-
MULTIPART_CONTENT_DISPOSITION = /Content-Disposition:.*;\s*name=(#{VALUE})/ni.freeze
|
30
|
-
MULTIPART_CONTENT_ID = /Content-ID:\s*([^#{CRLF}]*)/ni.freeze
|
31
|
-
# Updated definitions from RFC 2231
|
32
|
-
ATTRIBUTE_CHAR = %r{[^ \t\v\n\r)(><@,;:\\"/\[\]?='*%]}.freeze
|
33
|
-
ATTRIBUTE = /#{ATTRIBUTE_CHAR}+/.freeze
|
34
|
-
SECTION = /\*[0-9]+/.freeze
|
35
|
-
REGULAR_PARAMETER_NAME = /#{ATTRIBUTE}#{SECTION}?/.freeze
|
36
|
-
REGULAR_PARAMETER = /(#{REGULAR_PARAMETER_NAME})=(#{VALUE})/.freeze
|
37
|
-
EXTENDED_OTHER_NAME = /#{ATTRIBUTE}\*[1-9][0-9]*\*/.freeze
|
38
|
-
EXTENDED_OTHER_VALUE = /%[0-9a-fA-F]{2}|#{ATTRIBUTE_CHAR}/.freeze
|
39
|
-
EXTENDED_OTHER_PARAMETER = /(#{EXTENDED_OTHER_NAME})=(#{EXTENDED_OTHER_VALUE}*)/.freeze
|
40
|
-
EXTENDED_INITIAL_NAME = /#{ATTRIBUTE}(?:\*0)?\*/.freeze
|
41
|
-
EXTENDED_INITIAL_VALUE = /[a-zA-Z0-9\-]*'[a-zA-Z0-9\-]*'#{EXTENDED_OTHER_VALUE}*/.freeze
|
42
|
-
EXTENDED_INITIAL_PARAMETER = /(#{EXTENDED_INITIAL_NAME})=(#{EXTENDED_INITIAL_VALUE})/.freeze
|
43
|
-
EXTENDED_PARAMETER = /#{EXTENDED_INITIAL_PARAMETER}|#{EXTENDED_OTHER_PARAMETER}/.freeze
|
44
|
-
DISPPARM = /;\s*(?:#{REGULAR_PARAMETER}|#{EXTENDED_PARAMETER})\s*/.freeze
|
45
|
-
RFC2183 = /^#{CONDISP}(#{DISPPARM})+$/i.freeze
|
46
|
-
|
47
19
|
class Decoder
|
20
|
+
include HTTPX::Utils
|
21
|
+
|
22
|
+
CRLF = "\r\n"
|
48
23
|
BOUNDARY_RE = /;\s*boundary=([^;]+)/i.freeze
|
24
|
+
MULTIPART_CONTENT_TYPE = /Content-Type: (.*)#{CRLF}/ni.freeze
|
25
|
+
MULTIPART_CONTENT_DISPOSITION = /Content-Disposition:.*;\s*name=(#{VALUE})/ni.freeze
|
26
|
+
MULTIPART_CONTENT_ID = /Content-ID:\s*([^#{CRLF}]*)/ni.freeze
|
49
27
|
WINDOW_SIZE = 2 << 14
|
50
28
|
|
51
29
|
def initialize(response)
|
@@ -102,7 +80,7 @@ module HTTPX::Plugins
|
|
102
80
|
name = head[MULTIPART_CONTENT_ID, 1]
|
103
81
|
end
|
104
82
|
|
105
|
-
filename = get_filename(head)
|
83
|
+
filename = HTTPX::Utils.get_filename(head)
|
106
84
|
|
107
85
|
name = filename || +"#{content_type || "text/plain"}[]" if name.nil? || name.empty?
|
108
86
|
|
@@ -154,34 +132,6 @@ module HTTPX::Plugins
|
|
154
132
|
raise Error, "parsing should have been over by now"
|
155
133
|
end until @buffer.empty?
|
156
134
|
end
|
157
|
-
|
158
|
-
def get_filename(head)
|
159
|
-
filename = nil
|
160
|
-
case head
|
161
|
-
when RFC2183
|
162
|
-
params = Hash[*head.scan(DISPPARM).flat_map(&:compact)]
|
163
|
-
|
164
|
-
if (filename = params["filename"])
|
165
|
-
filename = Regexp.last_match(1) if filename =~ /^"(.*)"$/
|
166
|
-
elsif (filename = params["filename*"])
|
167
|
-
encoding, _, filename = filename.split("'", 3)
|
168
|
-
end
|
169
|
-
when BROKEN_QUOTED, BROKEN_UNQUOTED
|
170
|
-
filename = Regexp.last_match(1)
|
171
|
-
end
|
172
|
-
|
173
|
-
return unless filename
|
174
|
-
|
175
|
-
filename = URI::DEFAULT_PARSER.unescape(filename) if filename.scan(/%.?.?/).all? { |s| /%[0-9a-fA-F]{2}/.match?(s) }
|
176
|
-
|
177
|
-
filename.scrub!
|
178
|
-
|
179
|
-
filename = filename.gsub(/\\(.)/, '\1') unless /\\[^\\"]/.match?(filename)
|
180
|
-
|
181
|
-
filename.force_encoding ::Encoding.find(encoding) if encoding
|
182
|
-
|
183
|
-
filename
|
184
|
-
end
|
185
135
|
end
|
186
136
|
end
|
187
137
|
end
|
@@ -7,7 +7,7 @@ module HTTPX
|
|
7
7
|
#
|
8
8
|
# HTTPX.post(URL, form: form: { image: HTTP::FormData::File.new("path/to/file")})
|
9
9
|
#
|
10
|
-
# https://gitlab.com/
|
10
|
+
# https://gitlab.com/os85/httpx/wikis/Multipart-Uploads
|
11
11
|
#
|
12
12
|
module Multipart
|
13
13
|
MULTIPART_VALUE_COND = lambda do |value|
|
@@ -29,7 +29,7 @@ module HTTPX
|
|
29
29
|
# in order not to break legacy code, we'll keep loading http/form_data for them.
|
30
30
|
require "http/form_data"
|
31
31
|
warn "httpx: http/form_data is no longer a requirement to use HTTPX :multipart plugin. See migration instructions under" \
|
32
|
-
"https://
|
32
|
+
"https://os85.gitlab.io/httpx/wiki/Multipart-Uploads.html#notes. \n\n" \
|
33
33
|
"If you'd like to stop seeing this message, require 'http/form_data' yourself."
|
34
34
|
end
|
35
35
|
rescue LoadError
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module HTTPX
|
4
4
|
module Plugins
|
5
5
|
#
|
6
|
-
# https://gitlab.com/
|
6
|
+
# https://gitlab.com/os85/httpx/wikis/Authentication#ntlm-authentication
|
7
7
|
#
|
8
8
|
module NTLMAuth
|
9
9
|
class << self
|
@@ -39,6 +39,8 @@ module HTTPX
|
|
39
39
|
request.headers["authorization"] = ntlm.negotiate
|
40
40
|
probe_response = wrap { super(request).first }
|
41
41
|
|
42
|
+
return probe_response unless probe_response.is_a?(Response)
|
43
|
+
|
42
44
|
if probe_response.status == 401 && ntlm.can_authenticate?(probe_response.headers["www-authenticate"])
|
43
45
|
request.transition(:idle)
|
44
46
|
request.headers["authorization"] = ntlm.authenticate(request, probe_response.headers["www-authenticate"])
|
@@ -15,7 +15,7 @@ module HTTPX
|
|
15
15
|
# This plugin is also not recommendable when connecting to >9000 (like, a lot) different origins.
|
16
16
|
# So when you use this, make sure that you don't fall into this trap.
|
17
17
|
#
|
18
|
-
# https://gitlab.com/
|
18
|
+
# https://gitlab.com/os85/httpx/wikis/Persistent
|
19
19
|
#
|
20
20
|
module Persistent
|
21
21
|
def self.load_dependencies(klass)
|
@@ -23,6 +23,7 @@ module HTTPX
|
|
23
23
|
response = super
|
24
24
|
|
25
25
|
if response &&
|
26
|
+
response.is_a?(Response) &&
|
26
27
|
response.status == 407 &&
|
27
28
|
!request.headers.key?("proxy-authorization") &&
|
28
29
|
response.headers.key?("proxy-authenticate")
|
@@ -113,13 +114,14 @@ module HTTPX
|
|
113
114
|
|
114
115
|
def __http_on_connect(request, response)
|
115
116
|
@inflight -= 1
|
116
|
-
if response.status == 200
|
117
|
+
if response.is_a?(Response) && response.status == 200
|
117
118
|
req = @pending.first
|
118
119
|
request_uri = req.uri
|
119
120
|
@io = ProxySSL.new(@io, request_uri, @options)
|
120
121
|
transition(:connected)
|
121
122
|
throw(:called)
|
122
|
-
elsif response.
|
123
|
+
elsif response.is_a?(Response) &&
|
124
|
+
response.status == 407 &&
|
123
125
|
!request.headers.key?("proxy-authorization") &&
|
124
126
|
@options.proxy.can_authenticate?(response.headers["proxy-authenticate"])
|
125
127
|
|
data/lib/httpx/plugins/proxy.rb
CHANGED
@@ -8,7 +8,7 @@ module HTTPX
|
|
8
8
|
# In order to benefit from this, requests are sent one at a time, so that
|
9
9
|
# no push responses are received after corresponding request has been sent.
|
10
10
|
#
|
11
|
-
# https://gitlab.com/
|
11
|
+
# https://gitlab.com/os85/httpx/wikis/Server-Push
|
12
12
|
#
|
13
13
|
module PushPromise
|
14
14
|
def self.extra_options(options)
|