httpx 0.11.3 → 0.14.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/README.md +2 -2
- data/doc/release_notes/0_10_1.md +1 -1
- data/doc/release_notes/0_11_1.md +5 -1
- data/doc/release_notes/0_12_0.md +55 -0
- data/doc/release_notes/0_13_0.md +58 -0
- data/doc/release_notes/0_13_1.md +5 -0
- data/doc/release_notes/0_13_2.md +9 -0
- data/doc/release_notes/0_14_0.md +79 -0
- data/lib/httpx.rb +3 -3
- data/lib/httpx/adapters/faraday.rb +4 -6
- data/lib/httpx/altsvc.rb +1 -0
- data/lib/httpx/callbacks.rb +12 -3
- data/lib/httpx/chainable.rb +2 -2
- data/lib/httpx/connection.rb +92 -37
- data/lib/httpx/connection/http1.rb +37 -19
- data/lib/httpx/connection/http2.rb +82 -31
- data/lib/httpx/headers.rb +1 -1
- data/lib/httpx/io.rb +16 -3
- data/lib/httpx/io/ssl.rb +35 -24
- data/lib/httpx/io/tcp.rb +50 -28
- data/lib/httpx/io/tls.rb +218 -0
- data/lib/httpx/io/tls/box.rb +365 -0
- data/lib/httpx/io/tls/context.rb +199 -0
- data/lib/httpx/io/tls/ffi.rb +390 -0
- data/lib/httpx/io/udp.rb +31 -7
- data/lib/httpx/io/unix.rb +27 -12
- data/lib/httpx/options.rb +97 -74
- data/lib/httpx/parser/http1.rb +4 -4
- data/lib/httpx/plugins/aws_sdk_authentication.rb +84 -0
- data/lib/httpx/plugins/aws_sigv4.rb +219 -0
- data/lib/httpx/plugins/basic_authentication.rb +8 -3
- data/lib/httpx/plugins/compression.rb +24 -12
- data/lib/httpx/plugins/compression/brotli.rb +10 -7
- data/lib/httpx/plugins/compression/deflate.rb +8 -10
- data/lib/httpx/plugins/compression/gzip.rb +4 -3
- data/lib/httpx/plugins/cookies.rb +3 -7
- data/lib/httpx/plugins/digest_authentication.rb +5 -5
- data/lib/httpx/plugins/expect.rb +6 -6
- data/lib/httpx/plugins/follow_redirects.rb +4 -4
- data/lib/httpx/plugins/grpc.rb +247 -0
- data/lib/httpx/plugins/grpc/call.rb +62 -0
- data/lib/httpx/plugins/grpc/message.rb +85 -0
- data/lib/httpx/plugins/h2c.rb +43 -58
- data/lib/httpx/plugins/internal_telemetry.rb +93 -0
- data/lib/httpx/plugins/multipart.rb +2 -0
- data/lib/httpx/plugins/multipart/encoder.rb +4 -9
- data/lib/httpx/plugins/multipart/part.rb +1 -1
- data/lib/httpx/plugins/proxy.rb +4 -8
- data/lib/httpx/plugins/proxy/http.rb +1 -1
- data/lib/httpx/plugins/proxy/socks4.rb +8 -0
- data/lib/httpx/plugins/proxy/socks5.rb +8 -0
- data/lib/httpx/plugins/proxy/ssh.rb +3 -3
- data/lib/httpx/plugins/push_promise.rb +3 -2
- data/lib/httpx/plugins/rate_limiter.rb +1 -1
- data/lib/httpx/plugins/retries.rb +15 -16
- data/lib/httpx/plugins/stream.rb +99 -77
- data/lib/httpx/plugins/upgrade.rb +84 -0
- data/lib/httpx/plugins/upgrade/h2.rb +54 -0
- data/lib/httpx/pool.rb +14 -6
- data/lib/httpx/registry.rb +1 -7
- data/lib/httpx/request.rb +36 -3
- data/lib/httpx/resolver/https.rb +3 -11
- data/lib/httpx/resolver/native.rb +7 -3
- data/lib/httpx/response.rb +18 -7
- data/lib/httpx/selector.rb +5 -0
- data/lib/httpx/session.rb +41 -8
- data/lib/httpx/transcoder/body.rb +3 -5
- data/lib/httpx/transcoder/chunker.rb +1 -1
- data/lib/httpx/version.rb +1 -1
- data/sig/callbacks.rbs +2 -0
- data/sig/chainable.rbs +2 -1
- data/sig/connection/http1.rbs +7 -2
- data/sig/connection/http2.rbs +10 -4
- data/sig/options.rbs +16 -22
- data/sig/plugins/aws_sdk_authentication.rbs +19 -0
- data/sig/plugins/aws_sigv4.rbs +64 -0
- data/sig/plugins/basic_authentication.rbs +2 -0
- data/sig/plugins/compression.rbs +7 -5
- data/sig/plugins/compression/brotli.rbs +1 -1
- data/sig/plugins/compression/deflate.rbs +1 -1
- data/sig/plugins/compression/gzip.rbs +1 -1
- data/sig/plugins/cookies.rbs +0 -1
- data/sig/plugins/digest_authentication.rbs +0 -1
- data/sig/plugins/expect.rbs +0 -2
- data/sig/plugins/follow_redirects.rbs +0 -2
- data/sig/plugins/h2c.rbs +5 -10
- data/sig/plugins/persistent.rbs +0 -1
- data/sig/plugins/proxy.rbs +0 -1
- data/sig/plugins/push_promise.rbs +1 -1
- data/sig/plugins/retries.rbs +0 -4
- data/sig/plugins/stream.rbs +17 -16
- data/sig/plugins/upgrade.rbs +23 -0
- data/sig/request.rbs +7 -2
- data/sig/response.rbs +4 -1
- data/sig/session.rbs +4 -0
- metadata +56 -33
- data/lib/httpx/timeout.rb +0 -67
- data/sig/timeout.rbs +0 -29
@@ -0,0 +1,219 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "set"
|
4
|
+
require "aws-sdk-s3"
|
5
|
+
|
6
|
+
module HTTPX
|
7
|
+
module Plugins
|
8
|
+
#
|
9
|
+
# This plugin adds AWS Sigv4 authentication.
|
10
|
+
#
|
11
|
+
# https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html
|
12
|
+
#
|
13
|
+
# https://gitlab.com/honeyryderchuck/httpx/wikis/AWS-SigV4
|
14
|
+
#
|
15
|
+
module AWSSigV4
|
16
|
+
Credentials = Struct.new(:username, :password, :security_token)
|
17
|
+
|
18
|
+
class Signer
|
19
|
+
def initialize(
|
20
|
+
service:,
|
21
|
+
region:,
|
22
|
+
credentials: nil,
|
23
|
+
username: nil,
|
24
|
+
password: nil,
|
25
|
+
security_token: nil,
|
26
|
+
provider_prefix: "aws",
|
27
|
+
header_provider_field: "amz",
|
28
|
+
unsigned_headers: [],
|
29
|
+
apply_checksum_header: true,
|
30
|
+
algorithm: "SHA256"
|
31
|
+
)
|
32
|
+
@credentials = credentials || Credentials.new(username, password, security_token)
|
33
|
+
@service = service
|
34
|
+
@region = region
|
35
|
+
|
36
|
+
@unsigned_headers = Set.new(unsigned_headers.map(&:downcase))
|
37
|
+
@unsigned_headers << "authorization"
|
38
|
+
@unsigned_headers << "x-amzn-trace-id"
|
39
|
+
@unsigned_headers << "expect"
|
40
|
+
|
41
|
+
@apply_checksum_header = apply_checksum_header
|
42
|
+
@provider_prefix = provider_prefix
|
43
|
+
@header_provider_field = header_provider_field
|
44
|
+
|
45
|
+
@algorithm = algorithm
|
46
|
+
end
|
47
|
+
|
48
|
+
def sign!(request)
|
49
|
+
lower_provider_prefix = "#{@provider_prefix}4"
|
50
|
+
upper_provider_prefix = lower_provider_prefix.upcase
|
51
|
+
|
52
|
+
downcased_algorithm = @algorithm.downcase
|
53
|
+
|
54
|
+
datetime = (request.headers["x-#{@header_provider_field}-date"] ||= Time.now.utc.strftime("%Y%m%dT%H%M%SZ"))
|
55
|
+
date = datetime[0, 8]
|
56
|
+
|
57
|
+
content_hashed = request.headers["x-#{@header_provider_field}-content-#{downcased_algorithm}"] || hexdigest(request.body)
|
58
|
+
|
59
|
+
request.headers["x-#{@header_provider_field}-content-#{downcased_algorithm}"] ||= content_hashed if @apply_checksum_header
|
60
|
+
request.headers["x-#{@header_provider_field}-security-token"] ||= @credentials.security_token if @credentials.security_token
|
61
|
+
|
62
|
+
signature_headers = request.headers.each.reject do |k, _|
|
63
|
+
@unsigned_headers.include?(k)
|
64
|
+
end
|
65
|
+
# aws sigv4 needs to declare the host, regardless of protocol version
|
66
|
+
signature_headers << ["host", request.authority] unless request.headers.key?("host")
|
67
|
+
signature_headers.sort_by!(&:first)
|
68
|
+
|
69
|
+
signed_headers = signature_headers.map(&:first).join(";")
|
70
|
+
|
71
|
+
canonical_headers = signature_headers.map do |k, v|
|
72
|
+
# eliminate whitespace between value fields, unless it's a quoted value
|
73
|
+
"#{k}:#{v.start_with?("\"") && v.end_with?("\"") ? v : v.gsub(/\s+/, " ").strip}\n"
|
74
|
+
end.join
|
75
|
+
|
76
|
+
# canonical request
|
77
|
+
creq = "#{request.verb.to_s.upcase}" \
|
78
|
+
"\n#{request.canonical_path}" \
|
79
|
+
"\n#{request.canonical_query}" \
|
80
|
+
"\n#{canonical_headers}" \
|
81
|
+
"\n#{signed_headers}" \
|
82
|
+
"\n#{content_hashed}"
|
83
|
+
|
84
|
+
credential_scope = "#{date}" \
|
85
|
+
"/#{@region}" \
|
86
|
+
"/#{@service}" \
|
87
|
+
"/#{lower_provider_prefix}_request"
|
88
|
+
|
89
|
+
algo_line = "#{upper_provider_prefix}-HMAC-#{@algorithm}"
|
90
|
+
# string to sign
|
91
|
+
sts = "#{algo_line}" \
|
92
|
+
"\n#{datetime}" \
|
93
|
+
"\n#{credential_scope}" \
|
94
|
+
"\n#{hexdigest(creq)}"
|
95
|
+
|
96
|
+
# signature
|
97
|
+
k_date = hmac("#{upper_provider_prefix}#{@credentials.password}", date)
|
98
|
+
k_region = hmac(k_date, @region)
|
99
|
+
k_service = hmac(k_region, @service)
|
100
|
+
k_credentials = hmac(k_service, "#{lower_provider_prefix}_request")
|
101
|
+
sig = hexhmac(k_credentials, sts)
|
102
|
+
|
103
|
+
credential = "#{@credentials.username}/#{credential_scope}"
|
104
|
+
# apply signature
|
105
|
+
request.headers["authorization"] =
|
106
|
+
"#{algo_line} " \
|
107
|
+
"Credential=#{credential}, " \
|
108
|
+
"SignedHeaders=#{signed_headers}, " \
|
109
|
+
"Signature=#{sig}"
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def hexdigest(value)
|
115
|
+
if value.respond_to?(:to_path)
|
116
|
+
# files, pathnames
|
117
|
+
OpenSSL::Digest.new(@algorithm).file(value.to_path).hexdigest
|
118
|
+
elsif value.respond_to?(:each)
|
119
|
+
digest = OpenSSL::Digest.new(@algorithm)
|
120
|
+
|
121
|
+
mb_buffer = value.each.each_with_object("".b) do |chunk, buffer|
|
122
|
+
buffer << chunk
|
123
|
+
break if buffer.bytesize >= 1024 * 1024
|
124
|
+
end
|
125
|
+
|
126
|
+
digest.update(mb_buffer)
|
127
|
+
value.rewind
|
128
|
+
digest.hexdigest
|
129
|
+
else
|
130
|
+
OpenSSL::Digest.new(@algorithm).hexdigest(value)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def hmac(key, value)
|
135
|
+
OpenSSL::HMAC.digest(OpenSSL::Digest.new(@algorithm), key, value)
|
136
|
+
end
|
137
|
+
|
138
|
+
def hexhmac(key, value)
|
139
|
+
OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new(@algorithm), key, value)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
class << self
|
144
|
+
def extra_options(options)
|
145
|
+
Class.new(options.class) do
|
146
|
+
def_option(:sigv4_signer, <<-OUT)
|
147
|
+
value.is_a?(#{Signer}) ? value : #{Signer}.new(value)
|
148
|
+
OUT
|
149
|
+
end.new(options)
|
150
|
+
end
|
151
|
+
|
152
|
+
def load_dependencies(klass)
|
153
|
+
require "digest/sha2"
|
154
|
+
require "openssl"
|
155
|
+
klass.plugin(:expect)
|
156
|
+
klass.plugin(:compression)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
module InstanceMethods
|
161
|
+
def aws_sigv4_authentication(**options)
|
162
|
+
with(sigv4_signer: Signer.new(**options))
|
163
|
+
end
|
164
|
+
|
165
|
+
def build_request(*, _)
|
166
|
+
request = super
|
167
|
+
|
168
|
+
return request if request.headers.key?("authorization")
|
169
|
+
|
170
|
+
signer = request.options.sigv4_signer
|
171
|
+
|
172
|
+
return request unless signer
|
173
|
+
|
174
|
+
signer.sign!(request)
|
175
|
+
|
176
|
+
request
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
module RequestMethods
|
181
|
+
def canonical_path
|
182
|
+
path = uri.path.dup
|
183
|
+
path << "/" if path.empty?
|
184
|
+
path.gsub(%r{[^/]+}) { |part| CGI.escape(part.encode("UTF-8")).gsub("+", "%20").gsub("%7E", "~") }
|
185
|
+
end
|
186
|
+
|
187
|
+
def canonical_query
|
188
|
+
params = query.split("&")
|
189
|
+
# params = params.map { |p| p.match(/=/) ? p : p + '=' }
|
190
|
+
# From: https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
|
191
|
+
# Sort the parameter names by character code point in ascending order.
|
192
|
+
# Parameters with duplicate names should be sorted by value.
|
193
|
+
#
|
194
|
+
# Default sort <=> in JRuby will swap members
|
195
|
+
# occasionally when <=> is 0 (considered still sorted), but this
|
196
|
+
# causes our normalized query string to not match the sent querystring.
|
197
|
+
# When names match, we then sort by their values. When values also
|
198
|
+
# match then we sort by their original order
|
199
|
+
params.each.with_index.sort do |a, b|
|
200
|
+
a, a_offset = a
|
201
|
+
b, b_offset = b
|
202
|
+
a_name, a_value = a.split("=")
|
203
|
+
b_name, b_value = b.split("=")
|
204
|
+
if a_name == b_name
|
205
|
+
if a_value == b_value
|
206
|
+
a_offset <=> b_offset
|
207
|
+
else
|
208
|
+
a_value <=> b_value
|
209
|
+
end
|
210
|
+
else
|
211
|
+
a_name <=> b_name
|
212
|
+
end
|
213
|
+
end.map(&:first).join("&")
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
register_plugin :aws_sigv4, AWSSigV4
|
218
|
+
end
|
219
|
+
end
|
@@ -8,9 +8,14 @@ module HTTPX
|
|
8
8
|
# https://gitlab.com/honeyryderchuck/httpx/wikis/Authentication#basic-authentication
|
9
9
|
#
|
10
10
|
module BasicAuthentication
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
class << self
|
12
|
+
def load_dependencies(_klass)
|
13
|
+
require "base64"
|
14
|
+
end
|
15
|
+
|
16
|
+
def configure(klass)
|
17
|
+
klass.plugin(:authentication)
|
18
|
+
end
|
14
19
|
end
|
15
20
|
|
16
21
|
module InstanceMethods
|
@@ -13,23 +13,31 @@ module HTTPX
|
|
13
13
|
# https://gitlab.com/honeyryderchuck/httpx/wikis/Compression
|
14
14
|
#
|
15
15
|
module Compression
|
16
|
-
extend Registry
|
17
|
-
|
18
16
|
class << self
|
19
|
-
def
|
17
|
+
def configure(klass)
|
20
18
|
klass.plugin(:"compression/gzip")
|
21
19
|
klass.plugin(:"compression/deflate")
|
22
20
|
end
|
23
21
|
|
24
22
|
def extra_options(options)
|
23
|
+
encodings = Module.new do
|
24
|
+
extend Registry
|
25
|
+
end
|
26
|
+
|
25
27
|
Class.new(options.class) do
|
26
|
-
def_option(:compression_threshold_size)
|
27
|
-
bytes = Integer(
|
28
|
+
def_option(:compression_threshold_size, <<-OUT)
|
29
|
+
bytes = Integer(value)
|
28
30
|
raise Error, ":expect_threshold_size must be positive" unless bytes.positive?
|
29
31
|
|
30
32
|
bytes
|
31
|
-
|
32
|
-
|
33
|
+
OUT
|
34
|
+
|
35
|
+
def_option(:encodings, <<-OUT)
|
36
|
+
raise Error, ":encodings must be a registry" unless value.respond_to?(:registry)
|
37
|
+
|
38
|
+
value
|
39
|
+
OUT
|
40
|
+
end.new(options).merge(encodings: encodings)
|
33
41
|
end
|
34
42
|
end
|
35
43
|
|
@@ -37,7 +45,11 @@ module HTTPX
|
|
37
45
|
def initialize(*)
|
38
46
|
super
|
39
47
|
# forego compression in the Range cases
|
40
|
-
|
48
|
+
if @headers.key?("range")
|
49
|
+
@headers.delete("accept-encoding")
|
50
|
+
else
|
51
|
+
@headers["accept-encoding"] ||= @options.encodings.registry.keys
|
52
|
+
end
|
41
53
|
end
|
42
54
|
end
|
43
55
|
|
@@ -52,16 +64,16 @@ module HTTPX
|
|
52
64
|
@headers.get("content-encoding").each do |encoding|
|
53
65
|
next if encoding == "identity"
|
54
66
|
|
55
|
-
@body = Encoder.new(@body,
|
67
|
+
@body = Encoder.new(@body, options.encodings.registry(encoding).deflater)
|
56
68
|
end
|
57
|
-
@headers["content-length"] = @body.bytesize unless
|
69
|
+
@headers["content-length"] = @body.bytesize unless unbounded_body?
|
58
70
|
end
|
59
71
|
end
|
60
72
|
|
61
73
|
module ResponseBodyMethods
|
62
74
|
attr_reader :encodings
|
63
75
|
|
64
|
-
def initialize(
|
76
|
+
def initialize(*)
|
65
77
|
@encodings = []
|
66
78
|
|
67
79
|
super
|
@@ -80,7 +92,7 @@ module HTTPX
|
|
80
92
|
@_inflaters = @headers.get("content-encoding").map do |encoding|
|
81
93
|
next if encoding == "identity"
|
82
94
|
|
83
|
-
inflater =
|
95
|
+
inflater = @options.encodings.registry(encoding).inflater(compressed_length)
|
84
96
|
# do not uncompress if there is no decoder available. In fact, we can't reliably
|
85
97
|
# continue decompressing beyond that, so ignore.
|
86
98
|
break unless inflater
|
@@ -4,24 +4,27 @@ module HTTPX
|
|
4
4
|
module Plugins
|
5
5
|
module Compression
|
6
6
|
module Brotli
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
class << self
|
8
|
+
def load_dependencies(_klass)
|
9
|
+
require "brotli"
|
10
|
+
end
|
11
11
|
|
12
|
-
|
13
|
-
|
12
|
+
def configure(klass)
|
13
|
+
klass.plugin(:compression)
|
14
|
+
klass.default_options.encodings.register "br", self
|
15
|
+
end
|
14
16
|
end
|
15
17
|
|
16
18
|
module Deflater
|
17
19
|
module_function
|
18
20
|
|
19
|
-
def deflate(raw, buffer, chunk_size:)
|
21
|
+
def deflate(raw, buffer = "".b, chunk_size: 16_384)
|
20
22
|
while (chunk = raw.read(chunk_size))
|
21
23
|
compressed = ::Brotli.deflate(chunk)
|
22
24
|
buffer << compressed
|
23
25
|
yield compressed if block_given?
|
24
26
|
end
|
27
|
+
buffer
|
25
28
|
end
|
26
29
|
end
|
27
30
|
|
@@ -4,24 +4,21 @@ module HTTPX
|
|
4
4
|
module Plugins
|
5
5
|
module Compression
|
6
6
|
module Deflate
|
7
|
-
def self.load_dependencies(
|
7
|
+
def self.load_dependencies(_klass)
|
8
8
|
require "stringio"
|
9
9
|
require "zlib"
|
10
|
-
klass.plugin(:"compression/gzip")
|
11
10
|
end
|
12
11
|
|
13
|
-
def self.configure(
|
14
|
-
|
12
|
+
def self.configure(klass)
|
13
|
+
klass.plugin(:"compression/gzip")
|
14
|
+
klass.default_options.encodings.register "deflate", self
|
15
15
|
end
|
16
16
|
|
17
17
|
module Deflater
|
18
18
|
module_function
|
19
19
|
|
20
|
-
def deflate(raw, buffer, chunk_size:)
|
21
|
-
deflater = Zlib::Deflate.new
|
22
|
-
Zlib::MAX_WBITS,
|
23
|
-
Zlib::MAX_MEM_LEVEL,
|
24
|
-
Zlib::HUFFMAN_ONLY)
|
20
|
+
def deflate(raw, buffer = "".b, chunk_size: 16_384)
|
21
|
+
deflater = Zlib::Deflate.new
|
25
22
|
while (chunk = raw.read(chunk_size))
|
26
23
|
compressed = deflater.deflate(chunk)
|
27
24
|
buffer << compressed
|
@@ -30,8 +27,9 @@ module HTTPX
|
|
30
27
|
last = deflater.finish
|
31
28
|
buffer << last
|
32
29
|
yield last if block_given?
|
30
|
+
buffer
|
33
31
|
ensure
|
34
|
-
deflater.close
|
32
|
+
deflater.close if deflater
|
35
33
|
end
|
36
34
|
end
|
37
35
|
|
@@ -10,8 +10,8 @@ module HTTPX
|
|
10
10
|
require "zlib"
|
11
11
|
end
|
12
12
|
|
13
|
-
def self.configure(
|
14
|
-
|
13
|
+
def self.configure(klass)
|
14
|
+
klass.default_options.encodings.register "gzip", self
|
15
15
|
end
|
16
16
|
|
17
17
|
class Deflater
|
@@ -19,7 +19,7 @@ module HTTPX
|
|
19
19
|
@compressed_chunk = "".b
|
20
20
|
end
|
21
21
|
|
22
|
-
def deflate(raw, buffer, chunk_size:)
|
22
|
+
def deflate(raw, buffer = "".b, chunk_size: 16_384)
|
23
23
|
gzip = Zlib::GzipWriter.new(self)
|
24
24
|
|
25
25
|
begin
|
@@ -38,6 +38,7 @@ module HTTPX
|
|
38
38
|
|
39
39
|
buffer << compressed
|
40
40
|
yield compressed if block_given?
|
41
|
+
buffer
|
41
42
|
end
|
42
43
|
|
43
44
|
private
|
@@ -20,13 +20,9 @@ module HTTPX
|
|
20
20
|
|
21
21
|
def self.extra_options(options)
|
22
22
|
Class.new(options.class) do
|
23
|
-
def_option(:cookies)
|
24
|
-
|
25
|
-
|
26
|
-
else
|
27
|
-
Jar.new(cookies)
|
28
|
-
end
|
29
|
-
end
|
23
|
+
def_option(:cookies, <<-OUT)
|
24
|
+
value.is_a?(#{Jar}) ? value : #{Jar}.new(value)
|
25
|
+
OUT
|
30
26
|
end.new(options)
|
31
27
|
end
|
32
28
|
|
@@ -14,11 +14,11 @@ module HTTPX
|
|
14
14
|
|
15
15
|
def self.extra_options(options)
|
16
16
|
Class.new(options.class) do
|
17
|
-
def_option(:digest)
|
18
|
-
raise Error, ":digest must be a Digest" unless
|
17
|
+
def_option(:digest, <<-OUT)
|
18
|
+
raise Error, ":digest must be a Digest" unless value.is_a?(#{Digest})
|
19
19
|
|
20
|
-
|
21
|
-
|
20
|
+
value
|
21
|
+
OUT
|
22
22
|
end.new(options)
|
23
23
|
end
|
24
24
|
|
@@ -29,7 +29,7 @@ module HTTPX
|
|
29
29
|
|
30
30
|
module InstanceMethods
|
31
31
|
def digest_authentication(user, password)
|
32
|
-
|
32
|
+
with(digest: Digest.new(user, password))
|
33
33
|
end
|
34
34
|
|
35
35
|
alias_method :digest_auth, :digest_authentication
|