wreq 1.2.5 → 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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/Cargo.lock +20 -13
  3. data/Cargo.toml +10 -2
  4. data/crates/wreq-util/src/emulate/macros.rs +12 -10
  5. data/crates/wreq-util/src/emulate/profile/chrome/header.rs +18 -3
  6. data/crates/wreq-util/src/emulate/profile/firefox/header.rs +16 -0
  7. data/crates/wreq-util/src/emulate/profile/opera/header.rs +6 -7
  8. data/crates/wreq-util/tests/client.rs +103 -1
  9. data/lib/wreq.rb +70 -45
  10. data/lib/wreq_ruby/body.rb +29 -11
  11. data/lib/wreq_ruby/client.rb +88 -55
  12. data/lib/wreq_ruby/cookie.rb +79 -19
  13. data/lib/wreq_ruby/emulate.rb +74 -6
  14. data/lib/wreq_ruby/error.rb +5 -7
  15. data/lib/wreq_ruby/http.rb +74 -0
  16. data/lib/wreq_ruby/response.rb +3 -0
  17. data/script/build_windows_gnu.ps1 +6 -0
  18. data/src/arch.rs +22 -0
  19. data/src/client/body/form.rs +2 -0
  20. data/src/client/body/json.rs +47 -14
  21. data/src/client/body/stream.rs +147 -43
  22. data/src/client/body.rs +11 -15
  23. data/src/client/param.rs +7 -7
  24. data/src/client/req.rs +87 -47
  25. data/src/client/resp.rs +23 -24
  26. data/src/client.rs +310 -230
  27. data/src/cookie.rs +280 -87
  28. data/src/emulate.rs +85 -50
  29. data/src/error.rs +198 -41
  30. data/src/extractor.rs +16 -76
  31. data/src/header.rs +146 -23
  32. data/src/http.rs +62 -33
  33. data/src/lib.rs +26 -20
  34. data/src/macros.rs +71 -46
  35. data/src/options.rs +284 -0
  36. data/src/rt.rs +22 -11
  37. data/src/serde/de/array_deserializer.rs +48 -0
  38. data/src/serde/de/array_enumerator.rs +59 -0
  39. data/src/serde/de/deserializer.rs +307 -0
  40. data/src/serde/de/enum_deserializer.rs +47 -0
  41. data/src/serde/de/hash_deserializer.rs +89 -0
  42. data/src/serde/de/number_deserializer.rs +51 -0
  43. data/src/serde/de/variant_deserializer.rs +101 -0
  44. data/src/serde/de.rs +33 -0
  45. data/src/serde/error.rs +146 -0
  46. data/src/serde/ser/enums.rs +14 -0
  47. data/src/serde/ser/map_serializer.rs +56 -0
  48. data/src/serde/ser/seq_serializer.rs +71 -0
  49. data/src/serde/ser/serializer.rs +194 -0
  50. data/src/serde/ser/struct_serializer.rs +106 -0
  51. data/src/serde/ser/struct_variant_serializer.rs +44 -0
  52. data/src/serde/ser/tuple_variant_serializer.rs +41 -0
  53. data/src/serde/ser.rs +18 -0
  54. data/src/serde/tests.rs +237 -0
  55. data/src/serde.rs +202 -0
  56. data/test/body_sender_test.rb +246 -0
  57. data/test/cookie_test.rb +211 -10
  58. data/test/json_precision_test.rb +209 -0
  59. data/test/option_validation_test.rb +311 -0
  60. data/test/value_semantics_test.rb +238 -0
  61. metadata +25 -2
  62. data/src/header/helper.rs +0 -112
data/src/client/param.rs CHANGED
@@ -1,19 +1,19 @@
1
+ use ::serde::{Deserialize, Serialize};
1
2
  use indexmap::IndexMap;
2
- use serde::{Deserialize, Serialize};
3
3
 
4
- /// Represents HTTP parameters from Python as either a mapping or a sequence of key-value pairs.
4
+ /// HTTP parameters represented as an insertion-ordered Ruby mapping.
5
5
  pub type Params = IndexMap<String, ParamValue>;
6
6
 
7
- /// Represents a single parameter value that can be automatically converted from Python types.
7
+ /// A scalar Ruby value accepted in query-string and form mappings.
8
8
  #[derive(Serialize, Deserialize)]
9
9
  #[serde(untagged)]
