wreq 1.0.0 → 1.2.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 +4 -4
- data/Cargo.lock +1709 -0
- data/Cargo.toml +14 -14
- data/README.md +8 -8
- data/examples/cookie.rb +24 -0
- data/examples/{emulation_request.rb → emulate_request.rb} +8 -8
- data/lib/wreq.rb +45 -55
- data/lib/wreq_ruby/client.rb +71 -63
- data/lib/wreq_ruby/cookie.rb +21 -9
- data/lib/wreq_ruby/{emulation.rb → emulate.rb} +74 -29
- data/lib/wreq_ruby/error.rb +1 -3
- data/lib/wreq_ruby/header.rb +8 -0
- data/lib/wreq_ruby/http.rb +14 -0
- data/lib/wreq_ruby/response.rb +22 -21
- data/src/client/body/stream.rs +9 -14
- data/src/client/req.rs +35 -41
- data/src/client/resp.rs +46 -40
- data/src/client.rs +35 -31
- data/src/cookie.rs +46 -11
- data/src/emulate.rs +375 -0
- data/src/error.rs +8 -0
- data/src/extractor.rs +2 -38
- data/src/header.rs +37 -6
- data/src/lib.rs +2 -2
- data/src/macros.rs +5 -0
- data/src/rt.rs +0 -18
- data/test/client_cookie_test.rb +1 -1
- data/test/cookie_test.rb +30 -16
- data/test/emulation_test.rb +8 -8
- data/test/error_handling_test.rb +4 -1
- data/test/inspect_test.rb +125 -0
- data/test/stream_test.rb +292 -2
- metadata +7 -4
- data/src/emulation.rs +0 -317
data/src/emulate.rs
ADDED
|
@@ -0,0 +1,375 @@
|
|
|
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 profile.
|
|
8
|
+
const,
|
|
9
|
+
Profile,
|
|
10
|
+
"Wreq::Profile",
|
|
11
|
+
wreq_util::Profile,
|
|
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
|
+
Chrome146,
|
|
50
|
+
Chrome147,
|
|
51
|
+
Chrome148,
|
|
52
|
+
|
|
53
|
+
Edge101,
|
|
54
|
+
Edge122,
|
|
55
|
+
Edge127,
|
|
56
|
+
Edge131,
|
|
57
|
+
Edge134,
|
|
58
|
+
Edge135,
|
|
59
|
+
Edge136,
|
|
60
|
+
Edge137,
|
|
61
|
+
Edge138,
|
|
62
|
+
Edge139,
|
|
63
|
+
Edge140,
|
|
64
|
+
Edge141,
|
|
65
|
+
Edge142,
|
|
66
|
+
Edge143,
|
|
67
|
+
Edge144,
|
|
68
|
+
Edge145,
|
|
69
|
+
Edge146,
|
|
70
|
+
Edge147,
|
|
71
|
+
Edge148,
|
|
72
|
+
|
|
73
|
+
Firefox109,
|
|
74
|
+
Firefox117,
|
|
75
|
+
Firefox128,
|
|
76
|
+
Firefox133,
|
|
77
|
+
Firefox135,
|
|
78
|
+
FirefoxPrivate135,
|
|
79
|
+
FirefoxAndroid135,
|
|
80
|
+
Firefox136,
|
|
81
|
+
FirefoxPrivate136,
|
|
82
|
+
Firefox139,
|
|
83
|
+
Firefox142,
|
|
84
|
+
Firefox143,
|
|
85
|
+
Firefox144,
|
|
86
|
+
Firefox145,
|
|
87
|
+
Firefox146,
|
|
88
|
+
Firefox147,
|
|
89
|
+
Firefox148,
|
|
90
|
+
Firefox149,
|
|
91
|
+
Firefox150,
|
|
92
|
+
Firefox151,
|
|
93
|
+
|
|
94
|
+
SafariIos17_2,
|
|
95
|
+
SafariIos17_4_1,
|
|
96
|
+
SafariIos16_5,
|
|
97
|
+
Safari15_3,
|
|
98
|
+
Safari15_5,
|
|
99
|
+
Safari15_6_1,
|
|
100
|
+
Safari16,
|
|
101
|
+
Safari16_5,
|
|
102
|
+
Safari17_0,
|
|
103
|
+
Safari17_2_1,
|
|
104
|
+
Safari17_4_1,
|
|
105
|
+
Safari17_5,
|
|
106
|
+
Safari17_6,
|
|
107
|
+
Safari18,
|
|
108
|
+
SafariIPad18,
|
|
109
|
+
Safari18_2,
|
|
110
|
+
Safari18_3,
|
|
111
|
+
Safari18_3_1,
|
|
112
|
+
SafariIos18_1_1,
|
|
113
|
+
Safari18_5,
|
|
114
|
+
Safari26,
|
|
115
|
+
Safari26_1,
|
|
116
|
+
Safari26_2,
|
|
117
|
+
Safari26_3,
|
|
118
|
+
Safari26_4,
|
|
119
|
+
SafariIos26,
|
|
120
|
+
SafariIos26_2,
|
|
121
|
+
SafariIPad26,
|
|
122
|
+
SafariIpad26_2,
|
|
123
|
+
|
|
124
|
+
OkHttp3_9,
|
|
125
|
+
OkHttp3_11,
|
|
126
|
+
OkHttp3_13,
|
|
127
|
+
OkHttp3_14,
|
|
128
|
+
OkHttp4_9,
|
|
129
|
+
OkHttp4_10,
|
|
130
|
+
OkHttp4_12,
|
|
131
|
+
OkHttp5,
|
|
132
|
+
|
|
133
|
+
Opera116,
|
|
134
|
+
Opera117,
|
|
135
|
+
Opera118,
|
|
136
|
+
Opera119,
|
|
137
|
+
Opera120,
|
|
138
|
+
Opera121,
|
|
139
|
+
Opera122,
|
|
140
|
+
Opera123,
|
|
141
|
+
Opera124,
|
|
142
|
+
Opera125,
|
|
143
|
+
Opera126,
|
|
144
|
+
Opera127,
|
|
145
|
+
Opera128,
|
|
146
|
+
Opera129,
|
|
147
|
+
Opera130,
|
|
148
|
+
Opera131,
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
define_ruby_enum!(
|
|
152
|
+
/// An emulation profile for OS.
|
|
153
|
+
const,
|
|
154
|
+
Platform,
|
|
155
|
+
"Wreq::Platform",
|
|
156
|
+
wreq_util::Platform,
|
|
157
|
+
Windows,
|
|
158
|
+
MacOS,
|
|
159
|
+
Linux,
|
|
160
|
+
Android,
|
|
161
|
+
IOS,
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
/// A struct to represent the `EmulationOption` class.
|
|
165
|
+
#[derive(Clone)]
|
|
166
|
+
#[magnus::wrap(class = "Wreq::Emulation", free_immediately, size)]
|
|
167
|
+
pub struct Emulation(pub wreq_util::Emulation);
|
|
168
|
+
|
|
169
|
+
// ===== impl Profile =====
|
|
170
|
+
|
|
171
|
+
impl Profile {
|
|
172
|
+
pub fn to_s(&self) -> String {
|
|
173
|
+
self.into_ffi().inspect()
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// ===== impl Platform =====
|
|
178
|
+
|
|
179
|
+
impl Platform {
|
|
180
|
+
pub fn to_s(&self) -> String {
|
|
181
|
+
self.into_ffi().inspect()
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// ===== impl Emulation =====
|
|
186
|
+
|
|
187
|
+
impl Emulation {
|
|
188
|
+
fn new(ruby: &Ruby, args: &[Value]) -> Result<Self, Error> {
|
|
189
|
+
let mut profile = None;
|
|
190
|
+
let mut platform = None;
|
|
191
|
+
let mut http2 = None;
|
|
192
|
+
let mut headers = None;
|
|
193
|
+
|
|
194
|
+
if let Some(hash) = args.first().and_then(|v| RHash::from_value(*v)) {
|
|
195
|
+
if let Some(v) = hash.get(ruby.to_symbol(stringify!(profile))) {
|
|
196
|
+
profile = Some(Obj::<Profile>::try_convert(v)?);
|
|
197
|
+
}
|
|
198
|
+
if let Some(v) = hash.get(ruby.to_symbol(stringify!(platform))) {
|
|
199
|
+
platform = Some(Obj::<Platform>::try_convert(v)?);
|
|
200
|
+
}
|
|
201
|
+
if let Some(v) = hash.get(ruby.to_symbol(stringify!(http2))) {
|
|
202
|
+
http2 = Some(bool::try_convert(v)?);
|
|
203
|
+
}
|
|
204
|
+
if let Some(v) = hash.get(ruby.to_symbol(stringify!(headers))) {
|
|
205
|
+
headers = Some(bool::try_convert(v)?);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
let emulation = wreq_util::Emulation::builder()
|
|
210
|
+
.profile(profile.map(|obj| obj.into_ffi()).unwrap_or_default())
|
|
211
|
+
.platform(platform.map(|os| os.into_ffi()).unwrap_or_default())
|
|
212
|
+
.http2(http2.unwrap_or(true))
|
|
213
|
+
.headers(headers.unwrap_or(true))
|
|
214
|
+
.build();
|
|
215
|
+
|
|
216
|
+
Ok(Self(emulation))
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
pub fn include(ruby: &Ruby, gem_module: &RModule) -> Result<(), Error> {
|
|
221
|
+
// Profile enum binding
|
|
222
|
+
let profile = gem_module.define_class("Profile", ruby.class_object())?;
|
|
223
|
+
profile.define_method("to_s", method!(Profile::to_s, 0))?;
|
|
224
|
+
profile.const_set("Chrome100", Profile::Chrome100)?;
|
|
225
|
+
profile.const_set("Chrome101", Profile::Chrome101)?;
|
|
226
|
+
profile.const_set("Chrome104", Profile::Chrome104)?;
|
|
227
|
+
profile.const_set("Chrome105", Profile::Chrome105)?;
|
|
228
|
+
profile.const_set("Chrome106", Profile::Chrome106)?;
|
|
229
|
+
profile.const_set("Chrome107", Profile::Chrome107)?;
|
|
230
|
+
profile.const_set("Chrome108", Profile::Chrome108)?;
|
|
231
|
+
profile.const_set("Chrome109", Profile::Chrome109)?;
|
|
232
|
+
profile.const_set("Chrome110", Profile::Chrome110)?;
|
|
233
|
+
profile.const_set("Chrome114", Profile::Chrome114)?;
|
|
234
|
+
profile.const_set("Chrome116", Profile::Chrome116)?;
|
|
235
|
+
profile.const_set("Chrome117", Profile::Chrome117)?;
|
|
236
|
+
profile.const_set("Chrome118", Profile::Chrome118)?;
|
|
237
|
+
profile.const_set("Chrome119", Profile::Chrome119)?;
|
|
238
|
+
profile.const_set("Chrome120", Profile::Chrome120)?;
|
|
239
|
+
profile.const_set("Chrome123", Profile::Chrome123)?;
|
|
240
|
+
profile.const_set("Chrome124", Profile::Chrome124)?;
|
|
241
|
+
profile.const_set("Chrome126", Profile::Chrome126)?;
|
|
242
|
+
profile.const_set("Chrome127", Profile::Chrome127)?;
|
|
243
|
+
profile.const_set("Chrome128", Profile::Chrome128)?;
|
|
244
|
+
profile.const_set("Chrome129", Profile::Chrome129)?;
|
|
245
|
+
profile.const_set("Chrome130", Profile::Chrome130)?;
|
|
246
|
+
profile.const_set("Chrome131", Profile::Chrome131)?;
|
|
247
|
+
profile.const_set("Chrome132", Profile::Chrome132)?;
|
|
248
|
+
profile.const_set("Chrome133", Profile::Chrome133)?;
|
|
249
|
+
profile.const_set("Chrome134", Profile::Chrome134)?;
|
|
250
|
+
profile.const_set("Chrome135", Profile::Chrome135)?;
|
|
251
|
+
profile.const_set("Chrome136", Profile::Chrome136)?;
|
|
252
|
+
profile.const_set("Chrome137", Profile::Chrome137)?;
|
|
253
|
+
profile.const_set("Chrome138", Profile::Chrome138)?;
|
|
254
|
+
profile.const_set("Chrome139", Profile::Chrome139)?;
|
|
255
|
+
profile.const_set("Chrome140", Profile::Chrome140)?;
|
|
256
|
+
profile.const_set("Chrome141", Profile::Chrome141)?;
|
|
257
|
+
profile.const_set("Chrome142", Profile::Chrome142)?;
|
|
258
|
+
profile.const_set("Chrome143", Profile::Chrome143)?;
|
|
259
|
+
profile.const_set("Chrome144", Profile::Chrome144)?;
|
|
260
|
+
profile.const_set("Chrome145", Profile::Chrome145)?;
|
|
261
|
+
profile.const_set("Chrome146", Profile::Chrome146)?;
|
|
262
|
+
profile.const_set("Chrome147", Profile::Chrome147)?;
|
|
263
|
+
profile.const_set("Chrome148", Profile::Chrome148)?;
|
|
264
|
+
|
|
265
|
+
profile.const_set("Edge101", Profile::Edge101)?;
|
|
266
|
+
profile.const_set("Edge122", Profile::Edge122)?;
|
|
267
|
+
profile.const_set("Edge127", Profile::Edge127)?;
|
|
268
|
+
profile.const_set("Edge131", Profile::Edge131)?;
|
|
269
|
+
profile.const_set("Edge134", Profile::Edge134)?;
|
|
270
|
+
profile.const_set("Edge135", Profile::Edge135)?;
|
|
271
|
+
profile.const_set("Edge136", Profile::Edge136)?;
|
|
272
|
+
profile.const_set("Edge137", Profile::Edge137)?;
|
|
273
|
+
profile.const_set("Edge138", Profile::Edge138)?;
|
|
274
|
+
profile.const_set("Edge139", Profile::Edge139)?;
|
|
275
|
+
profile.const_set("Edge140", Profile::Edge140)?;
|
|
276
|
+
profile.const_set("Edge141", Profile::Edge141)?;
|
|
277
|
+
profile.const_set("Edge142", Profile::Edge142)?;
|
|
278
|
+
profile.const_set("Edge143", Profile::Edge143)?;
|
|
279
|
+
profile.const_set("Edge144", Profile::Edge144)?;
|
|
280
|
+
profile.const_set("Edge145", Profile::Edge145)?;
|
|
281
|
+
profile.const_set("Edge146", Profile::Edge146)?;
|
|
282
|
+
profile.const_set("Edge147", Profile::Edge147)?;
|
|
283
|
+
profile.const_set("Edge148", Profile::Edge148)?;
|
|
284
|
+
|
|
285
|
+
profile.const_set("Firefox109", Profile::Firefox109)?;
|
|
286
|
+
profile.const_set("Firefox117", Profile::Firefox117)?;
|
|
287
|
+
profile.const_set("Firefox128", Profile::Firefox128)?;
|
|
288
|
+
profile.const_set("Firefox133", Profile::Firefox133)?;
|
|
289
|
+
profile.const_set("Firefox135", Profile::Firefox135)?;
|
|
290
|
+
profile.const_set("FirefoxPrivate135", Profile::FirefoxPrivate135)?;
|
|
291
|
+
profile.const_set("FirefoxAndroid135", Profile::FirefoxAndroid135)?;
|
|
292
|
+
profile.const_set("Firefox136", Profile::Firefox136)?;
|
|
293
|
+
profile.const_set("FirefoxPrivate136", Profile::FirefoxPrivate136)?;
|
|
294
|
+
profile.const_set("Firefox139", Profile::Firefox139)?;
|
|
295
|
+
profile.const_set("Firefox142", Profile::Firefox142)?;
|
|
296
|
+
profile.const_set("Firefox143", Profile::Firefox143)?;
|
|
297
|
+
profile.const_set("Firefox144", Profile::Firefox144)?;
|
|
298
|
+
profile.const_set("Firefox145", Profile::Firefox145)?;
|
|
299
|
+
profile.const_set("Firefox146", Profile::Firefox146)?;
|
|
300
|
+
profile.const_set("Firefox147", Profile::Firefox147)?;
|
|
301
|
+
profile.const_set("Firefox148", Profile::Firefox148)?;
|
|
302
|
+
profile.const_set("Firefox149", Profile::Firefox149)?;
|
|
303
|
+
profile.const_set("Firefox150", Profile::Firefox150)?;
|
|
304
|
+
profile.const_set("Firefox151", Profile::Firefox151)?;
|
|
305
|
+
|
|
306
|
+
profile.const_set("SafariIos17_2", Profile::SafariIos17_2)?;
|
|
307
|
+
profile.const_set("SafariIos17_4_1", Profile::SafariIos17_4_1)?;
|
|
308
|
+
profile.const_set("SafariIos16_5", Profile::SafariIos16_5)?;
|
|
309
|
+
profile.const_set("Safari15_3", Profile::Safari15_3)?;
|
|
310
|
+
profile.const_set("Safari15_5", Profile::Safari15_5)?;
|
|
311
|
+
profile.const_set("Safari15_6_1", Profile::Safari15_6_1)?;
|
|
312
|
+
profile.const_set("Safari16", Profile::Safari16)?;
|
|
313
|
+
profile.const_set("Safari16_5", Profile::Safari16_5)?;
|
|
314
|
+
profile.const_set("Safari17_0", Profile::Safari17_0)?;
|
|
315
|
+
profile.const_set("Safari17_2_1", Profile::Safari17_2_1)?;
|
|
316
|
+
profile.const_set("Safari17_4_1", Profile::Safari17_4_1)?;
|
|
317
|
+
profile.const_set("Safari17_5", Profile::Safari17_5)?;
|
|
318
|
+
profile.const_set("Safari17_6", Profile::Safari17_6)?;
|
|
319
|
+
profile.const_set("Safari18", Profile::Safari18)?;
|
|
320
|
+
profile.const_set("SafariIPad18", Profile::SafariIPad18)?;
|
|
321
|
+
profile.const_set("Safari18_2", Profile::Safari18_2)?;
|
|
322
|
+
profile.const_set("Safari18_3", Profile::Safari18_3)?;
|
|
323
|
+
profile.const_set("Safari18_3_1", Profile::Safari18_3_1)?;
|
|
324
|
+
profile.const_set("SafariIos18_1_1", Profile::SafariIos18_1_1)?;
|
|
325
|
+
profile.const_set("Safari18_5", Profile::Safari18_5)?;
|
|
326
|
+
profile.const_set("Safari26", Profile::Safari26)?;
|
|
327
|
+
profile.const_set("Safari26_1", Profile::Safari26_1)?;
|
|
328
|
+
profile.const_set("Safari26_2", Profile::Safari26_2)?;
|
|
329
|
+
profile.const_set("Safari26_3", Profile::Safari26_3)?;
|
|
330
|
+
profile.const_set("Safari26_4", Profile::Safari26_4)?;
|
|
331
|
+
profile.const_set("SafariIos26", Profile::SafariIos26)?;
|
|
332
|
+
profile.const_set("SafariIos26_2", Profile::SafariIos26_2)?;
|
|
333
|
+
profile.const_set("SafariIPad26", Profile::SafariIPad26)?;
|
|
334
|
+
profile.const_set("SafariIpad26_2", Profile::SafariIpad26_2)?;
|
|
335
|
+
|
|
336
|
+
profile.const_set("OkHttp3_9", Profile::OkHttp3_9)?;
|
|
337
|
+
profile.const_set("OkHttp3_11", Profile::OkHttp3_11)?;
|
|
338
|
+
profile.const_set("OkHttp3_13", Profile::OkHttp3_13)?;
|
|
339
|
+
profile.const_set("OkHttp3_14", Profile::OkHttp3_14)?;
|
|
340
|
+
profile.const_set("OkHttp4_9", Profile::OkHttp4_9)?;
|
|
341
|
+
profile.const_set("OkHttp4_10", Profile::OkHttp4_10)?;
|
|
342
|
+
profile.const_set("OkHttp4_12", Profile::OkHttp4_12)?;
|
|
343
|
+
profile.const_set("OkHttp5", Profile::OkHttp5)?;
|
|
344
|
+
|
|
345
|
+
profile.const_set("Opera116", Profile::Opera116)?;
|
|
346
|
+
profile.const_set("Opera117", Profile::Opera117)?;
|
|
347
|
+
profile.const_set("Opera118", Profile::Opera118)?;
|
|
348
|
+
profile.const_set("Opera119", Profile::Opera119)?;
|
|
349
|
+
profile.const_set("Opera120", Profile::Opera120)?;
|
|
350
|
+
profile.const_set("Opera121", Profile::Opera121)?;
|
|
351
|
+
profile.const_set("Opera122", Profile::Opera122)?;
|
|
352
|
+
profile.const_set("Opera123", Profile::Opera123)?;
|
|
353
|
+
profile.const_set("Opera124", Profile::Opera124)?;
|
|
354
|
+
profile.const_set("Opera125", Profile::Opera125)?;
|
|
355
|
+
profile.const_set("Opera126", Profile::Opera126)?;
|
|
356
|
+
profile.const_set("Opera127", Profile::Opera127)?;
|
|
357
|
+
profile.const_set("Opera128", Profile::Opera128)?;
|
|
358
|
+
profile.const_set("Opera129", Profile::Opera129)?;
|
|
359
|
+
profile.const_set("Opera130", Profile::Opera130)?;
|
|
360
|
+
profile.const_set("Opera131", Profile::Opera131)?;
|
|
361
|
+
|
|
362
|
+
// Platform enum binding
|
|
363
|
+
let platform = gem_module.define_class("Platform", ruby.class_object())?;
|
|
364
|
+
platform.define_method("to_s", method!(Platform::to_s, 0))?;
|
|
365
|
+
platform.const_set("Windows", Platform::Windows)?;
|
|
366
|
+
platform.const_set("MacOS", Platform::MacOS)?;
|
|
367
|
+
platform.const_set("Linux", Platform::Linux)?;
|
|
368
|
+
platform.const_set("Android", Platform::Android)?;
|
|
369
|
+
platform.const_set("IOS", Platform::IOS)?;
|
|
370
|
+
|
|
371
|
+
// Emulation class binding
|
|
372
|
+
let emulation = gem_module.define_class("Emulation", ruby.class_object())?;
|
|
373
|
+
emulation.define_singleton_method("new", function!(Emulation::new, -1))?;
|
|
374
|
+
Ok(())
|
|
375
|
+
}
|
data/src/error.rs
CHANGED
|
@@ -85,6 +85,14 @@ pub fn interrupt_error() -> MagnusError {
|
|
|
85
85
|
MagnusError::new(ruby!().get_inner(&INTERRUPT_ERROR), "request interrupted")
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
/// LocalJumpError for methods that require a Ruby block.
|
|
89
|
+
pub fn no_block_given_error() -> MagnusError {
|
|
90
|
+
MagnusError::new(
|
|
91
|
+
ruby!().exception_local_jump_error(),
|
|
92
|
+
"no block given (yield)",
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
|
|
88
96
|
/// Map [`tokio::sync::mpsc::error::SendError`] to corresponding [`magnus::Error`]
|
|
89
97
|
pub fn mpsc_send_error_to_magnus<T>(err: SendError<T>) -> MagnusError {
|
|
90
98
|
MagnusError::new(
|
data/src/extractor.rs
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
use bytes::Bytes;
|
|
2
1
|
use magnus::{RArray, RHash, RString, Ruby, TryConvert, r_hash::ForEach};
|
|
3
2
|
use wreq::{
|
|
4
3
|
Proxy, Version,
|
|
@@ -138,41 +137,6 @@ impl TryConvert for Extractor<OrigHeaderMap> {
|
|
|
138
137
|
}
|
|
139
138
|
}
|
|
140
139
|
|
|
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
140
|
// ===== impl Extractor<Proxy> =====
|
|
177
141
|
|
|
178
142
|
impl ExtractorName for Proxy {
|
|
@@ -182,9 +146,9 @@ impl ExtractorName for Proxy {
|
|
|
182
146
|
impl TryConvert for Extractor<Proxy> {
|
|
183
147
|
fn try_convert(value: magnus::Value) -> Result<Self, magnus::Error> {
|
|
184
148
|
let ruby = Ruby::get_with(value);
|
|
185
|
-
let
|
|
149
|
+
let rhash = RHash::try_convert(value)?;
|
|
186
150
|
|
|
187
|
-
if let Some(proxy) =
|
|
151
|
+
if let Some(proxy) = rhash
|
|
188
152
|
.get(ruby.to_symbol(Proxy::NAME))
|
|
189
153
|
.and_then(RString::from_value)
|
|
190
154
|
{
|
data/src/header.rs
CHANGED
|
@@ -3,8 +3,11 @@ use std::cell::RefCell;
|
|
|
3
3
|
use bytes::Bytes;
|
|
4
4
|
use http::{HeaderMap, HeaderName, HeaderValue};
|
|
5
5
|
use magnus::{
|
|
6
|
-
Error, Module, Object, RArray, RModule,
|
|
7
|
-
|
|
6
|
+
Error, Module, Object, RArray, RHash, RModule, RString, Ruby, TryConvert, Value,
|
|
7
|
+
block::Yield,
|
|
8
|
+
function, method,
|
|
9
|
+
r_hash::ForEach,
|
|
10
|
+
typed_data::{Inspect, Obj},
|
|
8
11
|
};
|
|
9
12
|
|
|
10
13
|
use crate::error::{header_name_error_to_magnus, header_value_error_to_magnus};
|
|
@@ -15,7 +18,14 @@ use crate::error::{header_name_error_to_magnus, header_value_error_to_magnus};
|
|
|
15
18
|
/// accessing, modifying, and iterating over header name-value pairs.
|
|
16
19
|
#[derive(Clone, Default)]
|
|
17
20
|
#[magnus::wrap(class = "Wreq::Headers", free_immediately, size)]
|
|
18
|
-
pub struct Headers(RefCell<HeaderMap>);
|
|
21
|
+
pub struct Headers(pub RefCell<HeaderMap>);
|
|
22
|
+
|
|
23
|
+
struct HeaderIter {
|
|
24
|
+
inner: http::header::IntoIter<HeaderValue>,
|
|
25
|
+
next_name: Option<HeaderName>,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// ===== impl Headers =====
|
|
19
29
|
|
|
20
30
|
impl Headers {
|
|
21
31
|
/// Create a new empty Headers instance.
|
|
@@ -132,11 +142,32 @@ impl From<HeaderMap> for Headers {
|
|
|
132
142
|
}
|
|
133
143
|
}
|
|
134
144
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
145
|
+
impl TryConvert for Headers {
|
|
146
|
+
fn try_convert(value: Value) -> Result<Self, Error> {
|
|
147
|
+
if let Some(rhash) = RHash::from_value(value) {
|
|
148
|
+
let mut headers = HeaderMap::new();
|
|
149
|
+
|
|
150
|
+
rhash.foreach(|name: RString, value: RString| {
|
|
151
|
+
let name = HeaderName::from_bytes(&name.to_bytes())
|
|
152
|
+
.map_err(header_name_error_to_magnus)?;
|
|
153
|
+
let value = HeaderValue::from_maybe_shared(value.to_bytes())
|
|
154
|
+
.map_err(header_value_error_to_magnus)?;
|
|
155
|
+
headers.insert(name, value);
|
|
156
|
+
|
|
157
|
+
Ok(ForEach::Continue)
|
|
158
|
+
})?;
|
|
159
|
+
|
|
160
|
+
return Ok(Self::from(headers));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
Obj::<Headers>::try_convert(value)
|
|
164
|
+
.map(|headers| headers.0.clone())
|
|
165
|
+
.map(Self)
|
|
166
|
+
}
|
|
138
167
|
}
|
|
139
168
|
|
|
169
|
+
// ===== impl HeaderIter =====
|
|
170
|
+
|
|
140
171
|
impl Iterator for HeaderIter {
|
|
141
172
|
type Item = (Bytes, Bytes);
|
|
142
173
|
fn next(&mut self) -> Option<Self::Item> {
|
data/src/lib.rs
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
mod macros;
|
|
5
5
|
mod client;
|
|
6
6
|
mod cookie;
|
|
7
|
-
mod
|
|
7
|
+
mod emulate;
|
|
8
8
|
mod error;
|
|
9
9
|
mod extractor;
|
|
10
10
|
mod gvl;
|
|
@@ -91,7 +91,7 @@ fn init(ruby: &Ruby) -> Result<(), Error> {
|
|
|
91
91
|
header::include(ruby, &gem_module)?;
|
|
92
92
|
cookie::include(ruby, &gem_module)?;
|
|
93
93
|
client::include(ruby, &gem_module)?;
|
|
94
|
-
|
|
94
|
+
emulate::include(ruby, &gem_module)?;
|
|
95
95
|
error::include(ruby);
|
|
96
96
|
Ok(())
|
|
97
97
|
}
|
data/src/macros.rs
CHANGED
|
@@ -14,6 +14,11 @@ macro_rules! apply_option {
|
|
|
14
14
|
$builder = $builder.$method(value.0);
|
|
15
15
|
}
|
|
16
16
|
};
|
|
17
|
+
(set_if_some_into_inner, $builder:expr, $option:expr, $method:ident) => {
|
|
18
|
+
if let Some(value) = $option.take() {
|
|
19
|
+
$builder = $builder.$method(value.0.into_inner());
|
|
20
|
+
}
|
|
21
|
+
};
|
|
17
22
|
(set_if_some_map, $builder:expr, $option:expr, $method:ident, $transform:expr) => {
|
|
18
23
|
if let Some(value) = $option.take() {
|
|
19
24
|
$builder = $builder.$method($transform(value));
|
data/src/rt.rs
CHANGED
|
@@ -27,21 +27,3 @@ where
|
|
|
27
27
|
})
|
|
28
28
|
})
|
|
29
29
|
}
|
|
30
|
-
|
|
31
|
-
/// Block on a future to completion on the global Tokio runtime,
|
|
32
|
-
/// returning `None` if cancelled via the provided `CancelFlag`.
|
|
33
|
-
#[inline]
|
|
34
|
-
pub fn maybe_block_on<F, T>(future: F) -> F::Output
|
|
35
|
-
where
|
|
36
|
-
F: Future<Output = Option<T>>,
|
|
37
|
-
{
|
|
38
|
-
gvl::nogvl_cancellable(|flag| {
|
|
39
|
-
RUNTIME.block_on(async move {
|
|
40
|
-
tokio::select! {
|
|
41
|
-
biased;
|
|
42
|
-
_ = flag.cancelled() => None,
|
|
43
|
-
result = future => result,
|
|
44
|
-
}
|
|
45
|
-
})
|
|
46
|
-
})
|
|
47
|
-
}
|
data/test/client_cookie_test.rb
CHANGED
|
@@ -36,7 +36,7 @@ class ClientCookieProviderTest < Minitest::Test
|
|
|
36
36
|
|
|
37
37
|
def test_prepopulated_jar_is_used_by_client
|
|
38
38
|
# pre-populate jar
|
|
39
|
-
@jar.
|
|
39
|
+
@jar.add("pref=1; Path=/", "#{HOST}/")
|
|
40
40
|
|
|
41
41
|
res = @client.get("#{HOST}/cookies")
|
|
42
42
|
assert_equal 200, res.code
|
data/test/cookie_test.rb
CHANGED
|
@@ -19,9 +19,9 @@ class CookieTest < Minitest::Test
|
|
|
19
19
|
assert_equal 0, cookies.length
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
def
|
|
22
|
+
def test_add_and_get_all
|
|
23
23
|
set_cookie = "sid=abc123; Path=/; Domain=example.com; HttpOnly; Secure"
|
|
24
|
-
@jar.
|
|
24
|
+
@jar.add(set_cookie, @base_url)
|
|
25
25
|
|
|
26
26
|
cookies = @jar.get_all
|
|
27
27
|
assert_kind_of Array, cookies
|
|
@@ -42,9 +42,9 @@ class CookieTest < Minitest::Test
|
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
def test_add_multiple_and_remove
|
|
45
|
-
@jar.
|
|
46
|
-
@jar.
|
|
47
|
-
@jar.
|
|
45
|
+
@jar.add("a=1; Path=/", @base_url)
|
|
46
|
+
@jar.add("b=2; Path=/", @base_url)
|
|
47
|
+
@jar.add("c=3; Path=/", @base_url)
|
|
48
48
|
|
|
49
49
|
cookies = @jar.get_all
|
|
50
50
|
assert_equal 3, cookies.length
|
|
@@ -58,8 +58,8 @@ class CookieTest < Minitest::Test
|
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
def test_clear
|
|
61
|
-
@jar.
|
|
62
|
-
@jar.
|
|
61
|
+
@jar.add("x=1; Path=/", @base_url)
|
|
62
|
+
@jar.add("y=2; Path=/", @base_url)
|
|
63
63
|
refute_empty @jar.get_all
|
|
64
64
|
|
|
65
65
|
@jar.clear
|
|
@@ -69,7 +69,7 @@ class CookieTest < Minitest::Test
|
|
|
69
69
|
def test_max_age_and_expires_optional
|
|
70
70
|
# Max-Age only
|
|
71
71
|
@jar.clear
|
|
72
|
-
@jar.
|
|
72
|
+
@jar.add("ma=1; Max-Age=3600; Path=/", @base_url)
|
|
73
73
|
c1 = @jar.get_all.find { |c| c.name == "ma" }
|
|
74
74
|
assert c1
|
|
75
75
|
# can be nil or Integer; just ensure responds and is truthy integer
|
|
@@ -81,7 +81,7 @@ class CookieTest < Minitest::Test
|
|
|
81
81
|
# Expires only
|
|
82
82
|
@jar.clear
|
|
83
83
|
t = Time.now + 3600
|
|
84
|
-
@jar.
|
|
84
|
+
@jar.add("exp=1; Expires=#{t.gmtime.strftime("%a, %d %b %Y %H:%M:%S GMT")}; Path=/", @base_url)
|
|
85
85
|
c2 = @jar.get_all.find { |c| c.name == "exp" }
|
|
86
86
|
assert c2
|
|
87
87
|
# expires returns Float (unix seconds) or nil
|
|
@@ -144,8 +144,8 @@ class CookieTest < Minitest::Test
|
|
|
144
144
|
|
|
145
145
|
def test_same_site_flags_from_parsed_header
|
|
146
146
|
@jar.clear
|
|
147
|
-
@jar.
|
|
148
|
-
@jar.
|
|
147
|
+
@jar.add("s1=1; Path=/; SameSite=Strict", @base_url)
|
|
148
|
+
@jar.add("s2=1; Path=/; SameSite=Lax", @base_url)
|
|
149
149
|
|
|
150
150
|
cookies = @jar.get_all
|
|
151
151
|
h = cookies.to_h { |ck| [ck.name, [ck.same_site_strict?, ck.same_site_lax?]] }
|
|
@@ -154,13 +154,27 @@ class CookieTest < Minitest::Test
|
|
|
154
154
|
assert_equal [false, true], h["s2"]
|
|
155
155
|
end
|
|
156
156
|
|
|
157
|
-
def
|
|
158
|
-
raw_value = "hello world?"
|
|
157
|
+
def test_request_uncompressed_cookies
|
|
159
158
|
client = Wreq::Client.new
|
|
160
159
|
resp = client.get(
|
|
161
|
-
"
|
|
162
|
-
cookies: {"
|
|
160
|
+
"https://httpbin.io/cookies",
|
|
161
|
+
cookies: {"foo" => "bar", "baz" => "qux"}
|
|
163
162
|
)
|
|
164
|
-
|
|
163
|
+
json = resp.json
|
|
164
|
+
assert_instance_of Hash, json
|
|
165
|
+
assert_equal "bar", json["foo"]
|
|
166
|
+
assert_equal "qux", json["baz"]
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def test_request_compressed_cookies
|
|
170
|
+
client = Wreq::Client.new
|
|
171
|
+
resp = client.get(
|
|
172
|
+
"https://httpbin.io/cookies",
|
|
173
|
+
cookies: "foo=bar; baz=qux"
|
|
174
|
+
)
|
|
175
|
+
json = resp.json
|
|
176
|
+
assert_instance_of Hash, json
|
|
177
|
+
assert_equal "bar", json["foo"]
|
|
178
|
+
assert_equal "qux", json["baz"]
|
|
165
179
|
end
|
|
166
180
|
end
|