wreq 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Cargo.toml +54 -0
- data/Gemfile +17 -0
- data/LICENSE +201 -0
- data/README.md +150 -0
- data/Rakefile +90 -0
- data/build.rs +9 -0
- data/examples/body.rb +42 -0
- data/examples/client.rb +33 -0
- data/examples/emulation_request.rb +37 -0
- data/examples/headers.rb +27 -0
- data/examples/proxy.rb +113 -0
- data/examples/send_stream.rb +85 -0
- data/examples/stream.rb +14 -0
- data/examples/thread_interrupt.rb +83 -0
- data/extconf.rb +7 -0
- data/lib/wreq.rb +313 -0
- data/lib/wreq_ruby/body.rb +36 -0
- data/lib/wreq_ruby/client.rb +516 -0
- data/lib/wreq_ruby/cookie.rb +144 -0
- data/lib/wreq_ruby/emulation.rb +186 -0
- data/lib/wreq_ruby/error.rb +159 -0
- data/lib/wreq_ruby/header.rb +197 -0
- data/lib/wreq_ruby/http.rb +132 -0
- data/lib/wreq_ruby/response.rb +208 -0
- data/script/build_platform_gem.rb +34 -0
- data/src/client/body/form.rs +2 -0
- data/src/client/body/json.rs +16 -0
- data/src/client/body/stream.rs +148 -0
- data/src/client/body.rs +57 -0
- data/src/client/param.rs +19 -0
- data/src/client/query.rs +2 -0
- data/src/client/req.rs +251 -0
- data/src/client/resp.rs +250 -0
- data/src/client.rs +392 -0
- data/src/cookie.rs +277 -0
- data/src/emulation.rs +317 -0
- data/src/error.rs +147 -0
- data/src/extractor.rs +199 -0
- data/src/gvl.rs +154 -0
- data/src/header.rs +177 -0
- data/src/http.rs +127 -0
- data/src/lib.rs +97 -0
- data/src/macros.rs +118 -0
- data/src/rt.rs +47 -0
- data/test/client_cookie_test.rb +46 -0
- data/test/client_test.rb +136 -0
- data/test/cookie_test.rb +166 -0
- data/test/emulation_test.rb +21 -0
- data/test/error_handling_test.rb +89 -0
- data/test/header_test.rb +290 -0
- data/test/module_methods_test.rb +75 -0
- data/test/request_parameters_test.rb +175 -0
- data/test/request_test.rb +234 -0
- data/test/response_test.rb +69 -0
- data/test/stream_test.rb +81 -0
- data/test/test_helper.rb +9 -0
- data/wreq.gemspec +68 -0
- metadata +112 -0
data/src/emulation.rs
ADDED
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
use magnus::{
|
|
2
|
+
Error, Module, Object, RHash, RModule, Ruby, TryConvert, Value, function, method,
|
|
3
|
+
typed_data::{Inspect, Obj},
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
define_ruby_enum!(
|
|
7
|
+
/// An emulation.
|
|
8
|
+
const,
|
|
9
|
+
EmulationDevice,
|
|
10
|
+
"Wreq::EmulationDevice",
|
|
11
|
+
wreq_util::Emulation,
|
|
12
|
+
Chrome100,
|
|
13
|
+
Chrome101,
|
|
14
|
+
Chrome104,
|
|
15
|
+
Chrome105,
|
|
16
|
+
Chrome106,
|
|
17
|
+
Chrome107,
|
|
18
|
+
Chrome108,
|
|
19
|
+
Chrome109,
|
|
20
|
+
Chrome110,
|
|
21
|
+
Chrome114,
|
|
22
|
+
Chrome116,
|
|
23
|
+
Chrome117,
|
|
24
|
+
Chrome118,
|
|
25
|
+
Chrome119,
|
|
26
|
+
Chrome120,
|
|
27
|
+
Chrome123,
|
|
28
|
+
Chrome124,
|
|
29
|
+
Chrome126,
|
|
30
|
+
Chrome127,
|
|
31
|
+
Chrome128,
|
|
32
|
+
Chrome129,
|
|
33
|
+
Chrome130,
|
|
34
|
+
Chrome131,
|
|
35
|
+
Chrome132,
|
|
36
|
+
Chrome133,
|
|
37
|
+
Chrome134,
|
|
38
|
+
Chrome135,
|
|
39
|
+
Chrome136,
|
|
40
|
+
Chrome137,
|
|
41
|
+
Chrome138,
|
|
42
|
+
Chrome139,
|
|
43
|
+
Chrome140,
|
|
44
|
+
Chrome141,
|
|
45
|
+
Chrome142,
|
|
46
|
+
Chrome143,
|
|
47
|
+
Chrome144,
|
|
48
|
+
Chrome145,
|
|
49
|
+
Edge101,
|
|
50
|
+
Edge122,
|
|
51
|
+
Edge127,
|
|
52
|
+
Edge131,
|
|
53
|
+
Edge134,
|
|
54
|
+
Edge135,
|
|
55
|
+
Edge136,
|
|
56
|
+
Edge137,
|
|
57
|
+
Edge138,
|
|
58
|
+
Edge139,
|
|
59
|
+
Edge140,
|
|
60
|
+
Edge141,
|
|
61
|
+
Edge142,
|
|
62
|
+
Edge143,
|
|
63
|
+
Edge144,
|
|
64
|
+
Edge145,
|
|
65
|
+
Firefox109,
|
|
66
|
+
Firefox117,
|
|
67
|
+
Firefox128,
|
|
68
|
+
Firefox133,
|
|
69
|
+
Firefox135,
|
|
70
|
+
FirefoxPrivate135,
|
|
71
|
+
FirefoxAndroid135,
|
|
72
|
+
Firefox136,
|
|
73
|
+
FirefoxPrivate136,
|
|
74
|
+
Firefox139,
|
|
75
|
+
Firefox142,
|
|
76
|
+
Firefox143,
|
|
77
|
+
Firefox144,
|
|
78
|
+
Firefox145,
|
|
79
|
+
Firefox146,
|
|
80
|
+
Firefox147,
|
|
81
|
+
SafariIos17_2,
|
|
82
|
+
SafariIos17_4_1,
|
|
83
|
+
SafariIos16_5,
|
|
84
|
+
Safari15_3,
|
|
85
|
+
Safari15_5,
|
|
86
|
+
Safari15_6_1,
|
|
87
|
+
Safari16,
|
|
88
|
+
Safari16_5,
|
|
89
|
+
Safari17_0,
|
|
90
|
+
Safari17_2_1,
|
|
91
|
+
Safari17_4_1,
|
|
92
|
+
Safari17_5,
|
|
93
|
+
Safari17_6,
|
|
94
|
+
Safari18,
|
|
95
|
+
SafariIPad18,
|
|
96
|
+
Safari18_2,
|
|
97
|
+
Safari18_3,
|
|
98
|
+
Safari18_3_1,
|
|
99
|
+
SafariIos18_1_1,
|
|
100
|
+
Safari18_5,
|
|
101
|
+
Safari26,
|
|
102
|
+
Safari26_1,
|
|
103
|
+
Safari26_2,
|
|
104
|
+
SafariIos26,
|
|
105
|
+
SafariIos26_2,
|
|
106
|
+
SafariIPad26,
|
|
107
|
+
SafariIpad26_2,
|
|
108
|
+
OkHttp3_9,
|
|
109
|
+
OkHttp3_11,
|
|
110
|
+
OkHttp3_13,
|
|
111
|
+
OkHttp3_14,
|
|
112
|
+
OkHttp4_9,
|
|
113
|
+
OkHttp4_10,
|
|
114
|
+
OkHttp4_12,
|
|
115
|
+
OkHttp5,
|
|
116
|
+
Opera116,
|
|
117
|
+
Opera117,
|
|
118
|
+
Opera118,
|
|
119
|
+
Opera119
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
define_ruby_enum!(
|
|
123
|
+
/// An emulation operating system.
|
|
124
|
+
const,
|
|
125
|
+
EmulationOS,
|
|
126
|
+
"Wreq::EmulationOS",
|
|
127
|
+
wreq_util::EmulationOS,
|
|
128
|
+
Windows,
|
|
129
|
+
MacOS,
|
|
130
|
+
Linux,
|
|
131
|
+
Android,
|
|
132
|
+
IOS,
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
/// A struct to represent the `EmulationOption` class.
|
|
136
|
+
#[derive(Clone)]
|
|
137
|
+
#[magnus::wrap(class = "Wreq::Emulation", free_immediately, size)]
|
|
138
|
+
pub struct Emulation(pub wreq_util::EmulationOption);
|
|
139
|
+
|
|
140
|
+
// ===== impl EmulationDevice =====
|
|
141
|
+
|
|
142
|
+
impl EmulationDevice {
|
|
143
|
+
pub fn to_s(&self) -> String {
|
|
144
|
+
self.into_ffi().inspect()
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ===== impl EmulationOS =====
|
|
149
|
+
|
|
150
|
+
impl EmulationOS {
|
|
151
|
+
pub fn to_s(&self) -> String {
|
|
152
|
+
self.into_ffi().inspect()
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ===== impl Emulation =====
|
|
157
|
+
|
|
158
|
+
impl Emulation {
|
|
159
|
+
fn new(ruby: &Ruby, args: &[Value]) -> Result<Self, Error> {
|
|
160
|
+
let mut device = None;
|
|
161
|
+
let mut os = None;
|
|
162
|
+
let mut skip_http2 = None;
|
|
163
|
+
let mut skip_headers = None;
|
|
164
|
+
|
|
165
|
+
if let Some(hash) = args.first().and_then(|v| RHash::from_value(*v)) {
|
|
166
|
+
if let Some(v) = hash.get(ruby.to_symbol("device")) {
|
|
167
|
+
device = Some(Obj::<EmulationDevice>::try_convert(v)?);
|
|
168
|
+
}
|
|
169
|
+
if let Some(v) = hash.get(ruby.to_symbol("os")) {
|
|
170
|
+
os = Some(Obj::<EmulationOS>::try_convert(v)?);
|
|
171
|
+
}
|
|
172
|
+
if let Some(v) = hash.get(ruby.to_symbol("skip_http2")) {
|
|
173
|
+
skip_http2 = Some(bool::try_convert(v)?);
|
|
174
|
+
}
|
|
175
|
+
if let Some(v) = hash.get(ruby.to_symbol("skip_headers")) {
|
|
176
|
+
skip_headers = Some(bool::try_convert(v)?);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
let emulation = wreq_util::EmulationOption::builder()
|
|
181
|
+
.emulation(device.map(|obj| obj.into_ffi()).unwrap_or_default())
|
|
182
|
+
.emulation_os(os.map(|os| os.into_ffi()).unwrap_or_default())
|
|
183
|
+
.skip_http2(skip_http2.unwrap_or(false))
|
|
184
|
+
.skip_headers(skip_headers.unwrap_or(false))
|
|
185
|
+
.build();
|
|
186
|
+
|
|
187
|
+
Ok(Self(emulation))
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
pub fn include(ruby: &Ruby, gem_module: &RModule) -> Result<(), Error> {
|
|
192
|
+
// EmulationDevice enum binding
|
|
193
|
+
let emulation_class = gem_module.define_class("EmulationDevice", ruby.class_object())?;
|
|
194
|
+
emulation_class.define_method("to_s", method!(EmulationDevice::to_s, 0))?;
|
|
195
|
+
emulation_class.const_set("Chrome100", EmulationDevice::Chrome100)?;
|
|
196
|
+
emulation_class.const_set("Chrome101", EmulationDevice::Chrome101)?;
|
|
197
|
+
emulation_class.const_set("Chrome104", EmulationDevice::Chrome104)?;
|
|
198
|
+
emulation_class.const_set("Chrome105", EmulationDevice::Chrome105)?;
|
|
199
|
+
emulation_class.const_set("Chrome106", EmulationDevice::Chrome106)?;
|
|
200
|
+
emulation_class.const_set("Chrome107", EmulationDevice::Chrome107)?;
|
|
201
|
+
emulation_class.const_set("Chrome108", EmulationDevice::Chrome108)?;
|
|
202
|
+
emulation_class.const_set("Chrome109", EmulationDevice::Chrome109)?;
|
|
203
|
+
emulation_class.const_set("Chrome110", EmulationDevice::Chrome110)?;
|
|
204
|
+
emulation_class.const_set("Chrome114", EmulationDevice::Chrome114)?;
|
|
205
|
+
emulation_class.const_set("Chrome116", EmulationDevice::Chrome116)?;
|
|
206
|
+
emulation_class.const_set("Chrome117", EmulationDevice::Chrome117)?;
|
|
207
|
+
emulation_class.const_set("Chrome118", EmulationDevice::Chrome118)?;
|
|
208
|
+
emulation_class.const_set("Chrome119", EmulationDevice::Chrome119)?;
|
|
209
|
+
emulation_class.const_set("Chrome120", EmulationDevice::Chrome120)?;
|
|
210
|
+
emulation_class.const_set("Chrome123", EmulationDevice::Chrome123)?;
|
|
211
|
+
emulation_class.const_set("Chrome124", EmulationDevice::Chrome124)?;
|
|
212
|
+
emulation_class.const_set("Chrome126", EmulationDevice::Chrome126)?;
|
|
213
|
+
emulation_class.const_set("Chrome127", EmulationDevice::Chrome127)?;
|
|
214
|
+
emulation_class.const_set("Chrome128", EmulationDevice::Chrome128)?;
|
|
215
|
+
emulation_class.const_set("Chrome129", EmulationDevice::Chrome129)?;
|
|
216
|
+
emulation_class.const_set("Chrome130", EmulationDevice::Chrome130)?;
|
|
217
|
+
emulation_class.const_set("Chrome131", EmulationDevice::Chrome131)?;
|
|
218
|
+
emulation_class.const_set("Chrome132", EmulationDevice::Chrome132)?;
|
|
219
|
+
emulation_class.const_set("Chrome133", EmulationDevice::Chrome133)?;
|
|
220
|
+
emulation_class.const_set("Chrome134", EmulationDevice::Chrome134)?;
|
|
221
|
+
emulation_class.const_set("Chrome135", EmulationDevice::Chrome135)?;
|
|
222
|
+
emulation_class.const_set("Chrome136", EmulationDevice::Chrome136)?;
|
|
223
|
+
emulation_class.const_set("Chrome137", EmulationDevice::Chrome137)?;
|
|
224
|
+
emulation_class.const_set("Chrome138", EmulationDevice::Chrome138)?;
|
|
225
|
+
emulation_class.const_set("Chrome139", EmulationDevice::Chrome139)?;
|
|
226
|
+
emulation_class.const_set("Chrome140", EmulationDevice::Chrome140)?;
|
|
227
|
+
emulation_class.const_set("Chrome141", EmulationDevice::Chrome141)?;
|
|
228
|
+
emulation_class.const_set("Chrome142", EmulationDevice::Chrome142)?;
|
|
229
|
+
emulation_class.const_set("Chrome143", EmulationDevice::Chrome143)?;
|
|
230
|
+
emulation_class.const_set("Chrome144", EmulationDevice::Chrome144)?;
|
|
231
|
+
emulation_class.const_set("Chrome145", EmulationDevice::Chrome145)?;
|
|
232
|
+
emulation_class.const_set("Edge101", EmulationDevice::Edge101)?;
|
|
233
|
+
emulation_class.const_set("Edge122", EmulationDevice::Edge122)?;
|
|
234
|
+
emulation_class.const_set("Edge127", EmulationDevice::Edge127)?;
|
|
235
|
+
emulation_class.const_set("Edge131", EmulationDevice::Edge131)?;
|
|
236
|
+
emulation_class.const_set("Edge134", EmulationDevice::Edge134)?;
|
|
237
|
+
emulation_class.const_set("Edge135", EmulationDevice::Edge135)?;
|
|
238
|
+
emulation_class.const_set("Edge136", EmulationDevice::Edge136)?;
|
|
239
|
+
emulation_class.const_set("Edge137", EmulationDevice::Edge137)?;
|
|
240
|
+
emulation_class.const_set("Edge138", EmulationDevice::Edge138)?;
|
|
241
|
+
emulation_class.const_set("Edge139", EmulationDevice::Edge139)?;
|
|
242
|
+
emulation_class.const_set("Edge140", EmulationDevice::Edge140)?;
|
|
243
|
+
emulation_class.const_set("Edge141", EmulationDevice::Edge141)?;
|
|
244
|
+
emulation_class.const_set("Edge142", EmulationDevice::Edge142)?;
|
|
245
|
+
emulation_class.const_set("Edge143", EmulationDevice::Edge143)?;
|
|
246
|
+
emulation_class.const_set("Edge144", EmulationDevice::Edge144)?;
|
|
247
|
+
emulation_class.const_set("Edge145", EmulationDevice::Edge145)?;
|
|
248
|
+
emulation_class.const_set("Firefox109", EmulationDevice::Firefox109)?;
|
|
249
|
+
emulation_class.const_set("Firefox117", EmulationDevice::Firefox117)?;
|
|
250
|
+
emulation_class.const_set("Firefox128", EmulationDevice::Firefox128)?;
|
|
251
|
+
emulation_class.const_set("Firefox133", EmulationDevice::Firefox133)?;
|
|
252
|
+
emulation_class.const_set("Firefox135", EmulationDevice::Firefox135)?;
|
|
253
|
+
emulation_class.const_set("FirefoxPrivate135", EmulationDevice::FirefoxPrivate135)?;
|
|
254
|
+
emulation_class.const_set("FirefoxAndroid135", EmulationDevice::FirefoxAndroid135)?;
|
|
255
|
+
emulation_class.const_set("Firefox136", EmulationDevice::Firefox136)?;
|
|
256
|
+
emulation_class.const_set("FirefoxPrivate136", EmulationDevice::FirefoxPrivate136)?;
|
|
257
|
+
emulation_class.const_set("Firefox139", EmulationDevice::Firefox139)?;
|
|
258
|
+
emulation_class.const_set("Firefox142", EmulationDevice::Firefox142)?;
|
|
259
|
+
emulation_class.const_set("Firefox143", EmulationDevice::Firefox143)?;
|
|
260
|
+
emulation_class.const_set("Firefox144", EmulationDevice::Firefox144)?;
|
|
261
|
+
emulation_class.const_set("Firefox145", EmulationDevice::Firefox145)?;
|
|
262
|
+
emulation_class.const_set("Firefox146", EmulationDevice::Firefox146)?;
|
|
263
|
+
emulation_class.const_set("Firefox147", EmulationDevice::Firefox147)?;
|
|
264
|
+
emulation_class.const_set("SafariIos17_2", EmulationDevice::SafariIos17_2)?;
|
|
265
|
+
emulation_class.const_set("SafariIos17_4_1", EmulationDevice::SafariIos17_4_1)?;
|
|
266
|
+
emulation_class.const_set("SafariIos16_5", EmulationDevice::SafariIos16_5)?;
|
|
267
|
+
emulation_class.const_set("Safari15_3", EmulationDevice::Safari15_3)?;
|
|
268
|
+
emulation_class.const_set("Safari15_5", EmulationDevice::Safari15_5)?;
|
|
269
|
+
emulation_class.const_set("Safari15_6_1", EmulationDevice::Safari15_6_1)?;
|
|
270
|
+
emulation_class.const_set("Safari16", EmulationDevice::Safari16)?;
|
|
271
|
+
emulation_class.const_set("Safari16_5", EmulationDevice::Safari16_5)?;
|
|
272
|
+
emulation_class.const_set("Safari17_0", EmulationDevice::Safari17_0)?;
|
|
273
|
+
emulation_class.const_set("Safari17_2_1", EmulationDevice::Safari17_2_1)?;
|
|
274
|
+
emulation_class.const_set("Safari17_4_1", EmulationDevice::Safari17_4_1)?;
|
|
275
|
+
emulation_class.const_set("Safari17_5", EmulationDevice::Safari17_5)?;
|
|
276
|
+
emulation_class.const_set("Safari17_6", EmulationDevice::Safari17_6)?;
|
|
277
|
+
emulation_class.const_set("Safari18", EmulationDevice::Safari18)?;
|
|
278
|
+
emulation_class.const_set("SafariIPad18", EmulationDevice::SafariIPad18)?;
|
|
279
|
+
emulation_class.const_set("Safari18_2", EmulationDevice::Safari18_2)?;
|
|
280
|
+
emulation_class.const_set("Safari18_3", EmulationDevice::Safari18_3)?;
|
|
281
|
+
emulation_class.const_set("Safari18_3_1", EmulationDevice::Safari18_3_1)?;
|
|
282
|
+
emulation_class.const_set("SafariIos18_1_1", EmulationDevice::SafariIos18_1_1)?;
|
|
283
|
+
emulation_class.const_set("Safari18_5", EmulationDevice::Safari18_5)?;
|
|
284
|
+
emulation_class.const_set("Safari26", EmulationDevice::Safari26)?;
|
|
285
|
+
emulation_class.const_set("Safari26_1", EmulationDevice::Safari26_1)?;
|
|
286
|
+
emulation_class.const_set("Safari26_2", EmulationDevice::Safari26_2)?;
|
|
287
|
+
emulation_class.const_set("SafariIos26", EmulationDevice::SafariIos26)?;
|
|
288
|
+
emulation_class.const_set("SafariIos26_2", EmulationDevice::SafariIos26_2)?;
|
|
289
|
+
emulation_class.const_set("SafariIPad26", EmulationDevice::SafariIPad26)?;
|
|
290
|
+
emulation_class.const_set("SafariIpad26_2", EmulationDevice::SafariIpad26_2)?;
|
|
291
|
+
emulation_class.const_set("OkHttp3_9", EmulationDevice::OkHttp3_9)?;
|
|
292
|
+
emulation_class.const_set("OkHttp3_11", EmulationDevice::OkHttp3_11)?;
|
|
293
|
+
emulation_class.const_set("OkHttp3_13", EmulationDevice::OkHttp3_13)?;
|
|
294
|
+
emulation_class.const_set("OkHttp3_14", EmulationDevice::OkHttp3_14)?;
|
|
295
|
+
emulation_class.const_set("OkHttp4_9", EmulationDevice::OkHttp4_9)?;
|
|
296
|
+
emulation_class.const_set("OkHttp4_10", EmulationDevice::OkHttp4_10)?;
|
|
297
|
+
emulation_class.const_set("OkHttp4_12", EmulationDevice::OkHttp4_12)?;
|
|
298
|
+
emulation_class.const_set("OkHttp5", EmulationDevice::OkHttp5)?;
|
|
299
|
+
emulation_class.const_set("Opera116", EmulationDevice::Opera116)?;
|
|
300
|
+
emulation_class.const_set("Opera117", EmulationDevice::Opera117)?;
|
|
301
|
+
emulation_class.const_set("Opera118", EmulationDevice::Opera118)?;
|
|
302
|
+
emulation_class.const_set("Opera119", EmulationDevice::Opera119)?;
|
|
303
|
+
|
|
304
|
+
// EmulationOS enum binding
|
|
305
|
+
let emulation_os_class = gem_module.define_class("EmulationOS", ruby.class_object())?;
|
|
306
|
+
emulation_os_class.define_method("to_s", method!(EmulationOS::to_s, 0))?;
|
|
307
|
+
emulation_os_class.const_set("Windows", EmulationOS::Windows)?;
|
|
308
|
+
emulation_os_class.const_set("MacOS", EmulationOS::MacOS)?;
|
|
309
|
+
emulation_os_class.const_set("Linux", EmulationOS::Linux)?;
|
|
310
|
+
emulation_os_class.const_set("Android", EmulationOS::Android)?;
|
|
311
|
+
emulation_os_class.const_set("IOS", EmulationOS::IOS)?;
|
|
312
|
+
|
|
313
|
+
// Emulation class binding
|
|
314
|
+
let emulation_option_class = gem_module.define_class("Emulation", ruby.class_object())?;
|
|
315
|
+
emulation_option_class.define_singleton_method("new", function!(Emulation::new, -1))?;
|
|
316
|
+
Ok(())
|
|
317
|
+
}
|
data/src/error.rs
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
use magnus::{
|
|
2
|
+
Error as MagnusError, RModule, Ruby, exception::ExceptionClass, prelude::*, value::Lazy,
|
|
3
|
+
};
|
|
4
|
+
use tokio::sync::mpsc::error::SendError;
|
|
5
|
+
|
|
6
|
+
const RACE_CONDITION_ERROR_MSG: &str = r#"Due to Rust's memory management with borrowing,
|
|
7
|
+
you cannot use certain instances multiple times as they may be consumed.
|
|
8
|
+
|
|
9
|
+
This error can occur in the following cases:
|
|
10
|
+
1) You passed a non-clonable instance to a function that requires ownership.
|
|
11
|
+
2) You attempted to use a method that consumes ownership more than once (e.g., reading a response body twice).
|
|
12
|
+
3) You tried to reference an instance after it was borrowed.
|
|
13
|
+
|
|
14
|
+
Potential solutions:
|
|
15
|
+
1) Avoid sharing instances; create a new instance each time you use it.
|
|
16
|
+
2) Refrain from performing actions that consume ownership multiple times.
|
|
17
|
+
3) Change the order of operations to reference the instance before borrowing it.
|
|
18
|
+
"#;
|
|
19
|
+
|
|
20
|
+
static WREQ: Lazy<RModule> = Lazy::new(|ruby| ruby.define_module(crate::RUBY_MODULE_NAME).unwrap());
|
|
21
|
+
|
|
22
|
+
macro_rules! define_exception {
|
|
23
|
+
($name:ident, $ruby_name:literal, $parent_method:ident) => {
|
|
24
|
+
static $name: Lazy<ExceptionClass> = Lazy::new(|ruby| {
|
|
25
|
+
ruby.get_inner(&WREQ)
|
|
26
|
+
.define_error($ruby_name, ruby.$parent_method())
|
|
27
|
+
.unwrap()
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
macro_rules! map_wreq_error {
|
|
33
|
+
($ruby:expr, $err:expr, $msg:expr, $($check_method:ident => $exception:ident),* $(,)?) => {
|
|
34
|
+
{
|
|
35
|
+
$(
|
|
36
|
+
if $err.$check_method() {
|
|
37
|
+
return MagnusError::new($ruby.get_inner(&$exception), $msg);
|
|
38
|
+
}
|
|
39
|
+
)*
|
|
40
|
+
MagnusError::new($ruby.exception_runtime_error(), $msg)
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// System-level and runtime errors
|
|
46
|
+
define_exception!(MEMORY, "MemoryError", exception_runtime_error);
|
|
47
|
+
|
|
48
|
+
// Network connection errors
|
|
49
|
+
define_exception!(CONNECTION_ERROR, "ConnectionError", exception_runtime_error);
|
|
50
|
+
define_exception!(
|
|
51
|
+
PROXY_CONNECTION_ERROR,
|
|
52
|
+
"ProxyConnectionError",
|
|
53
|
+
exception_runtime_error
|
|
54
|
+
);
|
|
55
|
+
define_exception!(
|
|
56
|
+
CONNECTION_RESET_ERROR,
|
|
57
|
+
"ConnectionResetError",
|
|
58
|
+
exception_runtime_error
|
|
59
|
+
);
|
|
60
|
+
define_exception!(TLS_ERROR, "TlsError", exception_runtime_error);
|
|
61
|
+
|
|
62
|
+
// HTTP protocol and request/response errors
|
|
63
|
+
define_exception!(REQUEST_ERROR, "RequestError", exception_runtime_error);
|
|
64
|
+
define_exception!(STATUS_ERROR, "StatusError", exception_runtime_error);
|
|
65
|
+
define_exception!(REDIRECT_ERROR, "RedirectError", exception_runtime_error);
|
|
66
|
+
define_exception!(TIMEOUT_ERROR, "TimeoutError", exception_runtime_error);
|
|
67
|
+
|
|
68
|
+
// Data processing and encoding errors
|
|
69
|
+
define_exception!(BODY_ERROR, "BodyError", exception_runtime_error);
|
|
70
|
+
define_exception!(DECODING_ERROR, "DecodingError", exception_runtime_error);
|
|
71
|
+
|
|
72
|
+
// Configuration and builder errors
|
|
73
|
+
define_exception!(BUILDER_ERROR, "BuilderError", exception_runtime_error);
|
|
74
|
+
|
|
75
|
+
// Thread interruption error
|
|
76
|
+
define_exception!(INTERRUPT_ERROR, "InterruptError", exception_interrupt);
|
|
77
|
+
|
|
78
|
+
/// Memory error constant
|
|
79
|
+
pub fn memory_error() -> MagnusError {
|
|
80
|
+
MagnusError::new(ruby!().get_inner(&MEMORY), RACE_CONDITION_ERROR_MSG)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/// Thread interruption error (raised when Thread.kill cancels a request)
|
|
84
|
+
pub fn interrupt_error() -> MagnusError {
|
|
85
|
+
MagnusError::new(ruby!().get_inner(&INTERRUPT_ERROR), "request interrupted")
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/// Map [`tokio::sync::mpsc::error::SendError`] to corresponding [`magnus::Error`]
|
|
89
|
+
pub fn mpsc_send_error_to_magnus<T>(err: SendError<T>) -> MagnusError {
|
|
90
|
+
MagnusError::new(
|
|
91
|
+
ruby!().get_inner(&BODY_ERROR),
|
|
92
|
+
format!("failed to send body chunk: {}", err),
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/// Map [`wreq::header::InvalidHeaderName`] to corresponding [`magnus::Error`]
|
|
97
|
+
pub fn header_name_error_to_magnus(err: wreq::header::InvalidHeaderName) -> MagnusError {
|
|
98
|
+
MagnusError::new(
|
|
99
|
+
ruby!().get_inner(&BUILDER_ERROR),
|
|
100
|
+
format!("invalid header name: {err}"),
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/// Map [`wreq::header::InvalidHeaderValue`] to corresponding [`magnus::Error`]
|
|
105
|
+
pub fn header_value_error_to_magnus(err: wreq::header::InvalidHeaderValue) -> MagnusError {
|
|
106
|
+
MagnusError::new(
|
|
107
|
+
ruby!().get_inner(&BUILDER_ERROR),
|
|
108
|
+
format!("invalid header value: {err}"),
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/// Map [`wreq::Error`] to corresponding [`magnus::Error`]
|
|
113
|
+
pub fn wreq_error_to_magnus(err: wreq::Error) -> MagnusError {
|
|
114
|
+
let error_msg = err.to_string();
|
|
115
|
+
map_wreq_error!(
|
|
116
|
+
ruby!(),
|
|
117
|
+
err,
|
|
118
|
+
error_msg,
|
|
119
|
+
is_builder => BUILDER_ERROR,
|
|
120
|
+
is_body => BODY_ERROR,
|
|
121
|
+
is_tls => TLS_ERROR,
|
|
122
|
+
is_connection_reset => CONNECTION_RESET_ERROR,
|
|
123
|
+
is_connect => CONNECTION_ERROR,
|
|
124
|
+
is_proxy_connect => PROXY_CONNECTION_ERROR,
|
|
125
|
+
is_decode => DECODING_ERROR,
|
|
126
|
+
is_redirect => REDIRECT_ERROR,
|
|
127
|
+
is_timeout => TIMEOUT_ERROR,
|
|
128
|
+
is_status => STATUS_ERROR,
|
|
129
|
+
is_request => REQUEST_ERROR,
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
pub fn include(ruby: &Ruby) {
|
|
134
|
+
Lazy::force(&MEMORY, ruby);
|
|
135
|
+
Lazy::force(&CONNECTION_ERROR, ruby);
|
|
136
|
+
Lazy::force(&PROXY_CONNECTION_ERROR, ruby);
|
|
137
|
+
Lazy::force(&CONNECTION_RESET_ERROR, ruby);
|
|
138
|
+
Lazy::force(&TLS_ERROR, ruby);
|
|
139
|
+
Lazy::force(&REQUEST_ERROR, ruby);
|
|
140
|
+
Lazy::force(&STATUS_ERROR, ruby);
|
|
141
|
+
Lazy::force(&REDIRECT_ERROR, ruby);
|
|
142
|
+
Lazy::force(&TIMEOUT_ERROR, ruby);
|
|
143
|
+
Lazy::force(&BODY_ERROR, ruby);
|
|
144
|
+
Lazy::force(&DECODING_ERROR, ruby);
|
|
145
|
+
Lazy::force(&BUILDER_ERROR, ruby);
|
|
146
|
+
Lazy::force(&INTERRUPT_ERROR, ruby);
|
|
147
|
+
}
|
data/src/extractor.rs
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
use bytes::Bytes;
|
|
2
|
+
use magnus::{RArray, RHash, RString, Ruby, TryConvert, r_hash::ForEach};
|
|
3
|
+
use wreq::{
|
|
4
|
+
Proxy, Version,
|
|
5
|
+
header::{HeaderMap, HeaderName, HeaderValue, OrigHeaderMap},
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
use crate::error::{
|
|
9
|
+
header_name_error_to_magnus, header_value_error_to_magnus, wreq_error_to_magnus,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/// A trait that defines the parameter name for extraction.
|
|
13
|
+
pub trait ExtractorName {
|
|
14
|
+
/// The name of the parameter in the Ruby hash.
|
|
15
|
+
const NAME: &str;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/// A generic extractor for various types.
|
|
19
|
+
pub struct Extractor<T>(Option<T>)
|
|
20
|
+
where
|
|
21
|
+
T: ExtractorName;
|
|
22
|
+
|
|
23
|
+
impl<T> Extractor<T>
|
|
24
|
+
where
|
|
25
|
+
T: ExtractorName,
|
|
26
|
+
{
|
|
27
|
+
/// Consumes the extractor and returns the wrapped value.
|
|
28
|
+
///
|
|
29
|
+
/// Returns `Some(T)` if a value was extracted, `None` otherwise.
|
|
30
|
+
#[inline]
|
|
31
|
+
pub fn into_inner(self) -> Option<T> {
|
|
32
|
+
self.0
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ===== impl Extractor<Version> =====
|
|
37
|
+
|
|
38
|
+
impl ExtractorName for Version {
|
|
39
|
+
const NAME: &str = "version";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
impl TryConvert for Extractor<Version> {
|
|
43
|
+
fn try_convert(value: magnus::Value) -> Result<Self, magnus::Error> {
|
|
44
|
+
let keyword = RHash::try_convert(value)?;
|
|
45
|
+
if let Some(version_val) = keyword.get(Version::NAME) {
|
|
46
|
+
return <&crate::http::Version>::try_convert(version_val)
|
|
47
|
+
.cloned()
|
|
48
|
+
.map(crate::http::Version::into_ffi)
|
|
49
|
+
.map(Some)
|
|
50
|
+
.map(Extractor);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
Ok(Extractor(None))
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ===== impl Extractor<HeaderValue> =====
|
|
58
|
+
|
|
59
|
+
impl ExtractorName for HeaderValue {
|
|
60
|
+
const NAME: &str = "user_agent";
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
impl TryConvert for Extractor<HeaderValue> {
|
|
64
|
+
fn try_convert(value: magnus::Value) -> Result<Self, magnus::Error> {
|
|
65
|
+
let ruby = Ruby::get_with(value);
|
|
66
|
+
let keyword = RHash::try_convert(value)?;
|
|
67
|
+
|
|
68
|
+
if let Some(ruby_value) = keyword
|
|
69
|
+
.get(ruby.to_symbol(HeaderValue::NAME))
|
|
70
|
+
.and_then(RString::from_value)
|
|
71
|
+
{
|
|
72
|
+
return HeaderValue::from_maybe_shared(ruby_value.to_bytes())
|
|
73
|
+
.map(Some)
|
|
74
|
+
.map(Extractor)
|
|
75
|
+
.map_err(header_value_error_to_magnus);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
Ok(Extractor(None))
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ===== impl Extractor<HeaderMap> =====
|
|
83
|
+
|
|
84
|
+
impl ExtractorName for HeaderMap {
|
|
85
|
+
const NAME: &str = "headers";
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
impl TryConvert for Extractor<HeaderMap> {
|
|
89
|
+
fn try_convert(value: magnus::Value) -> Result<Self, magnus::Error> {
|
|
90
|
+
let ruby = Ruby::get_with(value);
|
|
91
|
+
let keyword = RHash::try_convert(value)?;
|
|
92
|
+
let mut headers = HeaderMap::new();
|
|
93
|
+
|
|
94
|
+
if let Some(hash) = keyword
|
|
95
|
+
.get(ruby.to_symbol(HeaderMap::NAME))
|
|
96
|
+
.and_then(RHash::from_value)
|
|
97
|
+
{
|
|
98
|
+
hash.foreach(|name: RString, value: RString| {
|
|
99
|
+
let name = HeaderName::from_bytes(&name.to_bytes())
|
|
100
|
+
.map_err(header_name_error_to_magnus)?;
|
|
101
|
+
let value = HeaderValue::from_maybe_shared(value.to_bytes())
|
|
102
|
+
.map_err(header_value_error_to_magnus)?;
|
|
103
|
+
headers.insert(name, value);
|
|
104
|
+
|
|
105
|
+
Ok(ForEach::Continue)
|
|
106
|
+
})?;
|
|
107
|
+
|
|
108
|
+
return Ok(Extractor(Some(headers)));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
Ok(Extractor(None))
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ===== impl Extractor<OrigHeaderMap> =====
|
|
116
|
+
|
|
117
|
+
impl ExtractorName for OrigHeaderMap {
|
|
118
|
+
const NAME: &str = "orig_headers";
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
impl TryConvert for Extractor<OrigHeaderMap> {
|
|
122
|
+
fn try_convert(value: magnus::Value) -> Result<Self, magnus::Error> {
|
|
123
|
+
let ruby = Ruby::get_with(value);
|
|
124
|
+
let keyword = RHash::try_convert(value)?;
|
|
125
|
+
|
|
126
|
+
if let Some(orig_headers) = keyword
|
|
127
|
+
.get(ruby.to_symbol(OrigHeaderMap::NAME))
|
|
128
|
+
.and_then(RArray::from_value)
|
|
129
|
+
{
|
|
130
|
+
let mut map = OrigHeaderMap::new();
|
|
131
|
+
for value in orig_headers.into_iter().flat_map(RString::from_value) {
|
|
132
|
+
map.insert(value.to_bytes());
|
|
133
|
+
}
|
|
134
|
+
return Ok(Extractor(Some(map)));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
Ok(Extractor(None))
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ===== impl Extractor<Vec<HeaderValue>> =====
|
|
142
|
+
|
|
143
|
+
impl ExtractorName for Vec<HeaderValue> {
|
|
144
|
+
const NAME: &str = "cookies";
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
impl TryConvert for Extractor<Vec<HeaderValue>> {
|
|
148
|
+
fn try_convert(value: magnus::Value) -> Result<Self, magnus::Error> {
|
|
149
|
+
use percent_encoding::{NON_ALPHANUMERIC, percent_encode};
|
|
150
|
+
|
|
151
|
+
let ruby = Ruby::get_with(value);
|
|
152
|
+
let keyword = RHash::try_convert(value)?;
|
|
153
|
+
|
|
154
|
+
if let Some(hash) = keyword
|
|
155
|
+
.get(ruby.to_symbol(Vec::<HeaderValue>::NAME))
|
|
156
|
+
.and_then(RHash::from_value)
|
|
157
|
+
{
|
|
158
|
+
let mut cookies = Vec::new();
|
|
159
|
+
hash.foreach(|name: RString, value: RString| {
|
|
160
|
+
let value = value.to_bytes();
|
|
161
|
+
let encoded_value = percent_encode(&value, NON_ALPHANUMERIC);
|
|
162
|
+
let cookie = format!("{name}={encoded_value}");
|
|
163
|
+
let header_value = HeaderValue::from_maybe_shared(Bytes::from(cookie))
|
|
164
|
+
.map_err(header_value_error_to_magnus)?;
|
|
165
|
+
cookies.push(header_value);
|
|
166
|
+
Ok(ForEach::Continue)
|
|
167
|
+
})?;
|
|
168
|
+
|
|
169
|
+
return Ok(Extractor(Some(cookies)));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
Ok(Extractor(None))
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// ===== impl Extractor<Proxy> =====
|
|
177
|
+
|
|
178
|
+
impl ExtractorName for Proxy {
|
|
179
|
+
const NAME: &str = "proxy";
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
impl TryConvert for Extractor<Proxy> {
|
|
183
|
+
fn try_convert(value: magnus::Value) -> Result<Self, magnus::Error> {
|
|
184
|
+
let ruby = Ruby::get_with(value);
|
|
185
|
+
let keyword = RHash::try_convert(value)?;
|
|
186
|
+
|
|
187
|
+
if let Some(proxy) = keyword
|
|
188
|
+
.get(ruby.to_symbol(Proxy::NAME))
|
|
189
|
+
.and_then(RString::from_value)
|
|
190
|
+
{
|
|
191
|
+
return Proxy::all(proxy.to_bytes().as_ref())
|
|
192
|
+
.map(Some)
|
|
193
|
+
.map(Extractor)
|
|
194
|
+
.map_err(wreq_error_to_magnus);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
Ok(Extractor(None))
|
|
198
|
+
}
|
|
199
|
+
}
|