10
10
  pub enum ParamValue {
11
- /// A boolean value from Python `bool`.
11
+ /// A Ruby `true` or `false` value.
12
12
  Boolean(bool),
13
- /// An integer value from Python `int`.
13
+ /// A Ruby Integer that fits in the native pointer-sized range.
14
14
  Number(isize),
15
- /// A floating-point value from Python `float`.
15
+ /// A Ruby Float.
16
16
  Float64(f64),
17
- /// A string value from Python `str`.
17
+ /// A Ruby String or Symbol.
18
18
  String(String),
19
19
  }
data/src/client/req.rs CHANGED
@@ -1,19 +1,21 @@
1
1
  use std::{net::IpAddr, time::Duration};
2
2
 
3
+ use ::serde::Deserialize;
3
4
  use http::header;
4
5
  use magnus::{RHash, TryConvert, typed_data::Obj, value::ReprValue};
5
- use serde::Deserialize;
6
6
  use wreq::{Client, Proxy};
7
7
 
8
- use super::body::{Body, Form, Json};
8
+ use super::body::{Body, form::Form, json::Json};
9
9
  use crate::{
10
+ arch::SUPPORTS_INTERFACE,
10
11
  client::{query::Query, resp::Response},
11
12
  cookie::Cookies,
12
13
  emulate::Emulation,
13
- error::wreq_error_to_magnus,
14
+ error::wreq_error,
14
15
  extractor::Extractor,
15
16
  header::{Headers, OrigHeaders},
16
17
  http::{Method, Version},
18
+ options::{NativeOption, Options},
17
19
  rt,
18
20
  };
19
21
 
