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/src/emulate.rs
CHANGED
|
@@ -1,11 +1,47 @@
|
|
|
1
|
-
use
|
|
2
|
-
|
|
3
|
-
typed_data::{Inspect, Obj},
|
|
4
|
-
};
|
|
1
|
+
use ::serde::Deserialize;
|
|
2
|
+
use magnus::{Error, Module, Object, RModule, Ruby, Value, function, method, typed_data::Obj};
|
|
5
3
|
|
|
4
|
+
use crate::options::{NativeOption, Options};
|
|
5
|
+
|
|
6
|
+
/// Keyword arguments accepted by `Wreq::Emulation.new`.
|
|
7
|
+
#[derive(Default, Deserialize)]
|
|
8
|
+
struct Builder {
|
|
9
|
+
/// The browser profile to emulate.
|
|
10
|
+
#[serde(default)]
|
|
11
|
+
profile: NativeOption<Obj<Profile>>,
|
|
12
|
+
|
|
13
|
+
/// The operating-system profile to emulate.
|
|
14
|
+
#[serde(default)]
|
|
15
|
+
platform: NativeOption<Obj<Platform>>,
|
|
16
|
+
|
|
17
|
+
/// Whether HTTP/2 settings are emulated.
|
|
18
|
+
http2: Option<bool>,
|
|
19
|
+
|
|
20
|
+
/// Whether browser headers are emulated.
|
|
21
|
+
headers: Option<bool>,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// ===== impl Builder =====
|
|
25
|
+
|
|
26
|
+
impl Builder {
|
|
27
|
+
/// Deserialize and convert one validated emulation options Hash.
|
|
28
|
+
///
|
|
29
|
+
/// # Errors
|
|
30
|
+
///
|
|
31
|
+
/// Returns `ArgumentError` for unknown or duplicate options and `TypeError`
|
|
32
|
+
/// for invalid option values.
|
|
33
|
+
fn from_options(options: Options<'_>) -> Result<Self, Error> {
|
|
34
|
+
let mut builder = options.validate_keys::<Self>()?.deserialize::<Self>()?;
|
|
35
|
+
extract_native_option!(options, builder, profile);
|
|
36
|
+
extract_native_option!(options, builder, platform);
|
|
37
|
+
Ok(builder)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Defines constant registration, `into_ffi`/`from_ffi`, and handlers for
|
|
42
|
+
// Ruby's `to_s`, `==`, `eql?`, and `hash` methods.
|
|
6
43
|
define_ruby_enum!(
|
|
7
44
|
/// An emulation profile.
|
|
8
|
-
const,
|
|
9
45
|
Profile,
|
|
10
46
|
"Wreq::Profile",
|
|
11
47
|
wreq_util::Profile,
|
|
@@ -50,6 +86,7 @@ define_ruby_enum!(
|
|
|
50
86
|
Chrome147,
|
|
51
87
|
Chrome148,
|
|
52
88
|
Chrome149,
|
|
89
|
+
Chrome150,
|
|
53
90
|
|
|
54
91
|
Edge101,
|
|
55
92
|
Edge122,
|
|
@@ -149,17 +186,19 @@ define_ruby_enum!(
|
|
|
149
186
|
Opera131,
|
|
150
187
|
);
|
|
151
188
|
|
|
189
|
+
// Defines constant registration, `into_ffi`/`from_ffi`, and handlers for
|
|
190
|
+
// Ruby's `to_s`, `to_sym`, `==`, `eql?`, and `hash` methods.
|
|
152
191
|
define_ruby_enum!(
|
|
153
192
|
/// An emulation profile for OS.
|
|
154
|
-
const,
|
|
155
193
|
Platform,
|
|
156
194
|
"Wreq::Platform",
|
|
157
195
|
wreq_util::Platform,
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
196
|
+
symbols:
|
|
197
|
+
Windows => "windows",
|
|
198
|
+
MacOS => "macos",
|
|
199
|
+
Linux => "linux",
|
|
200
|
+
Android => "android",
|
|
201
|
+
IOS => "ios",
|
|
163
202
|
);
|
|
164
203
|
|
|
165
204
|
/// A struct to represent the `EmulationOption` class.
|
|
@@ -167,51 +206,41 @@ define_ruby_enum!(
|
|
|
167
206
|
#[magnus::wrap(class = "Wreq::Emulation", free_immediately, size)]
|
|
168
207
|
pub struct Emulation(pub wreq_util::Emulation);
|
|
169
208
|
|
|
170
|
-
// ===== impl Profile =====
|
|
171
|
-
|
|
172
|
-
impl Profile {
|
|
173
|
-
pub fn to_s(&self) -> String {
|
|
174
|
-
self.into_ffi().inspect()
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// ===== impl Platform =====
|
|
179
|
-
|
|
180
|
-
impl Platform {
|
|
181
|
-
pub fn to_s(&self) -> String {
|
|
182
|
-
self.into_ffi().inspect()
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
209
|
// ===== impl Emulation =====
|
|
187
210
|
|
|
188
211
|
impl Emulation {
|
|
212
|
+
/// Create emulation settings from one optional Hash.
|
|
213
|
+
///
|
|
214
|
+
/// Unknown keys, non-Hash arguments, and extra positional arguments are
|
|
215
|
+
/// rejected before the native emulation value is built.
|
|
216
|
+
///
|
|
217
|
+
/// # Errors
|
|
218
|
+
///
|
|
219
|
+
/// Returns `ArgumentError` for unknown keys or argument count and
|
|
220
|
+
/// `TypeError` for invalid option values.
|
|
189
221
|
fn new(ruby: &Ruby, args: &[Value]) -> Result<Self, Error> {
|
|
190
|
-
let mut
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
if let Some(hash) = args.first().and_then(|v| RHash::from_value(*v)) {
|
|
196
|
-
if let Some(v) = hash.get(ruby.to_symbol(stringify!(profile))) {
|
|
197
|
-
profile = Some(Obj::<Profile>::try_convert(v)?);
|
|
198
|
-
}
|
|
199
|
-
if let Some(v) = hash.get(ruby.to_symbol(stringify!(platform))) {
|
|
200
|
-
platform = Some(Obj::<Platform>::try_convert(v)?);
|
|
201
|
-
}
|
|
202
|
-
if let Some(v) = hash.get(ruby.to_symbol(stringify!(http2))) {
|
|
203
|
-
http2 = Some(bool::try_convert(v)?);
|
|
204
|
-
}
|
|
205
|
-
if let Some(v) = hash.get(ruby.to_symbol(stringify!(headers))) {
|
|
206
|
-
headers = Some(bool::try_convert(v)?);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
222
|
+
let mut params = Options::from_args(ruby, args, "emulation")?
|
|
223
|
+
.map(Builder::from_options)
|
|
224
|
+
.transpose()?
|
|
225
|
+
.unwrap_or_default();
|
|
209
226
|
|
|
210
227
|
let emulation = wreq_util::Emulation::builder()
|
|
211
|
-
.profile(
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
228
|
+
.profile(
|
|
229
|
+
params
|
|
230
|
+
.profile
|
|
231
|
+
.take()
|
|
232
|
+
.map(|obj| obj.into_ffi())
|
|
233
|
+
.unwrap_or_default(),
|
|
234
|
+
)
|
|
235
|
+
.platform(
|
|
236
|
+
params
|
|
237
|
+
.platform
|
|
238
|
+
.take()
|
|
239
|
+
.map(|os| os.into_ffi())
|
|
240
|
+
.unwrap_or_default(),
|
|
241
|
+
)
|
|
242
|
+
.http2(params.http2.unwrap_or(true))
|
|
243
|
+
.headers(params.headers.unwrap_or(true))
|
|
215
244
|
.build();
|
|
216
245
|
|
|
217
246
|
Ok(Self(emulation))
|
|
@@ -222,11 +251,18 @@ pub fn include(ruby: &Ruby, gem_module: &RModule) -> Result<(), Error> {
|
|
|
222
251
|
// Profile enum binding
|
|
223
252
|
let profile = gem_module.define_class("Profile", ruby.class_object())?;
|
|
224
253
|
profile.define_method("to_s", method!(Profile::to_s, 0))?;
|
|
254
|
+
profile.define_method("==", method!(Profile::equals, 1))?;
|
|
255
|
+
profile.define_method("eql?", method!(Profile::is_eql, 1))?;
|
|
256
|
+
profile.define_method("hash", method!(Profile::hash_value, 0))?;
|
|
225
257
|
Profile::define_constants(profile)?;
|
|
226
258
|
|
|
227
259
|
// Platform enum binding
|
|
228
260
|
let platform = gem_module.define_class("Platform", ruby.class_object())?;
|
|
229
261
|
platform.define_method("to_s", method!(Platform::to_s, 0))?;
|
|
262
|
+
platform.define_method("to_sym", method!(Platform::to_sym, 0))?;
|
|
263
|
+
platform.define_method("==", method!(Platform::equals, 1))?;
|
|
264
|
+
platform.define_method("eql?", method!(Platform::is_eql, 1))?;
|
|
265
|
+
platform.define_method("hash", method!(Platform::hash_value, 0))?;
|
|
230
266
|
Platform::define_constants(platform)?;
|
|
231
267
|
|
|
232
268
|
// Emulation class binding
|
data/src/error.rs
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
|
+
use std::{
|
|
2
|
+
cell::{BorrowError, BorrowMutError},
|
|
3
|
+
fmt,
|
|
4
|
+
};
|
|
5
|
+
|
|
1
6
|
use magnus::{
|
|
2
|
-
Error as MagnusError, RModule, Ruby, exception::ExceptionClass, prelude::*,
|
|
7
|
+
Error as MagnusError, RModule, Ruby, error::ErrorType, exception::ExceptionClass, prelude::*,
|
|
8
|
+
value::Lazy,
|
|
3
9
|
};
|
|
4
10
|
use tokio::sync::mpsc::error::SendError;
|
|
5
11
|
|
|
@@ -17,18 +23,24 @@ Potential solutions:
|
|
|
17
23
|
3) Change the order of operations to reference the instance before borrowing it.
|
|
18
24
|
"#;
|
|
19
25
|
|
|
20
|
-
static WREQ: Lazy<RModule> = Lazy::new(|ruby| ruby.define_module(crate::RUBY_MODULE_NAME).unwrap());
|
|
21
|
-
|
|
22
26
|
macro_rules! define_exception {
|
|
23
27
|
($name:ident, $ruby_name:literal, $parent_method:ident) => {
|
|
24
28
|
static $name: Lazy<ExceptionClass> = Lazy::new(|ruby| {
|
|
25
|
-
ruby.
|
|
26
|
-
.
|
|
27
|
-
.
|
|
29
|
+
ruby.class_object()
|
|
30
|
+
.const_get::<_, RModule>(crate::RUBY_MODULE_NAME)
|
|
31
|
+
.and_then(|module| module.const_get::<_, ExceptionClass>($ruby_name))
|
|
32
|
+
.unwrap_or_else(|_| ruby.$parent_method())
|
|
28
33
|
});
|
|
29
34
|
};
|
|
30
35
|
}
|
|
31
36
|
|
|
37
|
+
macro_rules! initialize_exception {
|
|
38
|
+
($ruby:expr, $module:expr, $name:ident, $ruby_name:literal, $parent_method:ident) => {{
|
|
39
|
+
$module.define_error($ruby_name, $ruby.$parent_method())?;
|
|
40
|
+
Lazy::force(&$name, $ruby);
|
|
41
|
+
}};
|
|
42
|
+
}
|
|
43
|
+
|
|
32
44
|
macro_rules! map_wreq_error {
|
|
33
45
|
($ruby:expr, $err:expr, $msg:expr, $($check_method:ident => $exception:ident),* $(,)?) => {
|
|
34
46
|
{
|
|
@@ -72,64 +84,128 @@ define_exception!(DECODING_ERROR, "DecodingError", exception_runtime_error);
|
|
|
72
84
|
// Configuration and builder errors
|
|
73
85
|
define_exception!(BUILDER_ERROR, "BuilderError", exception_runtime_error);
|
|
74
86
|
|
|
75
|
-
// Thread interruption error
|
|
76
|
-
define_exception!(INTERRUPT_ERROR, "InterruptError", exception_interrupt);
|
|
77
|
-
|
|
78
87
|
/// Memory error constant
|
|
79
|
-
pub fn memory_error() -> MagnusError {
|
|
80
|
-
MagnusError::new(ruby
|
|
88
|
+
pub fn memory_error(ruby: &Ruby) -> MagnusError {
|
|
89
|
+
MagnusError::new(ruby.get_inner(&MEMORY), RACE_CONDITION_ERROR_MSG)
|
|
81
90
|
}
|
|
82
91
|
|
|
83
|
-
///
|
|
84
|
-
pub fn interrupt_error() -> MagnusError {
|
|
85
|
-
MagnusError::new(ruby
|
|
92
|
+
/// Create Ruby's standard thread interruption error.
|
|
93
|
+
pub fn interrupt_error(ruby: &Ruby) -> MagnusError {
|
|
94
|
+
MagnusError::new(ruby.exception_interrupt(), "request interrupted")
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/// Map a Tokio runtime initialization failure to `Wreq::BuilderError`.
|
|
98
|
+
pub fn runtime_initialization_error(ruby: &Ruby, err: &std::io::Error) -> MagnusError {
|
|
99
|
+
MagnusError::new(
|
|
100
|
+
ruby.get_inner(&BUILDER_ERROR),
|
|
101
|
+
format!("failed to initialize Tokio runtime: {err}"),
|
|
102
|
+
)
|
|
86
103
|
}
|
|
87
104
|
|
|
88
105
|
/// LocalJumpError for methods that require a Ruby block.
|
|
89
|
-
pub fn no_block_given_error() -> MagnusError {
|
|
106
|
+
pub fn no_block_given_error(ruby: &Ruby) -> MagnusError {
|
|
107
|
+
MagnusError::new(ruby.exception_local_jump_error(), "no block given (yield)")
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/// Build an `IOError` for writes to a closed request-body sender.
|
|
111
|
+
pub fn closed_body_sender_error(ruby: &Ruby) -> MagnusError {
|
|
112
|
+
MagnusError::new(ruby.exception_io_error(), "closed body sender")
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/// Map a failed body-channel send to `IOError`.
|
|
116
|
+
pub fn body_sender_send_error<T>(ruby: &Ruby, err: SendError<T>) -> MagnusError {
|
|
117
|
+
MagnusError::new(
|
|
118
|
+
ruby.exception_io_error(),
|
|
119
|
+
format!("closed body sender: {err}"),
|
|
120
|
+
)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/// Map an immutable sender-state borrow failure to `Wreq::BodyError`.
|
|
124
|
+
pub fn body_sender_borrow_error(ruby: &Ruby, err: BorrowError) -> MagnusError {
|
|
90
125
|
MagnusError::new(
|
|
91
|
-
ruby
|
|
92
|
-
"
|
|
126
|
+
ruby.get_inner(&BODY_ERROR),
|
|
127
|
+
format!("body sender state is unavailable: {err}"),
|
|
93
128
|
)
|
|
94
129
|
}
|
|
95
130
|
|
|
96
|
-
/// Map
|
|
97
|
-
pub fn
|
|
131
|
+
/// Map a mutable sender-state borrow failure to `Wreq::BodyError`.
|
|
132
|
+
pub fn body_sender_borrow_mut_error(ruby: &Ruby, err: BorrowMutError) -> MagnusError {
|
|
98
133
|
MagnusError::new(
|
|
99
|
-
ruby
|
|
100
|
-
format!("
|
|
134
|
+
ruby.get_inner(&BODY_ERROR),
|
|
135
|
+
format!("body sender state is unavailable: {err}"),
|
|
101
136
|
)
|
|
102
137
|
}
|
|
103
138
|
|
|
104
139
|
/// Map [`wreq::header::InvalidHeaderName`] to corresponding [`magnus::Error`]
|
|
105
|
-
pub fn
|
|
140
|
+
pub fn header_name_error(ruby: &Ruby, err: wreq::header::InvalidHeaderName) -> MagnusError {
|
|
106
141
|
MagnusError::new(
|
|
107
|
-
ruby
|
|
142
|
+
ruby.get_inner(&BUILDER_ERROR),
|
|
108
143
|
format!("invalid header name: {err}"),
|
|
109
144
|
)
|
|
110
145
|
}
|
|
111
146
|
|
|
112
147
|
/// Map [`wreq::header::InvalidHeaderValue`] to corresponding [`magnus::Error`]
|
|
113
|
-
pub fn
|
|
148
|
+
pub fn header_value_error(ruby: &Ruby, err: wreq::header::InvalidHeaderValue) -> MagnusError {
|
|
114
149
|
MagnusError::new(
|
|
115
|
-
ruby
|
|
150
|
+
ruby.get_inner(&BUILDER_ERROR),
|
|
116
151
|
format!("invalid header value: {err}"),
|
|
117
152
|
)
|
|
118
153
|
}
|
|
119
154
|
|
|
120
|
-
///
|
|
121
|
-
pub fn
|
|
155
|
+
/// Build a `Wreq::BuilderError` for an invalid Ruby header structure.
|
|
156
|
+
pub fn header_type_error(ruby: &Ruby, err: &str) -> MagnusError {
|
|
157
|
+
MagnusError::new(ruby.get_inner(&BUILDER_ERROR), format!("type error: {err}"))
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/// Build a `Wreq::BuilderError` for unsupported request JSON values.
|
|
161
|
+
pub fn json_serialization_error(ruby: &Ruby, err: MagnusError) -> MagnusError {
|
|
122
162
|
MagnusError::new(
|
|
123
|
-
ruby
|
|
124
|
-
format!("
|
|
163
|
+
ruby.get_inner(&BUILDER_ERROR),
|
|
164
|
+
format!("JSON serialization error: {err}"),
|
|
125
165
|
)
|
|
126
166
|
}
|
|
127
167
|
|
|
168
|
+
/// Prefix a Magnus error while preserving its original Ruby exception class.
|
|
169
|
+
pub(crate) fn contextualize_magnus_error(
|
|
170
|
+
err: MagnusError,
|
|
171
|
+
context: fmt::Arguments<'_>,
|
|
172
|
+
) -> MagnusError {
|
|
173
|
+
match err.error_type() {
|
|
174
|
+
ErrorType::Error(class, message) => {
|
|
175
|
+
MagnusError::new(*class, format!("{context}: {message}"))
|
|
176
|
+
}
|
|
177
|
+
ErrorType::Exception(exception) => MagnusError::new(
|
|
178
|
+
exception.exception_class(),
|
|
179
|
+
format!("{context}: {exception}"),
|
|
180
|
+
),
|
|
181
|
+
ErrorType::Jump(_) => err,
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/// Add an option name while preserving the original Ruby exception class.
|
|
186
|
+
pub fn option_value_error(option: &str, err: MagnusError) -> MagnusError {
|
|
187
|
+
contextualize_magnus_error(err, format_args!("invalid value for :{option}"))
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/// Build an `ArgumentError` from a validation message.
|
|
191
|
+
pub fn argument_error(ruby: &Ruby, message: impl Into<String>) -> MagnusError {
|
|
192
|
+
MagnusError::new(ruby.exception_arg_error(), message.into())
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/// Builds a Ruby `RangeError` from a validation message.
|
|
196
|
+
pub fn range_error(ruby: &Ruby, message: impl Into<String>) -> MagnusError {
|
|
197
|
+
MagnusError::new(ruby.exception_range_error(), message.into())
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/// Build a `TypeError` from a conversion message.
|
|
201
|
+
pub fn type_error(ruby: &Ruby, message: impl Into<String>) -> MagnusError {
|
|
202
|
+
MagnusError::new(ruby.exception_type_error(), message.into())
|
|
203
|
+
}
|
|
128
204
|
/// Map [`wreq::Error`] to corresponding [`magnus::Error`]
|
|
129
|
-
pub fn
|
|
205
|
+
pub fn wreq_error(ruby: &Ruby, err: wreq::Error) -> MagnusError {
|
|
130
206
|
let error_msg = err.to_string();
|
|
131
207
|
map_wreq_error!(
|
|
132
|
-
ruby
|
|
208
|
+
ruby,
|
|
133
209
|
err,
|
|
134
210
|
error_msg,
|
|
135
211
|
is_builder => BUILDER_ERROR,
|
|
@@ -146,18 +222,95 @@ pub fn wreq_error_to_magnus(err: wreq::Error) -> MagnusError {
|
|
|
146
222
|
)
|
|
147
223
|
}
|
|
148
224
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
225
|
+
/// Define and retain the Ruby exception classes used by the binding.
|
|
226
|
+
///
|
|
227
|
+
/// # Errors
|
|
228
|
+
///
|
|
229
|
+
/// Returns the Ruby exception raised while defining an error class.
|
|
230
|
+
pub fn include(ruby: &Ruby, gem_module: &RModule) -> Result<(), MagnusError> {
|
|
231
|
+
initialize_exception!(
|
|
232
|
+
ruby,
|
|
233
|
+
gem_module,
|
|
234
|
+
MEMORY,
|
|
235
|
+
"MemoryError",
|
|
236
|
+
exception_runtime_error
|
|
237
|
+
);
|
|
238
|
+
initialize_exception!(
|
|
239
|
+
ruby,
|
|
240
|
+
gem_module,
|
|
241
|
+
CONNECTION_ERROR,
|
|
242
|
+
"ConnectionError",
|
|
243
|
+
exception_runtime_error
|
|
244
|
+
);
|
|
245
|
+
initialize_exception!(
|
|
246
|
+
ruby,
|
|
247
|
+
gem_module,
|
|
248
|
+
PROXY_CONNECTION_ERROR,
|
|
249
|
+
"ProxyConnectionError",
|
|
250
|
+
exception_runtime_error
|
|
251
|
+
);
|
|
252
|
+
initialize_exception!(
|
|
253
|
+
ruby,
|
|
254
|
+
gem_module,
|
|
255
|
+
CONNECTION_RESET_ERROR,
|
|
256
|
+
"ConnectionResetError",
|
|
257
|
+
exception_runtime_error
|
|
258
|
+
);
|
|
259
|
+
initialize_exception!(
|
|
260
|
+
ruby,
|
|
261
|
+
gem_module,
|
|
262
|
+
TLS_ERROR,
|
|
263
|
+
"TlsError",
|
|
264
|
+
exception_runtime_error
|
|
265
|
+
);
|
|
266
|
+
initialize_exception!(
|
|
267
|
+
ruby,
|
|
268
|
+
gem_module,
|
|
269
|
+
REQUEST_ERROR,
|
|
270
|
+
"RequestError",
|
|
271
|
+
exception_runtime_error
|
|
272
|
+
);
|
|
273
|
+
initialize_exception!(
|
|
274
|
+
ruby,
|
|
275
|
+
gem_module,
|
|
276
|
+
STATUS_ERROR,
|
|
277
|
+
"StatusError",
|
|
278
|
+
exception_runtime_error
|
|
279
|
+
);
|
|
280
|
+
initialize_exception!(
|
|
281
|
+
ruby,
|
|
282
|
+
gem_module,
|
|
283
|
+
REDIRECT_ERROR,
|
|
284
|
+
"RedirectError",
|
|
285
|
+
exception_runtime_error
|
|
286
|
+
);
|
|
287
|
+
initialize_exception!(
|
|
288
|
+
ruby,
|
|
289
|
+
gem_module,
|
|
290
|
+
TIMEOUT_ERROR,
|
|
291
|
+
"TimeoutError",
|
|
292
|
+
exception_runtime_error
|
|
293
|
+
);
|
|
294
|
+
initialize_exception!(
|
|
295
|
+
ruby,
|
|
296
|
+
gem_module,
|
|
297
|
+
BODY_ERROR,
|
|
298
|
+
"BodyError",
|
|
299
|
+
exception_runtime_error
|
|
300
|
+
);
|
|
301
|
+
initialize_exception!(
|
|
302
|
+
ruby,
|
|
303
|
+
gem_module,
|
|
304
|
+
DECODING_ERROR,
|
|
305
|
+
"DecodingError",
|
|
306
|
+
exception_runtime_error
|
|
307
|
+
);
|
|
308
|
+
initialize_exception!(
|
|
309
|
+
ruby,
|
|
310
|
+
gem_module,
|
|
311
|
+
BUILDER_ERROR,
|
|
312
|
+
"BuilderError",
|
|
313
|
+
exception_runtime_error
|
|
314
|
+
);
|
|
315
|
+
Ok(())
|
|
163
316
|
}
|
data/src/extractor.rs
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
use magnus::{
|
|
2
|
-
use wreq::
|
|
3
|
-
Proxy,
|
|
4
|
-
header::{HeaderMap, HeaderName, HeaderValue, OrigHeaderMap},
|
|
5
|
-
};
|
|
1
|
+
use magnus::{RHash, RString, Ruby, TryConvert, value::ReprValue};
|
|
2
|
+
use wreq::Proxy;
|
|
6
3
|
|
|
7
|
-
use crate::error::{
|
|
8
|
-
|
|
9
|
-
};
|
|
4
|
+
use crate::error::{option_value_error, wreq_error};
|
|
5
|
+
use crate::options;
|
|
10
6
|
|
|
11
7
|
/// A trait that defines the parameter name for extraction.
|
|
12
8
|
pub trait ExtractorName {
|
|
@@ -32,65 +28,6 @@ where
|
|
|
32
28
|
}
|
|
33
29
|
}
|
|
34
30
|
|
|
35
|
-
// ===== impl Extractor<HeaderMap> =====
|
|
36
|
-
|
|
37
|
-
impl ExtractorName for HeaderMap {
|
|
38
|
-
const NAME: &str = "headers";
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
impl TryConvert for Extractor<HeaderMap> {
|
|
42
|
-
fn try_convert(value: magnus::Value) -> Result<Self, magnus::Error> {
|
|
43
|
-
let ruby = Ruby::get_with(value);
|
|
44
|
-
let keyword = RHash::try_convert(value)?;
|
|
45
|
-
let mut headers = HeaderMap::new();
|
|
46
|
-
|
|
47
|
-
if let Some(hash) = keyword
|
|
48
|
-
.get(ruby.to_symbol(HeaderMap::NAME))
|
|
49
|
-
.and_then(RHash::from_value)
|
|
50
|
-
{
|
|
51
|
-
hash.foreach(|name: RString, value: RString| {
|
|
52
|
-
let name = HeaderName::from_bytes(&name.to_bytes())
|
|
53
|
-
.map_err(header_name_error_to_magnus)?;
|
|
54
|
-
let value = HeaderValue::from_maybe_shared(value.to_bytes())
|
|
55
|
-
.map_err(header_value_error_to_magnus)?;
|
|
56
|
-
headers.insert(name, value);
|
|
57
|
-
|
|
58
|
-
Ok(ForEach::Continue)
|
|
59
|
-
})?;
|
|
60
|
-
|
|
61
|
-
return Ok(Extractor(Some(headers)));
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
Ok(Extractor(None))
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// ===== impl Extractor<OrigHeaderMap> =====
|
|
69
|
-
|
|
70
|
-
impl ExtractorName for OrigHeaderMap {
|
|
71
|
-
const NAME: &str = "orig_headers";
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
impl TryConvert for Extractor<OrigHeaderMap> {
|
|
75
|
-
fn try_convert(value: magnus::Value) -> Result<Self, magnus::Error> {
|
|
76
|
-
let ruby = Ruby::get_with(value);
|
|
77
|
-
let keyword = RHash::try_convert(value)?;
|
|
78
|
-
|
|
79
|
-
if let Some(orig_headers) = keyword
|
|
80
|
-
.get(ruby.to_symbol(OrigHeaderMap::NAME))
|
|
81
|
-
.and_then(RArray::from_value)
|
|
82
|
-
{
|
|
83
|
-
let mut map = OrigHeaderMap::new();
|
|
84
|
-
for value in orig_headers.into_iter().flat_map(RString::from_value) {
|
|
85
|
-
map.insert(value.to_bytes());
|
|
86
|
-
}
|
|
87
|
-
return Ok(Extractor(Some(map)));
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
Ok(Extractor(None))
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
31
|
// ===== impl Extractor<Proxy> =====
|
|
95
32
|
|
|
96
33
|
impl ExtractorName for Proxy {
|
|
@@ -102,16 +39,19 @@ impl TryConvert for Extractor<Proxy> {
|
|
|
102
39
|
let ruby = Ruby::get_with(value);
|
|
103
40
|
let rhash = RHash::try_convert(value)?;
|
|
104
41
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
{
|
|
109
|
-
return
|
|
110
|
-
.map(Some)
|
|
111
|
-
.map(Extractor)
|
|
112
|
-
.map_err(wreq_error_to_magnus);
|
|
42
|
+
let Some(value) = options::get(&ruby, rhash, Proxy::NAME) else {
|
|
43
|
+
return Ok(Extractor(None));
|
|
44
|
+
};
|
|
45
|
+
if value.is_nil() {
|
|
46
|
+
return Ok(Extractor(None));
|
|
113
47
|
}
|
|
114
48
|
|
|
115
|
-
|
|
49
|
+
let proxy =
|
|
50
|
+
RString::try_convert(value).map_err(|error| option_value_error(Proxy::NAME, error))?;
|
|
51
|
+
let proxy = Proxy::all(proxy.to_bytes().as_ref())
|
|
52
|
+
.map_err(|err| wreq_error(&ruby, err))
|
|
53
|
+
.map_err(|error| option_value_error(Proxy::NAME, error))?;
|
|
54
|
+
|
|
55
|
+
Ok(Extractor(Some(proxy)))
|
|
116
56
|
}
|
|
117
57
|
}
|