wreq 1.2.4 → 1.2.6
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/Cargo.lock +22 -19
- data/Cargo.toml +15 -2
- data/crates/wreq-util/.github/FUNDING.yml +15 -0
- data/crates/wreq-util/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
- data/crates/wreq-util/.github/ISSUE_TEMPLATE/custom.md +10 -0
- data/crates/wreq-util/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- data/crates/wreq-util/.github/dependabot.yml +22 -0
- data/crates/wreq-util/.github/workflows/ci.yml +82 -0
- data/crates/wreq-util/.github/workflows/release-plz.yml +52 -0
- data/crates/wreq-util/.gitignore +24 -0
- data/crates/wreq-util/CHANGELOG.md +74 -0
- data/crates/wreq-util/Cargo.toml +94 -0
- data/crates/wreq-util/LICENSE +201 -0
- data/crates/wreq-util/README.md +62 -0
- data/crates/wreq-util/examples/emulate.rs +39 -0
- data/crates/wreq-util/examples/tower_delay.rs +191 -0
- data/crates/wreq-util/release-plz.toml +2 -0
- data/crates/wreq-util/rustfmt.toml +5 -0
- data/crates/wreq-util/src/emulate/compress.rs +85 -0
- data/crates/wreq-util/src/emulate/macros.rs +372 -0
- data/crates/wreq-util/src/emulate/profile/chrome/header.rs +74 -0
- data/crates/wreq-util/src/emulate/profile/chrome/http2.rs +65 -0
- data/crates/wreq-util/src/emulate/profile/chrome/tls.rs +153 -0
- data/crates/wreq-util/src/emulate/profile/chrome.rs +2028 -0
- data/crates/wreq-util/src/emulate/profile/firefox/header.rs +37 -0
- data/crates/wreq-util/src/emulate/profile/firefox/http2.rs +113 -0
- data/crates/wreq-util/src/emulate/profile/firefox/tls.rs +253 -0
- data/crates/wreq-util/src/emulate/profile/firefox.rs +518 -0
- data/crates/wreq-util/src/emulate/profile/okhttp.rs +241 -0
- data/crates/wreq-util/src/emulate/profile/opera/header.rs +29 -0
- data/crates/wreq-util/src/emulate/profile/opera/http2.rs +50 -0
- data/crates/wreq-util/src/emulate/profile/opera/tls.rs +97 -0
- data/crates/wreq-util/src/emulate/profile/opera.rs +299 -0
- data/crates/wreq-util/src/emulate/profile/safari/header.rs +59 -0
- data/crates/wreq-util/src/emulate/profile/safari/http2.rs +116 -0
- data/crates/wreq-util/src/emulate/profile/safari/tls.rs +177 -0
- data/crates/wreq-util/src/emulate/profile/safari.rs +222 -0
- data/crates/wreq-util/src/emulate/profile.rs +47 -0
- data/crates/wreq-util/src/emulate.rs +369 -0
- data/crates/wreq-util/src/lib.rs +14 -0
- data/crates/wreq-util/src/rand.rs +24 -0
- data/crates/wreq-util/src/tower/delay/future.rs +43 -0
- data/crates/wreq-util/src/tower/delay/layer.rs +201 -0
- data/crates/wreq-util/src/tower/delay/service.rs +190 -0
- data/crates/wreq-util/src/tower/delay.rs +98 -0
- data/crates/wreq-util/src/tower.rs +7 -0
- data/crates/wreq-util/tests/client.rs +153 -0
- data/crates/wreq-util/tests/emulate_chrome.rs +270 -0
- data/crates/wreq-util/tests/emulate_firefox.rs +92 -0
- data/crates/wreq-util/tests/emulate_okhttp.rs +46 -0
- data/crates/wreq-util/tests/emulate_opera.rs +113 -0
- data/crates/wreq-util/tests/emulate_safari.rs +151 -0
- data/crates/wreq-util/tests/support/mod.rs +55 -0
- data/crates/wreq-util/tests/support/server.rs +232 -0
- data/examples/emulate_request.rb +1 -1
- data/lib/wreq.rb +72 -45
- data/lib/wreq_ruby/body.rb +30 -9
- data/lib/wreq_ruby/client.rb +88 -55
- data/lib/wreq_ruby/cookie.rb +81 -21
- data/lib/wreq_ruby/emulate.rb +77 -7
- data/lib/wreq_ruby/error.rb +5 -7
- data/lib/wreq_ruby/header.rb +205 -114
- data/lib/wreq_ruby/http.rb +74 -0
- data/lib/wreq_ruby/response.rb +31 -3
- data/script/build_windows_gnu.ps1 +6 -0
- data/src/arch.rs +22 -0
- data/src/client/body/form.rs +2 -0
- data/src/client/body/json.rs +47 -14
- data/src/client/body/stream.rs +147 -43
- data/src/client/body.rs +11 -15
- data/src/client/param.rs +7 -7
- data/src/client/req.rs +87 -47
- data/src/client/resp.rs +23 -24
- data/src/client.rs +310 -230
- data/src/cookie.rs +280 -87
- data/src/emulate.rs +86 -50
- data/src/error.rs +198 -45
- data/src/extractor.rs +16 -76
- data/src/header.rs +318 -130
- data/src/http.rs +62 -33
- data/src/lib.rs +26 -20
- data/src/macros.rs +71 -46
- data/src/options.rs +284 -0
- data/src/rt.rs +22 -11
- data/src/serde/de/array_deserializer.rs +48 -0
- data/src/serde/de/array_enumerator.rs +59 -0
- data/src/serde/de/deserializer.rs +307 -0
- data/src/serde/de/enum_deserializer.rs +47 -0
- data/src/serde/de/hash_deserializer.rs +89 -0
- data/src/serde/de/number_deserializer.rs +51 -0
- data/src/serde/de/variant_deserializer.rs +101 -0
- data/src/serde/de.rs +33 -0
- data/src/serde/error.rs +146 -0
- data/src/serde/ser/enums.rs +14 -0
- data/src/serde/ser/map_serializer.rs +56 -0
- data/src/serde/ser/seq_serializer.rs +71 -0
- data/src/serde/ser/serializer.rs +194 -0
- data/src/serde/ser/struct_serializer.rs +106 -0
- data/src/serde/ser/struct_variant_serializer.rs +44 -0
- data/src/serde/ser/tuple_variant_serializer.rs +41 -0
- data/src/serde/ser.rs +18 -0
- data/src/serde/tests.rs +237 -0
- data/src/serde.rs +202 -0
- data/test/body_sender_test.rb +246 -0
- data/test/cookie_test.rb +211 -10
- data/test/header_test.rb +228 -192
- data/test/json_precision_test.rb +209 -0
- data/test/option_validation_test.rb +311 -0
- data/test/stream_test.rb +36 -0
- data/test/value_semantics_test.rb +238 -0
- metadata +77 -1
data/test/cookie_test.rb
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "test_helper"
|
|
4
|
+
require "bigdecimal"
|
|
5
|
+
require "open3"
|
|
6
|
+
require "rbconfig"
|
|
4
7
|
|
|
5
8
|
class CookieTest < Minitest::Test
|
|
6
9
|
def setup
|
|
@@ -41,6 +44,14 @@ class CookieTest < Minitest::Test
|
|
|
41
44
|
assert_equal true, (c.secure || c.secure?)
|
|
42
45
|
end
|
|
43
46
|
|
|
47
|
+
def test_add_rejects_invalid_cookie_type
|
|
48
|
+
error = assert_raises(TypeError) do
|
|
49
|
+
@jar.add(Object.new, @base_url)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
assert_equal "cookie must be a Wreq::Cookie or String", error.message
|
|
53
|
+
end
|
|
54
|
+
|
|
44
55
|
def test_add_multiple_and_remove
|
|
45
56
|
@jar.add("a=1; Path=/", @base_url)
|
|
46
57
|
@jar.add("b=2; Path=/", @base_url)
|
|
@@ -84,7 +95,9 @@ class CookieTest < Minitest::Test
|
|
|
84
95
|
@jar.add("exp=1; Expires=#{t.gmtime.strftime("%a, %d %b %Y %H:%M:%S GMT")}; Path=/", @base_url)
|
|
85
96
|
c2 = @jar.get_all.find { |c| c.name == "exp" }
|
|
86
97
|
assert c2
|
|
87
|
-
#
|
|
98
|
+
# expires_at returns Time. expires remains available for numeric callers.
|
|
99
|
+
assert_kind_of Time, c2.expires_at
|
|
100
|
+
assert_predicate c2.expires_at, :utc?
|
|
88
101
|
if (e = c2.expires)
|
|
89
102
|
assert_kind_of Float, e
|
|
90
103
|
assert_operator e, :>, Time.now.to_f - 1_000_000 # sanity bound
|
|
@@ -104,15 +117,18 @@ class CookieTest < Minitest::Test
|
|
|
104
117
|
assert_nil c.domain
|
|
105
118
|
assert_nil c.max_age
|
|
106
119
|
assert_nil c.expires
|
|
120
|
+
assert_nil c.expires_at
|
|
107
121
|
|
|
108
122
|
assert_equal false, (c.http_only || c.http_only?)
|
|
109
123
|
assert_equal false, (c.secure || c.secure?)
|
|
110
124
|
assert_equal false, c.same_site_lax?
|
|
111
125
|
assert_equal false, c.same_site_strict?
|
|
126
|
+
assert_nil c.same_site
|
|
127
|
+
assert_equal "sid=abc", c.to_s
|
|
112
128
|
end
|
|
113
129
|
|
|
114
130
|
def test_cookie_new_full_attributes
|
|
115
|
-
exp = Time.
|
|
131
|
+
exp = Time.utc(2030, 1, 1, 0, 0, Rational(123_456_789, 1_000_000_000))
|
|
116
132
|
c = Wreq::Cookie.new("sess", "v",
|
|
117
133
|
domain: "example.com",
|
|
118
134
|
path: "/",
|
|
@@ -130,16 +146,199 @@ class CookieTest < Minitest::Test
|
|
|
130
146
|
# Max-Age returns seconds as Integer
|
|
131
147
|
assert_equal 3600, c.max_age
|
|
132
148
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
assert_in_delta exp, c.expires, 2.0
|
|
149
|
+
assert_equal exp, c.expires_at
|
|
150
|
+
assert_predicate c.expires_at, :utc?
|
|
151
|
+
assert_in_delta exp.to_f, c.expires, 1e-6
|
|
137
152
|
|
|
138
153
|
assert_equal true, (c.http_only || c.http_only?)
|
|
139
154
|
assert_equal true, (c.secure || c.secure?)
|
|
140
|
-
# constructor currently sets SameSite to none
|
|
141
155
|
assert_equal true, c.same_site_lax?
|
|
142
156
|
assert_equal false, c.same_site_strict?
|
|
157
|
+
assert_equal Wreq::SameSite::Lax, c.same_site
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def test_same_site_reader_distinguishes_none_from_unset
|
|
161
|
+
cookie = Wreq::Cookie.new("same-site", "value", same_site: Wreq::SameSite::None)
|
|
162
|
+
|
|
163
|
+
assert_equal Wreq::SameSite::None, cookie.same_site
|
|
164
|
+
refute cookie.same_site_lax?
|
|
165
|
+
refute cookie.same_site_strict?
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def test_cookie_new_uses_shared_keyword_validation
|
|
169
|
+
cookie = Wreq::Cookie.new("sid", "abc", **{"path" => "/"})
|
|
170
|
+
assert_equal "/", cookie.path
|
|
171
|
+
|
|
172
|
+
secret = "must-not-appear"
|
|
173
|
+
unknown_error = assert_raises(ArgumentError) do
|
|
174
|
+
Wreq::Cookie.new("sid", "abc", domian: secret)
|
|
175
|
+
end
|
|
176
|
+
assert_includes unknown_error.message, ":domian"
|
|
177
|
+
refute_includes unknown_error.message, secret
|
|
178
|
+
|
|
179
|
+
duplicate_options = {path: "/one"}
|
|
180
|
+
duplicate_options["path"] = "/two"
|
|
181
|
+
duplicate_error = assert_raises(ArgumentError) do
|
|
182
|
+
Wreq::Cookie.new("sid", "abc", **duplicate_options)
|
|
183
|
+
end
|
|
184
|
+
assert_includes duplicate_error.message, "duplicate option: :path"
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def test_expires_accepts_past_time
|
|
188
|
+
expiration = Time.at(Rational(-5, 4)).utc
|
|
189
|
+
cookie = Wreq::Cookie.new("past", "value", expires: expiration)
|
|
190
|
+
|
|
191
|
+
assert_equal expiration, cookie.expires_at
|
|
192
|
+
assert_in_delta(-1.25, cookie.expires, 1e-9)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def test_expires_accepts_integer_and_fractional_timestamps
|
|
196
|
+
[
|
|
197
|
+
1_893_456_000,
|
|
198
|
+
1_893_456_000.125,
|
|
199
|
+
Rational(-5, 4),
|
|
200
|
+
BigDecimal("1893456000.125")
|
|
201
|
+
].each do |timestamp|
|
|
202
|
+
cookie = Wreq::Cookie.new("timestamp", "value", expires: timestamp)
|
|
203
|
+
|
|
204
|
+
assert_kind_of Time, cookie.expires_at
|
|
205
|
+
assert_predicate cookie.expires_at, :utc?
|
|
206
|
+
assert_in_delta timestamp.to_f, cookie.expires_at.to_f, 1e-6
|
|
207
|
+
assert_in_delta timestamp.to_f, cookie.expires, 1e-6
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def test_expires_normalizes_time_to_utc_without_losing_precision
|
|
212
|
+
expiration = Time.new(
|
|
213
|
+
2030,
|
|
214
|
+
1,
|
|
215
|
+
1,
|
|
216
|
+
12,
|
|
217
|
+
34,
|
|
218
|
+
Rational(123_456_789, 1_000_000_000),
|
|
219
|
+
"+09:00"
|
|
220
|
+
)
|
|
221
|
+
actual = Wreq::Cookie.new("offset", "value", expires: expiration).expires_at
|
|
222
|
+
|
|
223
|
+
assert_predicate actual, :utc?
|
|
224
|
+
assert_equal expiration.to_r, actual.to_r
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def test_expires_rejects_non_finite_timestamps
|
|
228
|
+
[Float::NAN, Float::INFINITY, -Float::INFINITY].each do |timestamp|
|
|
229
|
+
error = assert_raises(ArgumentError) do
|
|
230
|
+
Wreq::Cookie.new("invalid", "value", expires: timestamp)
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
assert_match(/expires.*finite/, error.message)
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def test_expires_rejects_unrepresentable_times
|
|
238
|
+
expirations = [
|
|
239
|
+
-(2**63),
|
|
240
|
+
2**63 - 1,
|
|
241
|
+
-Float::MAX,
|
|
242
|
+
Float::MAX,
|
|
243
|
+
Time.utc(10_000, 1, 1)
|
|
244
|
+
]
|
|
245
|
+
|
|
246
|
+
expirations.each do |expiration|
|
|
247
|
+
error = assert_raises(RangeError) do
|
|
248
|
+
Wreq::Cookie.new("invalid", "value", expires: expiration)
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
assert_match(/expires.*supported range/, error.message)
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def test_max_age_accepts_signed_boundaries_without_wrapping
|
|
256
|
+
[-(2**63), -1, 0, 2**63 - 1].each do |max_age|
|
|
257
|
+
cookie = Wreq::Cookie.new("max-age", "value", max_age: max_age)
|
|
258
|
+
|
|
259
|
+
assert_equal max_age, cookie.max_age
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
[-(2**63) - 1, 2**63].each do |max_age|
|
|
263
|
+
assert_raises(RangeError) do
|
|
264
|
+
Wreq::Cookie.new("max-age", "value", max_age: max_age)
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def test_non_positive_max_age_removes_cookie_from_jar
|
|
270
|
+
[-1, 0].each do |max_age|
|
|
271
|
+
jar = Wreq::Jar.new
|
|
272
|
+
jar.add("session=old; Path=/", @base_url)
|
|
273
|
+
deletion = Wreq::Cookie.new("session", "gone", path: "/", max_age: max_age)
|
|
274
|
+
|
|
275
|
+
jar.add(deletion, @base_url)
|
|
276
|
+
|
|
277
|
+
assert_equal max_age, deletion.max_age
|
|
278
|
+
assert_empty jar.get_all
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
def test_past_expiration_removes_cookie_from_jar
|
|
283
|
+
@jar.add("session=old; Path=/", @base_url)
|
|
284
|
+
deletion = Wreq::Cookie.new(
|
|
285
|
+
"session",
|
|
286
|
+
"gone",
|
|
287
|
+
path: "/",
|
|
288
|
+
expires: Time.at(-1).utc
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
@jar.add(deletion, @base_url)
|
|
292
|
+
|
|
293
|
+
assert_empty @jar.get_all
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def test_expiration_regressions_exit_subprocess_normally
|
|
297
|
+
lib_dir = File.expand_path("../lib", __dir__)
|
|
298
|
+
script = <<~RUBY
|
|
299
|
+
require "wreq"
|
|
300
|
+
|
|
301
|
+
past = Wreq::Cookie.new("past", "value", expires: -1.0)
|
|
302
|
+
abort "past timestamp was not retained" unless past.expires_at == Time.at(-1).utc
|
|
303
|
+
|
|
304
|
+
[Float::NAN, Float::INFINITY, -Float::INFINITY].each do |timestamp|
|
|
305
|
+
begin
|
|
306
|
+
Wreq::Cookie.new("invalid", "value", expires: timestamp)
|
|
307
|
+
rescue ArgumentError
|
|
308
|
+
next
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
abort "non-finite timestamp did not raise ArgumentError"
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
[Float::MAX, -Float::MAX].each do |timestamp|
|
|
315
|
+
begin
|
|
316
|
+
Wreq::Cookie.new("invalid", "value", expires: timestamp)
|
|
317
|
+
rescue RangeError
|
|
318
|
+
next
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
abort "unrepresentable finite timestamp did not raise RangeError"
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
[-(2**63) - 1, 2**63].each do |max_age|
|
|
325
|
+
begin
|
|
326
|
+
Wreq::Cookie.new("invalid", "value", max_age: max_age)
|
|
327
|
+
rescue RangeError
|
|
328
|
+
next
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
abort "out-of-range Max-Age did not raise RangeError"
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
puts "ok"
|
|
335
|
+
RUBY
|
|
336
|
+
|
|
337
|
+
stdout, stderr, status = Open3.capture3(RbConfig.ruby, "-I", lib_dir, "-e", script)
|
|
338
|
+
|
|
339
|
+
assert status.success?, "subprocess failed with #{status.inspect}: #{stderr}"
|
|
340
|
+
assert_equal "ok\n", stdout
|
|
341
|
+
refute_match(/panicked|fatal|access violation|cannot convert float seconds to Duration/i, stderr)
|
|
143
342
|
end
|
|
144
343
|
|
|
145
344
|
def test_same_site_flags_from_parsed_header
|
|
@@ -148,10 +347,12 @@ class CookieTest < Minitest::Test
|
|
|
148
347
|
@jar.add("s2=1; Path=/; SameSite=Lax", @base_url)
|
|
149
348
|
|
|
150
349
|
cookies = @jar.get_all
|
|
151
|
-
h = cookies.to_h
|
|
350
|
+
h = cookies.to_h do |cookie|
|
|
351
|
+
[cookie.name, [cookie.same_site, cookie.same_site_strict?, cookie.same_site_lax?]]
|
|
352
|
+
end
|
|
152
353
|
|
|
153
|
-
assert_equal [true, false], h["s1"]
|
|
154
|
-
assert_equal [false, true], h["s2"]
|
|
354
|
+
assert_equal [Wreq::SameSite::Strict, true, false], h["s1"]
|
|
355
|
+
assert_equal [Wreq::SameSite::Lax, false, true], h["s2"]
|
|
155
356
|
end
|
|
156
357
|
|
|
157
358
|
def test_request_uncompressed_cookies
|