@@ -22,12 +24,12 @@ use crate::{
22
24
  #[non_exhaustive]
23
25
  pub struct Request {
24
26
  /// The emulation option for the request.
25
- #[serde(skip)]
26
- emulation: Option<Emulation>,
27
+ #[serde(default)]
28
+ emulation: NativeOption<Emulation>,
27
29
 
28
30
  /// The proxy to use for the request.
29
- #[serde(skip)]
30
- proxy: Option<Proxy>,
31
+ #[serde(default)]
32
+ proxy: NativeOption<Proxy>,
31
33
 
32
34
  /// Bind to a local IP Address.
33
35
  local_address: Option<IpAddr>,
@@ -43,23 +45,23 @@ pub struct Request {
43
45
  read_timeout: Option<u64>,
44
46
 
45
47
  /// The HTTP version to use for the request.
46
- #[serde(skip)]
47
- version: Option<Version>,
48
+ #[serde(default)]
49
+ version: NativeOption<Version>,
48
50
 
49
51
  /// The option enables default headers.
50
52
  default_headers: Option<bool>,
51
53
 
52
54
  /// The headers to use for the request.
53
- #[serde(skip)]
54
- headers: Option<Headers>,
55
+ #[serde(default)]
56
+ headers: NativeOption<Headers>,
55
57
 
56
58
  /// The original headers to use for the request.
57
- #[serde(skip)]
58
- orig_headers: Option<OrigHeaders>,
59
+ #[serde(default)]
60
+ orig_headers: NativeOption<OrigHeaders>,
59
61
 
60
62
  /// The cookies to use for the request.
61
- #[serde(skip)]
62
- cookies: Option<Cookies>,
63
+ #[serde(default)]
64
+ cookies: NativeOption<Cookies>,
63
65
 
64
66
  /// Whether to allow redirects.
65
67
  allow_redirects: Option<bool>,
@@ -95,57 +97,95 @@ pub struct Request {
95
97
  form: Option<Form>,
96
98
 
97
99
  /// The JSON body to use for the request.
98
- json: Option<Json>,
100
+ #[serde(default)]
101
+ json: NativeOption<Json>,
99
102
 
100
103
  /// The body to use for the request.
101
- #[serde(skip)]
102
- body: Option<Body>,
104
+ #[serde(default)]
105
+ body: NativeOption<Body>,
103
106
  }
104
107
 
105
108
  impl Request {
106
109
  /// Create a new [`Request`] from Ruby keyword arguments.
110
+ ///
111
+ /// # Errors
112
+ ///
113
+ /// Returns before network I/O for unknown, duplicate, unsupported,
114
+ /// conflicting, ineffective, or invalid option values.
107
115
  pub fn new(ruby: &magnus::Ruby, hash: RHash) -> Result<Self, magnus::Error> {
108
116
  let keyword = hash.as_value();
109
- let mut builder: Self = serde_magnus::deserialize(ruby, keyword)?;
110
-
111
- if let Some(v) = hash.get(ruby.to_symbol(stringify!(emulation))) {
112
- let obj = Obj::<Emulation>::try_convert(v)?;
113
- builder.emulation = Some((*obj).clone());
114
- }
115
-
116
- if let Some(v) = hash.get(ruby.to_symbol(stringify!(version))) {
117
- builder.version = Some(Version::try_convert(v)?);
118
- }
119
-
120
- if let Some(v) = hash.get(ruby.to_symbol(stringify!(headers))) {
121
- builder.headers = Some(Headers::try_convert(v)?);
122
- }
123
-
124
- if let Some(v) = hash.get(ruby.to_symbol(stringify!(orig_headers))) {
125
- builder.orig_headers = Some(OrigHeaders::try_convert(v)?);
126
- }
127
-
128
- if let Some(v) = hash.get(ruby.to_symbol(stringify!(cookies))) {
129
- builder.cookies = Some(Cookies::try_convert(v)?);
130
- }
131
-
132
- if let Some(v) = hash.get(ruby.to_symbol(stringify!(body))) {
133
- builder.body = Some(Body::try_convert(v)?);
134
- }
135
-
136
- builder.proxy = Extractor::<Proxy>::try_convert(keyword)?.into_inner();
117
+ let options = Options::new(ruby, hash);
118
+ let mut builder = Self::deserialize_options(&options)?;
119
+ options
120
+ .validator()
121
+ .require_when_present(
122
+ stringify!(max_redirects),
123
+ builder.max_redirects.is_some(),
124
+ builder.allow_redirects == Some(true),
125
+ ":allow_redirects to be true",
126
+ )
127
+ .finish()?;
128
+
129
+ extract_native_option!(
130
+ options,
131
+ builder,
132
+ emulation,
133
+ Obj<Emulation> => |value| (*value).clone()
134
+ );
135
+ extract_native_option!(options, builder, version);
136
+ extract_native_option!(options, builder, headers);
137
+ extract_native_option!(options, builder, orig_headers);
138
+ extract_native_option!(options, builder, cookies);
139
+ extract_native_option!(options, builder, json, present);
140
+ builder
141
+ .proxy
142
+ .set(Extractor::<Proxy>::try_convert(keyword)?.into_inner());
143
+ extract_native_option!(options, builder, body);
137
144
 
138
145
  Ok(builder)
139
146
  }
147
+
148
+ /// Validate pre-conversion rules and deserialize the request options.
149
+ ///
150
+ /// # Errors
151
+ ///
152
+ /// Returns `ArgumentError` for failed rules or the Ruby conversion error
153
+ /// produced by an invalid option value.
154
+ fn deserialize_options(options: &Options<'_>) -> Result<Self, magnus::Error> {
155
+ options
156
+ .validate_keys::<Self>()?
157
+ .validator()
158
+ .reject_unsupported(stringify!(interface), SUPPORTS_INTERFACE)
159
+ .reject_conflicts([
160
+ (stringify!(body), options.is_non_nil(stringify!(body))),
161
+ (stringify!(form), options.is_non_nil(stringify!(form))),
162
+ (stringify!(json), options.is_present(stringify!(json))),
163
+ ])
164
+ .reject_conflicts([
165
+ (stringify!(auth), options.is_non_nil(stringify!(auth))),
166
+ (
167
+ stringify!(bearer_auth),
168
+ options.is_non_nil(stringify!(bearer_auth)),
169
+ ),
170
+ (
171
+ stringify!(basic_auth),
172
+ options.is_non_nil(stringify!(basic_auth)),
173
+ ),
174
+ ])
175
+ .finish()?
176
+ .deserialize::<Self>()
177
+ }
140
178
  }
141
179
 
142
180
  pub fn execute_request<U: AsRef<str>>(
181
+ ruby: &magnus::Ruby,
143
182
  client: Client,
144
183
  method: Method,
145
184
  url: U,
146
185
  mut request: Request,
147
186
  ) -> Result<Response, magnus::Error> {
148
187
  rt::try_block_on(
188
+ ruby,
149
189
  async move {
150
190
  let mut builder = client.request(method.into_ffi(), url.as_ref());
151
191
 
@@ -269,6 +309,6 @@ pub fn execute_request<U: AsRef<str>>(
269
309
  // Send request.
270
310
  builder.send().await.map(Response::new)
271
311
  },
272
- wreq_error_to_magnus,
312
+ wreq_error,
273
313
  )
274
314
  }
data/src/client/resp.rs CHANGED
@@ -9,10 +9,10 @@ use magnus::{Error, Module, RArray, RModule, Ruby, Value, scan_args::scan_args};
9
9
  use wreq::Uri;
10
10
 
11
11
  use crate::{
12
- client::body::{BodyReceiver, Json},
12
+ client::body::{json::Json, stream::BodyReceiver},
13
13
  cookie::Cookie,
14
- error::{memory_error, no_block_given_error, wreq_error_to_magnus},
15
- gvl::{self, nogvl},
14
+ error::{memory_error, no_block_given_error, wreq_error},
15
+ gvl,
16
16
  header::Headers,
17
17
  http::{StatusCode, Version},
18
18
  rt,
@@ -64,7 +64,7 @@ impl Response {
64
64
  }
65
65
 
66
66
  /// Internal method to get the wreq::Response, optionally streaming the body.
67
- fn response(&self, stream: bool) -> Result<wreq::Response, Error> {
67
+ fn response(&self, ruby: &Ruby, stream: bool) -> Result<wreq::Response, Error> {
68
68
  let build_response = |body: wreq::Body| -> wreq::Response {
69
69
  let mut response = HttpResponse::new(body);
70
70
  *response.version_mut() = self.version.into_ffi();
@@ -81,8 +81,9 @@ impl Response {
81
81
  Ok(build_response(body))
82
82
  } else {
83
83
  let bytes = rt::try_block_on(
84
+ ruby,
84
85
  BodyExt::collect(body).map_ok(|buf| buf.to_bytes()),
85
- wreq_error_to_magnus,
86
+ wreq_error,
86
87
  )?;
87
88
 
88
89
  self.body
@@ -103,7 +104,7 @@ impl Response {
103
104
  };
104
105
  }
105
106
 
106
- Err(memory_error())
107
+ Err(memory_error(ruby))
107
108
  }
108
109
  }
109
110
 
@@ -167,44 +168,42 @@ impl Response {
167
168
  }
168
169
 
169
170
  /// Get the response body as bytes.
170
- pub fn bytes(&self) -> Result<Bytes, Error> {
171
- let response = self.response(false)?;
172
- rt::try_block_on(response.bytes(), wreq_error_to_magnus)
171
+ pub fn bytes(ruby: &Ruby, rb_self: &Self) -> Result<Bytes, Error> {
172
+ let response = rb_self.response(ruby, false)?;
173
+ rt::try_block_on(ruby, response.bytes(), wreq_error)
173
174
  }
174
175
 
175
176
  /// Get the full response text given a specific encoding.
176
- pub fn text(&self, args: &[Value]) -> Result<String, Error> {
177
+ pub fn text(ruby: &Ruby, rb_self: &Self, args: &[Value]) -> Result<String, Error> {
177
178
  let args = scan_args::<(), (Option<String>,), (), (), (), ()>(args)?;
178
- let response = self.response(false)?;
179
+ let response = rb_self.response(ruby, false)?;
179
180
  match args.optional.0 {
180
181
  Some(encoding) => {
181
- rt::try_block_on(response.text_with_charset(encoding), wreq_error_to_magnus)
182
+ rt::try_block_on(ruby, response.text_with_charset(encoding), wreq_error)
182
183
  }
183
- None => rt::try_block_on(response.text(), wreq_error_to_magnus),
184
+ None => rt::try_block_on(ruby, response.text(), wreq_error),
184
185
  }
185
186
  }
186
187
 
187
188
  /// Get the response body as JSON.
188
189
  pub fn json(ruby: &Ruby, rb_self: &Self) -> Result<Value, Error> {
189
- let response = rb_self.response(false)?;
190
- let json = rt::try_block_on(response.json::<Json>(), wreq_error_to_magnus)?;
191
- serde_magnus::serialize(ruby, &json)
190
+ let response = rb_self.response(ruby, false)?;
191
+ let json = rt::try_block_on(ruby, response.json::<Json>(), wreq_error)?;
192
+ crate::serde::serialize(ruby, &json)
192
193
  }
193
194
 
194
195
  /// Yield response body chunks to the given Ruby block.
195
196
  pub fn chunks(ruby: &Ruby, rb_self: &Self) -> Result<(), Error> {
196
197
  if !ruby.block_given() {
197
- return Err(no_block_given_error());
198
+ return Err(no_block_given_error(ruby));
198
199
  }
199
200
 
200
- let receiver = nogvl(|| {
201
- rb_self
202
- .response(true)
203
- .map(wreq::Response::bytes_stream)
204
- .map(BodyReceiver::new)
205
- })?;
201
+ let receiver = rb_self
202
+ .response(ruby, true)
203
+ .map(wreq::Response::bytes_stream)
204
+ .map(BodyReceiver::new)?;
206
205
 
207
- while let Some(chunk) = receiver.next()? {
206
+ while let Some(chunk) = receiver.next(ruby)? {
208
207
  let _: Value = ruby.yield_value(chunk)?;
209
208
  }
210
209