httpx 0.21.0 → 0.22.4
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/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)
|