wreq 1.2.0 → 1.2.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 55fb3870a295fb5c880d3114c560170df733c64738bf07fae26bcbe7055a3d0e
4
- data.tar.gz: d40e5a5942c85fbfb80c3a6dc76ee1bdc4da80a2286f38d217bdf2fe18e7e924
3
+ metadata.gz: 847301c3eef022dc8dbdc25f84308f03ef73ac06f366caea11ecad0df51c8342
4
+ data.tar.gz: 6ec2ed9471e605f58546a9deeba976ccc4984a1b3c2372e3f9d2e85fdd3d3f94
5
5
  SHA512:
6
- metadata.gz: 7eef95347359cbfbf15654d89425eee6ca329f3603d2c5467368f3f90118c7195b201fe2380026354901998a7bb73befccff227e5f71040d513b0953e09410ee
7
- data.tar.gz: d8b5c0a8416a36ef9f43721f824fb8f7201c8b31e709b2fd42c5b1ef61f5da8031fc529725013832efd4a1101f86d6e503c4e67e1d9f84c790d35897e2090fac
6
+ metadata.gz: 5bbebe9ab0ad0119019d95d15c37266fff1d22832f0beaf8f576ceb461bcb7129da8c617c17dbac0c1b918f5cf992b52c1d14ea140b9753a573dde3c9ee959a2
7
+ data.tar.gz: 7baa2818b2a41af307f1152c4a4608ab7b3d2d3d921b29e50f4587637838318808b0153d80fdb9f4761a3454d42c3f51101d09c06f679f5583d8c733f39b73b5
data/Cargo.lock CHANGED
@@ -85,9 +85,9 @@ dependencies = [
85
85
 
86
86
  [[package]]
87
87
  name = "bitflags"
88
- version = "2.12.1"
88
+ version = "2.13.0"
89
89
  source = "registry+https://github.com/rust-lang/crates.io-index"
90
- checksum = "84d7ced0ae9557296835c32bf1b1e02b44c746701f898460fb000d7eaa84f00a"
90
+ checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
91
91
 
92
92
  [[package]]
93
93
  name = "brotli"
@@ -444,9 +444,9 @@ dependencies = [
444
444
 
445
445
  [[package]]
446
446
  name = "http"
447
- version = "1.4.1"
447
+ version = "1.4.2"
448
448
  source = "registry+https://github.com/rust-lang/crates.io-index"
449
- checksum = "8be7462df143984c4598a256ef469b251d7d7f9e271135073e78fc535414f3d0"
449
+ checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425"
450
450
  dependencies = [
451
451
  "bytes",
452
452
  "itoa",
@@ -477,9 +477,9 @@ dependencies = [
477
477
 
478
478
  [[package]]
479
479
  name = "http2"
480
- version = "0.5.17"
480
+ version = "0.5.18"
481
481
  source = "registry+https://github.com/rust-lang/crates.io-index"
482
- checksum = "569ef7a780e853c4e1768f58a3c8168193b82cdcbab66638a0b1c6583ec5995e"
482
+ checksum = "9d8c910a959d6fd27dd6f8fdb26509ac0f6561465cc8c5f875d45a203ab97911"
483
483
  dependencies = [
484
484
  "atomic-waker",
485
485
  "bytes",
@@ -902,9 +902,9 @@ dependencies = [
902
902
 
903
903
  [[package]]
904
904
  name = "regex"
905
- version = "1.12.3"
905
+ version = "1.12.4"
906
906
  source = "registry+https://github.com/rust-lang/crates.io-index"
907
- checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
907
+ checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba"
908
908
  dependencies = [
909
909
  "aho-corasick",
910
910
  "memchr",
@@ -925,9 +925,9 @@ dependencies = [
925
925
 
926
926
  [[package]]
927
927
  name = "regex-syntax"
928
- version = "0.8.10"
928
+ version = "0.8.11"
929
929
  source = "registry+https://github.com/rust-lang/crates.io-index"
930
- checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
930
+ checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
931
931
 
932
932
  [[package]]
933
933
  name = "rustc-hash"
@@ -1559,7 +1559,7 @@ dependencies = [
1559
1559
 
1560
1560
  [[package]]
1561
1561
  name = "wreq-ruby"
1562
- version = "1.2.0"
1562
+ version = "1.2.2"
1563
1563
  dependencies = [
1564
1564
  "arc-swap",
1565
1565
  "bytes",
data/Cargo.toml CHANGED
@@ -6,7 +6,7 @@ homepage = "https://github.com/SearchApi/wreq-ruby"
6
6
  repository = "https://github.com/SearchApi/wreq-ruby"
7
7
  edition = "2024"
8
8
  rust-version = "1.85"
9
- version = "1.2.0"
9
+ version = "1.2.2"
10
10
 
11
11
  [lib]
12
12
  crate-type = ["cdylib"]
@@ -38,7 +38,7 @@ indexmap = { version = "2.14.0", features = ["serde"] }
38
38
  cookie = "0.18.1"
39
39
  bytes = "1.11.1"
40
40
  arc-swap = "1.9.1"
41
- http = "1.4.0"
41
+ http = "1.4.1"
42
42
  http-body-util = "0.1.3"
43
43
  futures-util = { version = "0.3.32", default-features = false }
44
44
 
data/lib/wreq.rb CHANGED
@@ -25,7 +25,7 @@ unless defined?(Wreq)
25
25
  # @param method [Wreq::Method] HTTP method to use
26
26
  # @param url [String] Target URL
27
27
  # @param headers [Wreq::Headers, Hash{String=>String}, nil] Custom headers for this request
28
- # @param orig_headers [Hash{String=>String}, nil] Original headers (raw, unmodified)
28
+ # @param orig_headers [Array<String>, nil] Original header names used to preserve raw header order and HTTP/1 case-sensitive header handling
29
29
  # @param default_headers [Hash{String=>String}, nil] Default headers to merge
30
30
  # @param query [Hash, nil] URL query parameters
31
31
  # @param auth [String, nil] Authorization header value
@@ -56,7 +56,7 @@ unless defined?(Wreq)
56
56
  #
57
57
  # @param url [String] Target URL
58
58
  # @param headers [Wreq::Headers, Hash{String=>String}, nil] Custom headers for this request
59
- # @param orig_headers [Hash{String=>String}, nil] Original headers (raw, unmodified)
59
+ # @param orig_headers [Array<String>, nil] Original header names used to preserve raw header order and HTTP/1 case-sensitive header handling
60
60
  # @param default_headers [Hash{String=>String}, nil] Default headers to merge
61
61
  # @param query [Hash, nil] URL query parameters
62
62
  # @param auth [String, nil] Authorization header value
@@ -87,7 +87,7 @@ unless defined?(Wreq)
87
87
  #
88
88
  # @param url [String] Target URL
89
89
  # @param headers [Wreq::Headers, Hash{String=>String}, nil] Custom headers for this request
90
- # @param orig_headers [Hash{String=>String}, nil] Original headers (raw, unmodified)
90
+ # @param orig_headers [Array<String>, nil] Original header names used to preserve raw header order and HTTP/1 case-sensitive header handling
91
91
  # @param default_headers [Hash{String=>String}, nil] Default headers to merge
92
92
  # @param query [Hash, nil] URL query parameters
93
93
  # @param auth [String, nil] Authorization header value
@@ -118,7 +118,7 @@ unless defined?(Wreq)
118
118
  #
119
119
  # @param url [String] Target URL
120
120
  # @param headers [Wreq::Headers, Hash{String=>String}, nil] Custom headers for this request
121
- # @param orig_headers [Hash{String=>String}, nil] Original headers (raw, unmodified)
121
+ # @param orig_headers [Array<String>, nil] Original header names used to preserve raw header order and HTTP/1 case-sensitive header handling
122
122
  # @param default_headers [Hash{String=>String}, nil] Default headers to merge
123
123
  # @param query [Hash, nil] URL query parameters
124
124
  # @param auth [String, nil] Authorization header value
@@ -149,7 +149,7 @@ unless defined?(Wreq)
149
149
  #
150
150
  # @param url [String] Target URL
151
151
  # @param headers [Wreq::Headers, Hash{String=>String}, nil] Custom headers for this request
152
- # @param orig_headers [Hash{String=>String}, nil] Original headers (raw, unmodified)
152
+ # @param orig_headers [Array<String>, nil] Original header names used to preserve raw header order and HTTP/1 case-sensitive header handling
153
153
  # @param default_headers [Hash{String=>String}, nil] Default headers to merge
154
154
  # @param query [Hash, nil] URL query parameters
155
155
  # @param auth [String, nil] Authorization header value
@@ -180,7 +180,7 @@ unless defined?(Wreq)
180
180
  #
181
181
  # @param url [String] Target URL
182
182
  # @param headers [Wreq::Headers, Hash{String=>String}, nil] Custom headers for this request
183
- # @param orig_headers [Hash{String=>String}, nil] Original headers (raw, unmodified)
183
+ # @param orig_headers [Array<String>, nil] Original header names used to preserve raw header order and HTTP/1 case-sensitive header handling
184
184
  # @param default_headers [Hash{String=>String}, nil] Default headers to merge
185
185
  # @param query [Hash, nil] URL query parameters
186
186
  # @param auth [String, nil] Authorization header value
@@ -211,7 +211,7 @@ unless defined?(Wreq)
211
211
  #
212
212
  # @param url [String] Target URL
213
213
  # @param headers [Wreq::Headers, Hash{String=>String}, nil] Custom headers for this request
214
- # @param orig_headers [Hash{String=>String}, nil] Original headers (raw, unmodified)
214
+ # @param orig_headers [Array<String>, nil] Original header names used to preserve raw header order and HTTP/1 case-sensitive header handling
215
215
  # @param default_headers [Hash{String=>String}, nil] Default headers to merge
216
216
  # @param query [Hash, nil] URL query parameters
217
217
  # @param auth [String, nil] Authorization header value
@@ -242,7 +242,7 @@ unless defined?(Wreq)
242
242
  #
243
243
  # @param url [String] Target URL
244
244
  # @param headers [Wreq::Headers, Hash{String=>String}, nil] Custom headers for this request
245
- # @param orig_headers [Hash{String=>String}, nil] Original headers (raw, unmodified)
245
+ # @param orig_headers [Array<String>, nil] Original header names used to preserve raw header order and HTTP/1 case-sensitive header handling
246
246
  # @param default_headers [Hash{String=>String}, nil] Default headers to merge
247
247
  # @param query [Hash, nil] URL query parameters
248
248
  # @param auth [String, nil] Authorization header value
@@ -273,7 +273,7 @@ unless defined?(Wreq)
273
273
  #
274
274
  # @param url [String] Target URL
275
275
  # @param headers [Wreq::Headers, Hash{String=>String}, nil] Custom headers for this request
276
- # @param orig_headers [Hash{String=>String}, nil] Original headers (raw, unmodified)
276
+ # @param orig_headers [Array<String>, nil] Original header names used to preserve raw header order and HTTP/1 case-sensitive header handling
277
277
  # @param default_headers [Hash{String=>String}, nil] Default headers to merge
278
278
  # @param query [Hash, nil] URL query parameters
279
279
  # @param auth [String, nil] Authorization header value
@@ -38,6 +38,8 @@ unless defined?(Wreq)
38
38
  # @param headers [Wreq::Headers, Hash{String=>String}, nil] Default headers to include
39
39
  # in every request. Header names are case-insensitive. These headers
40
40
  # can be overridden on a per-request basis.
41
+ # @param orig_headers [Array<String>, nil] Original header names used to
42
+ # preserve raw header order and HTTP/1 case-sensitive header handling.
41
43
  #
42
44
  # @param referer [Boolean, nil] Whether to automatically send Referer
43
45
  # headers when following redirects. When true, the previous URL will
@@ -237,7 +239,7 @@ unless defined?(Wreq)
237
239
  # @param method [Wreq::Method] HTTP method to use
238
240
  # @param url [String] Target URL
239
241
  # @param headers [Wreq::Headers, Hash{String=>String}, nil] Custom headers for this request
240
- # @param orig_headers [Hash{String=>String}, nil] Original headers (raw, unmodified)
242
+ # @param orig_headers [Array<String>, nil] Original header names used to preserve raw header order and HTTP/1 case-sensitive header handling
241
243
  # @param default_headers [Hash{String=>String}, nil] Default headers to merge
242
244
  # @param query [Hash, nil] URL query parameters
243
245
  # @param auth [String, nil] Authorization header value
@@ -268,7 +270,7 @@ unless defined?(Wreq)
268
270
  #
269
271
  # @param url [String] Target URL
270
272
  # @param headers [Wreq::Headers, Hash{String=>String}, nil] Custom headers for this request
271
- # @param orig_headers [Hash{String=>String}, nil] Original headers (raw, unmodified)
273
+ # @param orig_headers [Array<String>, nil] Original header names used to preserve raw header order and HTTP/1 case-sensitive header handling
272
274
  # @param default_headers [Hash{String=>String}, nil] Default headers to merge
273
275
  # @param query [Hash, nil] URL query parameters
274
276
  # @param auth [String, nil] Authorization header value
@@ -299,7 +301,7 @@ unless defined?(Wreq)
299
301
  #
300
302
  # @param url [String] Target URL
301
303
  # @param headers [Wreq::Headers, Hash{String=>String}, nil] Custom headers for this request
302
- # @param orig_headers [Hash{String=>String}, nil] Original headers (raw, unmodified)
304
+ # @param orig_headers [Array<String>, nil] Original header names used to preserve raw header order and HTTP/1 case-sensitive header handling
303
305
  # @param default_headers [Hash{String=>String}, nil] Default headers to merge
304
306
  # @param query [Hash, nil] URL query parameters
305
307
  # @param auth [String, nil] Authorization header value
@@ -330,7 +332,7 @@ unless defined?(Wreq)
330
332
  #
331
333
  # @param url [String] Target URL
332
334
  # @param headers [Wreq::Headers, Hash{String=>String}, nil] Custom headers for this request
333
- # @param orig_headers [Hash{String=>String}, nil] Original headers (raw, unmodified)
335
+ # @param orig_headers [Array<String>, nil] Original header names used to preserve raw header order and HTTP/1 case-sensitive header handling
334
336
  # @param default_headers [Hash{String=>String}, nil] Default headers to merge
335
337
  # @param query [Hash, nil] URL query parameters
336
338
  # @param auth [String, nil] Authorization header value
@@ -361,7 +363,7 @@ unless defined?(Wreq)
361
363
  #
362
364
  # @param url [String] Target URL
363
365
  # @param headers [Wreq::Headers, Hash{String=>String}, nil] Custom headers for this request
364
- # @param orig_headers [Hash{String=>String}, nil] Original headers (raw, unmodified)
366
+ # @param orig_headers [Array<String>, nil] Original header names used to preserve raw header order and HTTP/1 case-sensitive header handling
365
367
  # @param default_headers [Hash{String=>String}, nil] Default headers to merge
366
368
  # @param query [Hash, nil] URL query parameters
367
369
  # @param auth [String, nil] Authorization header value
@@ -392,7 +394,7 @@ unless defined?(Wreq)
392
394
  #
393
395
  # @param url [String] Target URL
394
396
  # @param headers [Wreq::Headers, Hash{String=>String}, nil] Custom headers for this request
395
- # @param orig_headers [Hash{String=>String}, nil] Original headers (raw, unmodified)
397
+ # @param orig_headers [Array<String>, nil] Original header names used to preserve raw header order and HTTP/1 case-sensitive header handling
396
398
  # @param default_headers [Hash{String=>String}, nil] Default headers to merge
397
399
  # @param query [Hash, nil] URL query parameters
398
400
  # @param auth [String, nil] Authorization header value
@@ -423,7 +425,7 @@ unless defined?(Wreq)
423
425
  #
424
426
  # @param url [String] Target URL
425
427
  # @param headers [Wreq::Headers, Hash{String=>String}, nil] Custom headers for this request
426
- # @param orig_headers [Hash{String=>String}, nil] Original headers (raw, unmodified)
428
+ # @param orig_headers [Array<String>, nil] Original header names used to preserve raw header order and HTTP/1 case-sensitive header handling
427
429
  # @param default_headers [Hash{String=>String}, nil] Default headers to merge
428
430
  # @param query [Hash, nil] URL query parameters
429
431
  # @param auth [String, nil] Authorization header value
@@ -454,7 +456,7 @@ unless defined?(Wreq)
454
456
  #
455
457
  # @param url [String] Target URL
456
458
  # @param headers [Wreq::Headers, Hash{String=>String}, nil] Custom headers for this request
457
- # @param orig_headers [Hash{String=>String}, nil] Original headers (raw, unmodified)
459
+ # @param orig_headers [Array<String>, nil] Original header names used to preserve raw header order and HTTP/1 case-sensitive header handling
458
460
  # @param default_headers [Hash{String=>String}, nil] Default headers to merge
459
461
  # @param query [Hash, nil] URL query parameters
460
462
  # @param auth [String, nil] Authorization header value
@@ -485,7 +487,7 @@ unless defined?(Wreq)
485
487
  #
486
488
  # @param url [String] Target URL
487
489
  # @param headers [Wreq::Headers, Hash{String=>String}, nil] Custom headers for this request
488
- # @param orig_headers [Hash{String=>String}, nil] Original headers (raw, unmodified)
490
+ # @param orig_headers [Array<String>, nil] Original header names used to preserve raw header order and HTTP/1 case-sensitive header handling
489
491
  # @param default_headers [Hash{String=>String}, nil] Default headers to merge
490
492
  # @param query [Hash, nil] URL query parameters
491
493
  # @param auth [String, nil] Authorization header value
@@ -49,6 +49,20 @@ module Wreq
49
49
  def to_s
50
50
  end
51
51
  end
52
+
53
+ # Compares HTTP versions by semantic value, not object identity.
54
+ #
55
+ # This method is implemented by the native extension.
56
+ # When comparing with non-{Wreq::Version} objects, it returns false.
57
+ #
58
+ # @param other [Object] object to compare against
59
+ # @return [Boolean] true when both represent the same HTTP version
60
+ # @example
61
+ # Wreq::Version::HTTP_11 == response.version
62
+ unless method_defined?(:==)
63
+ def ==(other)
64
+ end
65
+ end
52
66
  end
53
67
 
54
68
  # HTTP status code wrapper.
data/src/client/req.rs CHANGED
@@ -3,7 +3,7 @@ use std::{net::IpAddr, time::Duration};
3
3
  use http::header;
4
4
  use magnus::{RHash, TryConvert, typed_data::Obj, value::ReprValue};
5
5
  use serde::Deserialize;
6
- use wreq::{Client, Proxy, Version, header::OrigHeaderMap};
6
+ use wreq::{Client, Proxy};
7
7
 
8
8
  use super::body::{Body, Form, Json};
9
9
  use crate::{
@@ -12,8 +12,8 @@ use crate::{
12
12
  emulate::Emulation,
13
13
  error::wreq_error_to_magnus,
14
14
  extractor::Extractor,
15
- header::Headers,
16
- http::Method,
15
+ header::{Headers, OrigHeaders},
16
+ http::{Method, Version},
17
17
  rt,
18
18
  };
19
19
 
@@ -54,7 +54,7 @@ pub struct Request {
54
54
 
55
55
  /// The original headers to use for the request.
56
56
  #[serde(skip)]
57
- orig_headers: Option<OrigHeaderMap>,
57
+ orig_headers: Option<OrigHeaders>,
58
58
 
59
59
  /// The cookies to use for the request.
60
60
  #[serde(skip)]
@@ -112,10 +112,18 @@ impl Request {
112
112
  builder.emulation = Some((*obj).clone());
113
113
  }
114
114
 
115
+ if let Some(v) = hash.get(ruby.to_symbol(stringify!(version))) {
116
+ builder.version = Some(Version::try_convert(v)?);
117
+ }
118
+
115
119
  if let Some(v) = hash.get(ruby.to_symbol(stringify!(headers))) {
116
120
  builder.headers = Some(Headers::try_convert(v)?);
117
121
  }
118
122
 
123
+ if let Some(v) = hash.get(ruby.to_symbol(stringify!(orig_headers))) {
124
+ builder.orig_headers = Some(OrigHeaders::try_convert(v)?);
125
+ }
126
+
119
127
  if let Some(v) = hash.get(ruby.to_symbol(stringify!(cookies))) {
120
128
  builder.cookies = Some(Cookies::try_convert(v)?);
121
129
  }
@@ -125,8 +133,6 @@ impl Request {
125
133
  }
126
134
 
127
135
  builder.proxy = Extractor::<Proxy>::try_convert(keyword)?.into_inner();
128
- builder.version = Extractor::<Version>::try_convert(keyword)?.into_inner();
129
- builder.orig_headers = Extractor::<OrigHeaderMap>::try_convert(keyword)?.into_inner();
130
136
 
131
137
  Ok(builder)
132
138
  }
@@ -145,7 +151,13 @@ pub fn execute_request<U: AsRef<str>>(
145
151
  apply_option!(set_if_some_inner, builder, request.emulation, emulation);
146
152
 
147
153
  // Version options.
148
- apply_option!(set_if_some, builder, request.version, version);
154
+ apply_option!(
155
+ set_if_some_map,
156
+ builder,
157
+ request.version,
158
+ version,
159
+ Version::into_ffi
160
+ );
149
161
 
150
162
  // Timeout options.
151
163
  apply_option!(
@@ -170,7 +182,12 @@ pub fn execute_request<U: AsRef<str>>(
170
182
 
171
183
  // Headers options.
172
184
  apply_option!(set_if_some_into_inner, builder, request.headers, headers);
173
- apply_option!(set_if_some, builder, request.orig_headers, orig_headers);
185
+ apply_option!(
186
+ set_if_some_inner,
187
+ builder,
188
+ request.orig_headers,
189
+ orig_headers
190
+ );
174
191
  apply_option!(
175
192
  set_if_some,
176
193
  builder,
data/src/client.rs CHANGED
@@ -10,10 +10,7 @@ use magnus::{
10
10
  Module, Object, RHash, RModule, Ruby, TryConvert, Value, function, method, typed_data::Obj,
11
11
  };
12
12
  use serde::Deserialize;
13
- use wreq::{
14
- Proxy,
15
- header::{HeaderValue, OrigHeaderMap},
16
- };
13
+ use wreq::Proxy;
17
14
 
18
15
  use crate::{
19
16
  client::{req::execute_request, resp::Response},
@@ -22,7 +19,7 @@ use crate::{
22
19
  error::wreq_error_to_magnus,
23
20
  extractor::Extractor,
24
21
  gvl,
25
- header::Headers,
22
+ header::{Headers, OrigHeaders, UserAgent},
26
23
  http::Method,
27
24
  };
28
25
 
@@ -34,13 +31,13 @@ struct Builder {
34
31
  emulation: Option<Emulation>,
35
32
  /// The user agent to use for the client.
36
33
  #[serde(skip)]
37
- user_agent: Option<HeaderValue>,
34
+ user_agent: Option<UserAgent>,
38
35
  /// The headers to use for the client.
39
36
  #[serde(skip)]
40
37
  headers: Option<Headers>,
41
38
  /// The original headers to use for the client.
42
39
  #[serde(skip)]
43
- orig_headers: Option<OrigHeaderMap>,
40
+ orig_headers: Option<OrigHeaders>,
44
41
  /// Whether to use referer.
45
42
  referer: Option<bool>,
46
43
  /// Whether to allow redirects.
@@ -139,20 +136,22 @@ impl Builder {
139
136
  builder.emulation = Some((*Obj::<Emulation>::try_convert(v)?).clone());
140
137
  }
141
138
 
139
+ if let Some(v) = hash.get(ruby.to_symbol(stringify!(user_agent))) {
140
+ builder.user_agent = Some(UserAgent::try_convert(v)?);
141
+ }
142
+
142
143
  if let Some(v) = hash.get(ruby.to_symbol(stringify!(headers))) {
143
144
  builder.headers = Some(Headers::try_convert(v)?);
144
145
  }
145
146
 
146
- if let Some(v) = hash.get(ruby.to_symbol(stringify!(cookie_provider))) {
147
- builder.cookie_provider = Some((*Obj::<Jar>::try_convert(v)?).clone());
147
+ if let Some(v) = hash.get(ruby.to_symbol(stringify!(orig_headers))) {
148
+ builder.orig_headers = Some(OrigHeaders::try_convert(v)?);
148
149
  }
149
150
 
150
- if let Some(jar) = hash.get(ruby.to_symbol(stringify!(cookie_provider))) {
151
- builder.cookie_provider = Some((*Obj::<Jar>::try_convert(jar)?).clone());
151
+ if let Some(v) = hash.get(ruby.to_symbol(stringify!(cookie_provider))) {
152
+ builder.cookie_provider = Some((*Obj::<Jar>::try_convert(v)?).clone());
152
153
  }
153
154
 
154
- builder.user_agent = Extractor::<HeaderValue>::try_convert(*keyword)?.into_inner();
155
- builder.orig_headers = Extractor::<OrigHeaderMap>::try_convert(*keyword)?.into_inner();
156
155
  builder.proxy = Extractor::<Proxy>::try_convert(*keyword)?.into_inner();
157
156
 
158
157
  Ok(builder)
@@ -173,16 +172,21 @@ impl Client {
173
172
  apply_option!(set_if_some_inner, builder, params.emulation, emulation);
174
173
 
175
174
  // User agent options.
176
- apply_option!(set_if_some, builder, params.user_agent, user_agent);
175
+ apply_option!(set_if_some_inner, builder, params.user_agent, user_agent);
177
176
 
178
- // Default headers options.
177
+ // Headers options.
179
178
  apply_option!(
180
179
  set_if_some_into_inner,
181
180
  builder,
182
181
  params.headers,
183
182
  default_headers
184
183
  );
185
- apply_option!(set_if_some, builder, params.orig_headers, orig_headers);
184
+ apply_option!(
185
+ set_if_some_inner,
186
+ builder,
187
+ params.orig_headers,
188
+ orig_headers
189
+ );
186
190
 
187
191
  // Allow redirects options.
188
192
  apply_option!(set_if_some, builder, params.referer, referer);
data/src/error.rs CHANGED
@@ -117,6 +117,14 @@ pub fn header_value_error_to_magnus(err: wreq::header::InvalidHeaderValue) -> Ma
117
117
  )
118
118
  }
119
119
 
120
+ /// Map type/value errors to corresponding [`magnus::Error`]
121
+ pub fn type_value_error_to_magnus(err: &str) -> MagnusError {
122
+ MagnusError::new(
123
+ ruby!().get_inner(&BUILDER_ERROR),
124
+ format!("type error: {err}"),
125
+ )
126
+ }
127
+
120
128
  /// Map [`wreq::Error`] to corresponding [`magnus::Error`]
121
129
  pub fn wreq_error_to_magnus(err: wreq::Error) -> MagnusError {
122
130
  let error_msg = err.to_string();
data/src/extractor.rs CHANGED
@@ -1,6 +1,6 @@
1
1
  use magnus::{RArray, RHash, RString, Ruby, TryConvert, r_hash::ForEach};
2
2
  use wreq::{
3
- Proxy, Version,
3
+ Proxy,
4
4
  header::{HeaderMap, HeaderName, HeaderValue, OrigHeaderMap},
5
5
  };
6
6
 
@@ -32,52 +32,6 @@ where
32
32
  }
33
33
  }
34
34
 
35
- // ===== impl Extractor<Version> =====
36
-
37
- impl ExtractorName for Version {
38
- const NAME: &str = "version";
39
- }
40
-
41
- impl TryConvert for Extractor<Version> {
42
- fn try_convert(value: magnus::Value) -> Result<Self, magnus::Error> {
43
- let keyword = RHash::try_convert(value)?;
44
- if let Some(version_val) = keyword.get(Version::NAME) {
45
- return <&crate::http::Version>::try_convert(version_val)
46
- .cloned()
47
- .map(crate::http::Version::into_ffi)
48
- .map(Some)
49
- .map(Extractor);
50
- }
51
-
52
- Ok(Extractor(None))
53
- }
54
- }
55
-
56
- // ===== impl Extractor<HeaderValue> =====
57
-
58
- impl ExtractorName for HeaderValue {
59
- const NAME: &str = "user_agent";
60
- }
61
-
62
- impl TryConvert for Extractor<HeaderValue> {
63
- fn try_convert(value: magnus::Value) -> Result<Self, magnus::Error> {
64
- let ruby = Ruby::get_with(value);
65
- let keyword = RHash::try_convert(value)?;
66
-
67
- if let Some(ruby_value) = keyword
68
- .get(ruby.to_symbol(HeaderValue::NAME))
69
- .and_then(RString::from_value)
70
- {
71
- return HeaderValue::from_maybe_shared(ruby_value.to_bytes())
72
- .map(Some)
73
- .map(Extractor)
74
- .map_err(header_value_error_to_magnus);
75
- }
76
-
77
- Ok(Extractor(None))
78
- }
79
- }
80
-
81
35
  // ===== impl Extractor<HeaderMap> =====
82
36
 
83
37
  impl ExtractorName for HeaderMap {
data/src/header.rs CHANGED
@@ -9,8 +9,14 @@ use magnus::{
9
9
  r_hash::ForEach,
10
10
  typed_data::{Inspect, Obj},
11
11
  };
12
+ use wreq::header::OrigHeaderMap;
12
13
 
13
- use crate::error::{header_name_error_to_magnus, header_value_error_to_magnus};
14
+ use crate::error::{
15
+ header_name_error_to_magnus, header_value_error_to_magnus, type_value_error_to_magnus,
16
+ };
17
+
18
+ /// A wrapper for the User-Agent header value.
19
+ pub struct UserAgent(pub HeaderValue);
14
20
 
15
21
  /// HTTP headers collection with read and write operations.
16
22
  ///
@@ -20,11 +26,25 @@ use crate::error::{header_name_error_to_magnus, header_value_error_to_magnus};
20
26
  #[magnus::wrap(class = "Wreq::Headers", free_immediately, size)]
21
27
  pub struct Headers(pub RefCell<HeaderMap>);
22
28
 
29
+ /// A map from header names to their original casing as received in an HTTP message.
30
+ pub struct OrigHeaders(pub OrigHeaderMap);
31
+
23
32
  struct HeaderIter {
24
33
  inner: http::header::IntoIter<HeaderValue>,
25
34
  next_name: Option<HeaderName>,
26
35
  }
27
36
 
37
+ // ===== impl UserAgent =====
38
+
39
+ impl TryConvert for UserAgent {
40
+ fn try_convert(value: Value) -> Result<Self, Error> {
41
+ let s = RString::try_convert(value)?;
42
+ let header_value =
43
+ HeaderValue::from_maybe_shared(s.to_bytes()).map_err(header_value_error_to_magnus)?;
44
+ Ok(Self(header_value))
45
+ }
46
+ }
47
+
28
48
  // ===== impl Headers =====
29
49
 
30
50
  impl Headers {
@@ -166,6 +186,23 @@ impl TryConvert for Headers {
166
186
  }
167
187
  }
168
188
 
189
+ // ===== impl OrigHeaders =====
190
+
191
+ impl TryConvert for OrigHeaders {
192
+ fn try_convert(value: magnus::Value) -> Result<Self, magnus::Error> {
193
+ let mut map = OrigHeaderMap::new();
194
+
195
+ let rarray = RArray::from_value(value)
196
+ .ok_or_else(|| type_value_error_to_magnus("Expected an array of strings"))?;
197
+
198
+ for value in rarray.into_iter().flat_map(RString::from_value) {
199
+ map.insert(value.to_bytes());
200
+ }
201
+
202
+ Ok(Self(map))
203
+ }
204
+ }
205
+
169
206
  // ===== impl HeaderIter =====
170
207
 
171
208
  impl Iterator for HeaderIter {
data/src/http.rs CHANGED
@@ -1,4 +1,4 @@
1
- use magnus::{Error, Module, RModule, Ruby, method, typed_data::Inspect};
1
+ use magnus::{Error, Module, RModule, Ruby, TryConvert, Value, method, typed_data::Inspect};
2
2
 
3
3
  define_ruby_enum!(
4
4
  /// An HTTP version.
@@ -41,6 +41,20 @@ impl Version {
41
41
  pub fn to_s(&self) -> String {
42
42
  self.into_ffi().inspect()
43
43
  }
44
+
45
+ /// Value-based equality for Ruby (`==`).
46
+ #[inline]
47
+ pub fn equals(&self, other: Value) -> bool {
48
+ <&Version>::try_convert(other)
49
+ .map(|other| *self == *other)
50
+ .unwrap_or(false)
51
+ }
52
+ }
53
+
54
+ impl TryConvert for Version {
55
+ fn try_convert(value: magnus::Value) -> Result<Self, magnus::Error> {
56
+ <&Version>::try_convert(value).cloned()
57
+ }
44
58
  }
45
59
 
46
60
  // ===== impl StatusCode =====
@@ -113,6 +127,7 @@ pub fn include(ruby: &Ruby, gem_module: &RModule) -> Result<(), Error> {
113
127
  version_class.const_set("HTTP_2", Version::HTTP_2)?;
114
128
  version_class.const_set("HTTP_3", Version::HTTP_3)?;
115
129
  version_class.define_method("to_s", method!(Version::to_s, 0))?;
130
+ version_class.define_method("==", method!(Version::equals, 1))?;
116
131
 
117
132
  let status_code_class = gem_module.define_class("StatusCode", ruby.class_object())?;
118
133
  status_code_class.define_method("as_int", method!(StatusCode::as_int, 0))?;
@@ -0,0 +1,115 @@
1
+ require "test_helper"
2
+
3
+ class OrigHeaderTest < Minitest::Test
4
+ URL = "https://tls.browserleaks.com/http1"
5
+
6
+ CASES = [
7
+ {
8
+ name: "mixed_case_descending",
9
+ headers: {
10
+ "X-Zeta-Token" => "zeta",
11
+ "x-alpha-key" => "alpha",
12
+ "X-MiXeD-CaSe" => "mixed"
13
+ },
14
+ orig_headers: ["X-Zeta-Token", "x-alpha-key", "X-MiXeD-CaSe"]
15
+ },
16
+ {
17
+ name: "reverse_alpha_order",
18
+ headers: {
19
+ "X-Third" => "3",
20
+ "X-Second" => "2",
21
+ "X-First" => "1"
22
+ },
23
+ orig_headers: ["X-Third", "X-Second", "X-First"]
24
+ },
25
+ {
26
+ name: "preserve_weird_casing",
27
+ headers: {
28
+ "x-a" => "a",
29
+ "X-B" => "b",
30
+ "x-C" => "c"
31
+ },
32
+ orig_headers: ["x-C", "x-a", "X-B"]
33
+ },
34
+ {
35
+ name: "interleaved_tokens",
36
+ headers: {
37
+ "X-Token-3" => "v3",
38
+ "X-Token-1" => "v1",
39
+ "X-Token-2" => "v2"
40
+ },
41
+ orig_headers: ["X-Token-1", "X-Token-2", "X-Token-3"]
42
+ }
43
+ ].freeze
44
+
45
+ def test_client_default_orig_headers_preserves_header_order_in_multiple_shuffled_cases
46
+ CASES.each do |kase|
47
+ client = Wreq::Client.new(
48
+ headers: kase[:headers],
49
+ orig_headers: kase[:orig_headers]
50
+ )
51
+
52
+ response = client.get(URL, version: Wreq::Version::HTTP_11)
53
+ assert_equal 200, response.code, "case=#{kase[:name]}"
54
+
55
+ echoed_headers = extract_http1_headers(response.json, kase[:name])
56
+ assert_header_order(echoed_headers, kase[:orig_headers], kase[:name])
57
+ assert_header_values(echoed_headers, kase[:headers], kase[:name])
58
+ end
59
+ end
60
+
61
+ def test_module_request_orig_headers_preserves_header_order_in_multiple_shuffled_cases
62
+ CASES.each do |kase|
63
+ response = Wreq.get(
64
+ URL,
65
+ headers: kase[:headers],
66
+ orig_headers: kase[:orig_headers],
67
+ version: Wreq::Version::HTTP_11
68
+ )
69
+ assert_equal 200, response.code, "case=#{kase[:name]}"
70
+
71
+ echoed_headers = extract_http1_headers(response.json, kase[:name])
72
+ assert_header_order(echoed_headers, kase[:orig_headers], kase[:name])
73
+ assert_header_values(echoed_headers, kase[:headers], kase[:name])
74
+ end
75
+ end
76
+
77
+ private
78
+
79
+ def extract_http1_headers(json, case_name)
80
+ http1 = fetch_by_name(json, "http1")
81
+ refute_nil http1, "case=#{case_name}: expected JSON key 'http1', got #{json.keys.inspect}"
82
+
83
+ headers = fetch_by_name(http1, "headers")
84
+ refute_nil headers, "case=#{case_name}: expected JSON key 'http1.headers'"
85
+ headers
86
+ end
87
+
88
+ def fetch_by_name(hash_like, key_name)
89
+ return hash_like[key_name] if hash_like.respond_to?(:key?) && hash_like.key?(key_name)
90
+ return hash_like[key_name.to_sym] if hash_like.respond_to?(:key?) && hash_like.key?(key_name.to_sym)
91
+
92
+ pair = hash_like.find { |k, _| k.to_s == key_name }
93
+ pair&.last
94
+ end
95
+
96
+ def assert_header_order(echoed_headers, ordered_names, case_name)
97
+ echoed_keys = echoed_headers.keys
98
+ positions = ordered_names.map do |expected_name|
99
+ index = echoed_keys.index(expected_name)
100
+ refute_nil index, "case=#{case_name}: expected header to exist in echo: #{expected_name}"
101
+ index
102
+ end
103
+
104
+ assert_equal positions.sort, positions,
105
+ "case=#{case_name}: expected header order #{ordered_names.inspect}, got keys #{echoed_keys.inspect}"
106
+ end
107
+
108
+ def assert_header_values(echoed_headers, expected_headers, case_name)
109
+ expected_headers.each do |name, expected_value|
110
+ assert echoed_headers.key?(name),
111
+ "case=#{case_name}: expected exact-case header name #{name}, got #{echoed_headers.keys.inspect}"
112
+ assert_equal expected_value, echoed_headers[name]
113
+ end
114
+ end
115
+ end
data/test/request_test.rb CHANGED
@@ -6,6 +6,16 @@ class WreqHttpbinTest < Minitest::Test
6
6
  @client = Wreq::Client.new(timeout: 30)
7
7
  end
8
8
 
9
+ def test_spec_http1_version
10
+ response = Wreq.get("https://tls.browserleaks.com", version: Wreq::Version::HTTP_11)
11
+ assert_equal response.version, Wreq::Version::HTTP_11
12
+ end
13
+
14
+ def test_spec_http2_version
15
+ response = Wreq.get("https://tls.browserleaks.com", version: Wreq::Version::HTTP_2)
16
+ assert_equal response.version, Wreq::Version::HTTP_2
17
+ end
18
+
9
19
  def test_module_get_method
10
20
  response = Wreq.get("http://localhost:8080/get")
11
21
  assert_equal 200, response.code
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wreq
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - SearchApi
@@ -74,6 +74,7 @@ files:
74
74
  - test/header_test.rb
75
75
  - test/inspect_test.rb
76
76
  - test/module_methods_test.rb
77
+ - test/orig_header_test.rb
77
78
  - test/request_parameters_test.rb
78
79
  - test/request_test.rb
79
80
  - test/response_test.rb