wreq 1.2.12 → 1.2.13
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 +1 -1
- data/Cargo.toml +1 -1
- data/docs/fork-safety.md +49 -32
- data/docs/interrupt-handling.md +4 -3
- data/lib/wreq.rb +36 -12
- data/lib/wreq_ruby/body.rb +9 -6
- data/lib/wreq_ruby/client.rb +18 -14
- data/lib/wreq_ruby/cookie.rb +9 -0
- data/lib/wreq_ruby/error.rb +7 -5
- data/lib/wreq_ruby/response.rb +23 -7
- data/src/arch.rs +83 -37
- data/src/client/body/stream.rs +15 -24
- data/src/client/req.rs +119 -122
- data/src/client/resp.rs +101 -55
- data/src/client.rs +78 -36
- data/src/cookie.rs +47 -11
- data/src/error.rs +1 -1
- data/src/lib.rs +0 -2
- data/src/macros.rs +0 -1
- data/src/rt.rs +20 -36
- data/test/fork_test.rb +30 -6
- data/test/scripts/fork_safety.rb +66 -43
- data/test/scripts/prefork_runtime.rb +95 -0
- metadata +2 -1
data/src/arch.rs
CHANGED
|
@@ -8,35 +8,63 @@
|
|
|
8
8
|
|
|
9
9
|
use std::mem::ManuallyDrop;
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
use magnus::Ruby;
|
|
12
|
+
|
|
13
|
+
#[cfg(unix)]
|
|
14
|
+
use crate::error::fork_error;
|
|
15
|
+
|
|
16
|
+
/// Native state that belongs to the process where it was created.
|
|
12
17
|
///
|
|
13
18
|
/// A forked child must not destroy inherited clients, channels, or response
|
|
14
19
|
/// bodies because their synchronization state may belong to threads that no
|
|
15
20
|
/// longer exist. The child intentionally leaks the value and lets the operating
|
|
16
21
|
/// system reclaim it when the process exits.
|
|
17
22
|
///
|
|
18
|
-
///
|
|
19
|
-
///
|
|
20
|
-
|
|
21
|
-
|
|
23
|
+
/// `Send` and `Sync` only describe access between threads in one process. They
|
|
24
|
+
/// do not make a runtime, lock, channel, or connection pool safe after `fork`.
|
|
25
|
+
///
|
|
26
|
+
/// [`ProcessLocal::get`] is the only access path and checks the object's own
|
|
27
|
+
/// process generation before exposing its value.
|
|
28
|
+
pub(crate) struct ProcessLocal<T> {
|
|
29
|
+
value: ManuallyDrop<T>,
|
|
30
|
+
#[cfg(unix)]
|
|
31
|
+
owner: unix::ProcessToken,
|
|
32
|
+
}
|
|
22
33
|
|
|
23
34
|
impl<T> ProcessLocal<T> {
|
|
24
35
|
/// Wrap native state created by the current process.
|
|
25
36
|
pub(crate) fn new(value: T) -> Self {
|
|
26
|
-
Self
|
|
37
|
+
Self {
|
|
38
|
+
value: ManuallyDrop::new(value),
|
|
39
|
+
#[cfg(unix)]
|
|
40
|
+
owner: unix::ProcessToken::current(),
|
|
41
|
+
}
|
|
27
42
|
}
|
|
28
|
-
}
|
|
29
43
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
44
|
+
/// Borrow native state only from the process that created it.
|
|
45
|
+
///
|
|
46
|
+
/// # Errors
|
|
47
|
+
///
|
|
48
|
+
/// Returns `Wreq::ForkError` when the value was inherited from a parent
|
|
49
|
+
/// process.
|
|
50
|
+
#[inline]
|
|
51
|
+
pub(crate) fn get(&self, ruby: &Ruby) -> Result<&T, magnus::Error> {
|
|
52
|
+
#[cfg(unix)]
|
|
53
|
+
if let Some((owner_pid, current_pid)) = self.owner.forked_process_ids() {
|
|
54
|
+
return Err(fork_error(ruby, owner_pid, current_pid));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
#[cfg(not(unix))]
|
|
58
|
+
let _ = ruby;
|
|
59
|
+
|
|
60
|
+
Ok(&self.value)
|
|
33
61
|
}
|
|
34
62
|
}
|
|
35
63
|
|
|
36
64
|
impl<T> Drop for ProcessLocal<T> {
|
|
37
65
|
fn drop(&mut self) {
|
|
38
66
|
#[cfg(unix)]
|
|
39
|
-
if forked_process_ids().is_some() {
|
|
67
|
+
if self.owner.forked_process_ids().is_some() {
|
|
40
68
|
return;
|
|
41
69
|
}
|
|
42
70
|
|
|
@@ -44,7 +72,7 @@ impl<T> Drop for ProcessLocal<T> {
|
|
|
44
72
|
// prevents an automatic second drop, and this wrapper's `Drop`
|
|
45
73
|
// implementation runs at most once.
|
|
46
74
|
unsafe {
|
|
47
|
-
ManuallyDrop::drop(&mut self.
|
|
75
|
+
ManuallyDrop::drop(&mut self.value);
|
|
48
76
|
}
|
|
49
77
|
}
|
|
50
78
|
}
|
|
@@ -74,54 +102,72 @@ pub(crate) const SUPPORTS_INTERFACE: bool = cfg!(any(
|
|
|
74
102
|
mod unix {
|
|
75
103
|
use std::{io, process, sync::OnceLock};
|
|
76
104
|
|
|
77
|
-
///
|
|
105
|
+
/// Identity of the process generation that created native state.
|
|
78
106
|
///
|
|
79
|
-
///
|
|
80
|
-
///
|
|
107
|
+
/// Forkguard's child callback only advances an atomic generation counter.
|
|
108
|
+
/// The PID is retained for diagnostics and as a fallback if registering
|
|
109
|
+
/// the callback fails.
|
|
81
110
|
/// https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_atfork.html
|
|
82
|
-
struct
|
|
83
|
-
detector: forkguard::Guard
|
|
111
|
+
pub(super) struct ProcessToken {
|
|
112
|
+
detector: Option<forkguard::Guard>,
|
|
84
113
|
owner_pid: u32,
|
|
85
114
|
}
|
|
86
115
|
|
|
87
|
-
impl
|
|
88
|
-
///
|
|
89
|
-
fn
|
|
116
|
+
impl ProcessToken {
|
|
117
|
+
/// Capture the current process and fork generation.
|
|
118
|
+
pub(super) fn current() -> Self {
|
|
119
|
+
Self::try_current().unwrap_or_else(|_| Self {
|
|
120
|
+
detector: None,
|
|
121
|
+
owner_pid: process::id(),
|
|
122
|
+
})
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/// Capture the current process after registering fork detection.
|
|
126
|
+
fn try_current() -> io::Result<Self> {
|
|
90
127
|
forkguard::Guard::try_new()
|
|
91
128
|
.map(|detector| Self {
|
|
92
|
-
detector,
|
|
129
|
+
detector: Some(detector),
|
|
93
130
|
owner_pid: process::id(),
|
|
94
131
|
})
|
|
95
132
|
.map_err(|error| io::Error::from_raw_os_error(error.code().get()))
|
|
96
133
|
}
|
|
97
134
|
|
|
98
|
-
/// Return process IDs when this
|
|
99
|
-
fn forked_process_ids(&self) -> Option<(u32, u32)> {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
135
|
+
/// Return process IDs when this token was inherited through a fork.
|
|
136
|
+
pub(super) fn forked_process_ids(&self) -> Option<(u32, u32)> {
|
|
137
|
+
if let Some(detector) = &self.detector {
|
|
138
|
+
// Keep the stored generation unchanged so repeated accesses
|
|
139
|
+
// continue to reject the same inherited object. Cloning the
|
|
140
|
+
// detector copies one usize.
|
|
141
|
+
return detector
|
|
142
|
+
.clone()
|
|
143
|
+
.detected_fork()
|
|
144
|
+
.then(|| (self.owner_pid, process::id()));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
let current_pid = process::id();
|
|
148
|
+
(self.owner_pid != current_pid).then_some((self.owner_pid, current_pid))
|
|
106
149
|
}
|
|
107
150
|
}
|
|
108
151
|
|
|
109
|
-
|
|
152
|
+
/// Runtime owner captured when Tokio first initializes.
|
|
153
|
+
static RUNTIME_OWNER: OnceLock<ProcessToken> = OnceLock::new();
|
|
110
154
|
|
|
111
|
-
/// Register process fork tracking before the
|
|
155
|
+
/// Register process fork tracking before the Tokio runtime is initialized.
|
|
112
156
|
pub(crate) fn initialize_fork_tracking() -> io::Result<()> {
|
|
113
|
-
if
|
|
157
|
+
if RUNTIME_OWNER.get().is_some() {
|
|
114
158
|
return Ok(());
|
|
115
159
|
}
|
|
116
160
|
|
|
117
|
-
let
|
|
118
|
-
let _ =
|
|
161
|
+
let owner = ProcessToken::try_current()?;
|
|
162
|
+
let _ = RUNTIME_OWNER.set(owner);
|
|
119
163
|
Ok(())
|
|
120
164
|
}
|
|
121
165
|
|
|
122
|
-
/// Return process IDs
|
|
166
|
+
/// Return process IDs when this process inherited an initialized runtime.
|
|
123
167
|
pub(crate) fn forked_process_ids() -> Option<(u32, u32)> {
|
|
124
|
-
|
|
168
|
+
RUNTIME_OWNER
|
|
169
|
+
.get()
|
|
170
|
+
.and_then(ProcessToken::forked_process_ids)
|
|
125
171
|
}
|
|
126
172
|
}
|
|
127
173
|
|
|
@@ -175,7 +221,7 @@ mod tests {
|
|
|
175
221
|
|
|
176
222
|
{
|
|
177
223
|
let value = ProcessLocal::new(DropCounter(&drops));
|
|
178
|
-
assert_eq!(value.
|
|
224
|
+
assert_eq!(value.value.0.get(), 0);
|
|
179
225
|
}
|
|
180
226
|
|
|
181
227
|
assert_eq!(drops.get(), 1);
|
data/src/client/body/stream.rs
CHANGED
|
@@ -65,17 +65,14 @@ impl BodyReceiver {
|
|
|
65
65
|
|
|
66
66
|
/// Read the next body chunk, converting stream errors into Ruby errors.
|
|
67
67
|
pub fn next(&self, ruby: &Ruby) -> Result<Option<Bytes>, Error> {
|
|
68
|
-
rt::
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
},
|
|
77
|
-
wreq_error,
|
|
78
|
-
)
|
|
68
|
+
rt::block_on(ruby, async {
|
|
69
|
+
match self.0.lock().await.as_mut().next().await {
|
|
70
|
+
Some(Ok(data)) => Ok(Some(data)),
|
|
71
|
+
Some(Err(err)) => Err(err),
|
|
72
|
+
None => Ok(None),
|
|
73
|
+
}
|
|
74
|
+
})?
|
|
75
|
+
.map_err(|err| wreq_error(ruby, err))
|
|
79
76
|
}
|
|
80
77
|
}
|
|
81
78
|
|
|
@@ -90,10 +87,8 @@ impl BodySender {
|
|
|
90
87
|
/// # Errors
|
|
91
88
|
///
|
|
92
89
|
/// Returns `TypeError` for a non-Integer capacity and `ArgumentError` for
|
|
93
|
-
/// an invalid range or argument count.
|
|
94
|
-
/// creating a channel in a child that inherited the extension.
|
|
90
|
+
/// an invalid range or argument count.
|
|
95
91
|
pub fn new(ruby: &Ruby, args: &[Value]) -> Result<Self, Error> {
|
|
96
|
-
rt::ensure_current(ruby)?;
|
|
97
92
|
let capacity = parse_capacity(ruby, args)?;
|
|
98
93
|
|
|
99
94
|
// Create the Tokio channel without allowing an unwind to cross the Ruby FFI boundary.
|
|
@@ -121,8 +116,6 @@ impl BodySender {
|
|
|
121
116
|
/// wait raises `Wreq::InterruptError`. Returns `Wreq::ForkError` before
|
|
122
117
|
/// reading an inherited channel.
|
|
123
118
|
pub fn push(ruby: &Ruby, rb_self: &Self, data: RString) -> Result<(), Error> {
|
|
124
|
-
rt::ensure_current(ruby)?;
|
|
125
|
-
|
|
126
119
|
// Clone during the shared borrow, then release it before waiting
|
|
127
120
|
// for capacity. Request attachment needs a mutable borrow.
|
|
128
121
|
let tx = match &rb_self.read_inner(ruby)?.tx {
|
|
@@ -130,7 +123,8 @@ impl BodySender {
|
|
|
130
123
|
_ => return Err(closed_body_sender_error(ruby)),
|
|
131
124
|
};
|
|
132
125
|
|
|
133
|
-
rt::
|
|
126
|
+
rt::block_on(ruby, tx.send(data.to_bytes()))?
|
|
127
|
+
.map_err(|err| body_sender_send_error(ruby, err))
|
|
134
128
|
}
|
|
135
129
|
|
|
136
130
|
/// Close the producing side while retaining the receiver and queued chunks.
|
|
@@ -142,7 +136,6 @@ impl BodySender {
|
|
|
142
136
|
/// Returns `Wreq::ForkError` before reading an inherited channel, or
|
|
143
137
|
/// `Wreq::BodyError` if the internal state is already borrowed.
|
|
144
138
|
pub fn close(ruby: &Ruby, rb_self: &Self) -> Result<(), Error> {
|
|
145
|
-
rt::ensure_current(ruby)?;
|
|
146
139
|
let mut inner = rb_self.write_inner(ruby)?;
|
|
147
140
|
inner.tx.take();
|
|
148
141
|
Ok(())
|
|
@@ -155,22 +148,21 @@ impl BodySender {
|
|
|
155
148
|
/// Returns `Wreq::ForkError` before reading an inherited channel, or
|
|
156
149
|
/// `Wreq::BodyError` if the internal state is already borrowed.
|
|
157
150
|
pub fn is_closed(ruby: &Ruby, rb_self: &Self) -> Result<bool, Error> {
|
|
158
|
-
rt::ensure_current(ruby)?;
|
|
159
151
|
rb_self.read_inner(ruby).map(|r| r.is_closed())
|
|
160
152
|
}
|
|
161
153
|
|
|
162
|
-
/// Borrow
|
|
154
|
+
/// Borrow channel state only in the process that created this sender.
|
|
163
155
|
fn read_inner(&self, ruby: &Ruby) -> Result<Ref<'_, InnerBodySender>, Error> {
|
|
164
156
|
self.0
|
|
165
|
-
.
|
|
157
|
+
.get(ruby)?
|
|
166
158
|
.try_borrow()
|
|
167
159
|
.map_err(|err| body_sender_borrow_error(ruby, err))
|
|
168
160
|
}
|
|
169
161
|
|
|
170
|
-
/// Mutably borrow
|
|
162
|
+
/// Mutably borrow channel state only in the process that created this sender.
|
|
171
163
|
fn write_inner(&self, ruby: &Ruby) -> Result<RefMut<'_, InnerBodySender>, Error> {
|
|
172
164
|
self.0
|
|
173
|
-
.
|
|
165
|
+
.get(ruby)?
|
|
174
166
|
.try_borrow_mut()
|
|
175
167
|
.map_err(|err| body_sender_borrow_mut_error(ruby, err))
|
|
176
168
|
}
|
|
@@ -182,7 +174,6 @@ impl BodySender {
|
|
|
182
174
|
/// Returns `Wreq::MemoryError` if the receiver was already consumed, or
|
|
183
175
|
/// `Wreq::BodyError` if Ruby re-enters while the state is borrowed.
|
|
184
176
|
pub(super) fn take_receiver(&self, ruby: &Ruby) -> Result<ReceiverStream<Bytes>, Error> {
|
|
185
|
-
rt::ensure_current(ruby)?;
|
|
186
177
|
self.write_inner(ruby)?
|
|
187
178
|
.rx
|
|
188
179
|
.take()
|
data/src/client/req.rs
CHANGED
|
@@ -184,131 +184,128 @@ pub fn execute_request<U: AsRef<str>>(
|
|
|
184
184
|
url: U,
|
|
185
185
|
mut request: Request,
|
|
186
186
|
) -> Result<Response, magnus::Error> {
|
|
187
|
-
rt::
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
Duration::from_secs
|
|
218
|
-
);
|
|
219
|
-
|
|
220
|
-
// Network options.
|
|
221
|
-
apply_option!(set_if_some, builder, request.proxy, proxy);
|
|
222
|
-
apply_option!(set_if_some, builder, request.local_address, local_address);
|
|
223
|
-
#[cfg(any(
|
|
224
|
-
target_os = "android",
|
|
225
|
-
target_os = "fuchsia",
|
|
226
|
-
target_os = "illumos",
|
|
227
|
-
target_os = "ios",
|
|
228
|
-
target_os = "linux",
|
|
229
|
-
target_os = "macos",
|
|
230
|
-
target_os = "solaris",
|
|
231
|
-
target_os = "tvos",
|
|
232
|
-
target_os = "visionos",
|
|
233
|
-
target_os = "watchos",
|
|
234
|
-
))]
|
|
235
|
-
apply_option!(set_if_some, builder, request.interface, interface);
|
|
236
|
-
|
|
237
|
-
// Headers options.
|
|
238
|
-
apply_option!(set_if_some_into_inner, builder, request.headers, headers);
|
|
239
|
-
apply_option!(
|
|
240
|
-
set_if_some_inner,
|
|
241
|
-
builder,
|
|
242
|
-
request.orig_headers,
|
|
243
|
-
orig_headers
|
|
244
|
-
);
|
|
245
|
-
apply_option!(
|
|
246
|
-
set_if_some,
|
|
247
|
-
builder,
|
|
248
|
-
request.default_headers,
|
|
249
|
-
default_headers
|
|
250
|
-
);
|
|
251
|
-
|
|
252
|
-
// Cookies options.
|
|
253
|
-
if let Some(cookies) = request.cookies.take() {
|
|
254
|
-
for cookie in cookies.0 {
|
|
255
|
-
builder = builder.header(header::COOKIE, cookie);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
187
|
+
rt::block_on(ruby, async move {
|
|
188
|
+
let mut builder = client.request(method.into_ffi(), url.as_ref());
|
|
189
|
+
|
|
190
|
+
// Emulation options.
|
|
191
|
+
apply_option!(set_if_some_inner, builder, request.emulation, emulation);
|
|
192
|
+
|
|
193
|
+
// Version options.
|
|
194
|
+
apply_option!(
|
|
195
|
+
set_if_some_map,
|
|
196
|
+
builder,
|
|
197
|
+
request.version,
|
|
198
|
+
version,
|
|
199
|
+
Version::into_ffi
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
// Timeout options.
|
|
203
|
+
apply_option!(
|
|
204
|
+
set_if_some_map,
|
|
205
|
+
builder,
|
|
206
|
+
request.timeout,
|
|
207
|
+
timeout,
|
|
208
|
+
Duration::from_secs
|
|
209
|
+
);
|
|
210
|
+
apply_option!(
|
|
211
|
+
set_if_some_map,
|
|
212
|
+
builder,
|
|
213
|
+
request.read_timeout,
|
|
214
|
+
read_timeout,
|
|
215
|
+
Duration::from_secs
|
|
216
|
+
);
|
|
258
217
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
218
|
+
// Network options.
|
|
219
|
+
apply_option!(set_if_some, builder, request.proxy, proxy);
|
|
220
|
+
apply_option!(set_if_some, builder, request.local_address, local_address);
|
|
221
|
+
#[cfg(any(
|
|
222
|
+
target_os = "android",
|
|
223
|
+
target_os = "fuchsia",
|
|
224
|
+
target_os = "illumos",
|
|
225
|
+
target_os = "ios",
|
|
226
|
+
target_os = "linux",
|
|
227
|
+
target_os = "macos",
|
|
228
|
+
target_os = "solaris",
|
|
229
|
+
target_os = "tvos",
|
|
230
|
+
target_os = "visionos",
|
|
231
|
+
target_os = "watchos",
|
|
232
|
+
))]
|
|
233
|
+
apply_option!(set_if_some, builder, request.interface, interface);
|
|
234
|
+
|
|
235
|
+
// Headers options.
|
|
236
|
+
apply_option!(set_if_some_into_inner, builder, request.headers, headers);
|
|
237
|
+
apply_option!(
|
|
238
|
+
set_if_some_inner,
|
|
239
|
+
builder,
|
|
240
|
+
request.orig_headers,
|
|
241
|
+
orig_headers
|
|
242
|
+
);
|
|
243
|
+
apply_option!(
|
|
244
|
+
set_if_some,
|
|
245
|
+
builder,
|
|
246
|
+
request.default_headers,
|
|
247
|
+
default_headers
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
// Cookies options.
|
|
251
|
+
if let Some(cookies) = request.cookies.take() {
|
|
252
|
+
for cookie in cookies.0 {
|
|
253
|
+
builder = builder.header(header::COOKIE, cookie);
|
|
270
254
|
}
|
|
255
|
+
}
|
|
271
256
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
// Form options.
|
|
299
|
-
apply_option!(set_if_some_ref, builder, request.form, form);
|
|
300
|
-
|
|
301
|
-
// JSON options.
|
|
302
|
-
apply_option!(set_if_some_ref, builder, request.json, json);
|
|
303
|
-
|
|
304
|
-
// Body options.
|
|
305
|
-
if let Some(body) = request.body.take() {
|
|
306
|
-
builder = builder.body(wreq::Body::from(body));
|
|
257
|
+
// Authentication options.
|
|
258
|
+
apply_option!(
|
|
259
|
+
set_if_some_map_ref,
|
|
260
|
+
builder,
|
|
261
|
+
request.auth,
|
|
262
|
+
auth,
|
|
263
|
+
AsRef::<str>::as_ref
|
|
264
|
+
);
|
|
265
|
+
apply_option!(set_if_some, builder, request.bearer_auth, bearer_auth);
|
|
266
|
+
if let Some(basic_auth) = request.basic_auth.take() {
|
|
267
|
+
builder = builder.basic_auth(basic_auth.0, basic_auth.1);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Allow redirects options.
|
|
271
|
+
match request.allow_redirects {
|
|
272
|
+
Some(false) => {
|
|
273
|
+
builder = builder.redirect(wreq::redirect::Policy::none());
|
|
274
|
+
}
|
|
275
|
+
Some(true) => {
|
|
276
|
+
builder = builder.redirect(
|
|
277
|
+
request
|
|
278
|
+
.max_redirects
|
|
279
|
+
.take()
|
|
280
|
+
.map(wreq::redirect::Policy::limited)
|
|
281
|
+
.unwrap_or_default(),
|
|
282
|
+
);
|
|
307
283
|
}
|
|
284
|
+
None => {}
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
// Compression options.
|
|
288
|
+
apply_option!(set_if_some, builder, request.gzip, gzip);
|
|
289
|
+
apply_option!(set_if_some, builder, request.brotli, brotli);
|
|
290
|
+
apply_option!(set_if_some, builder, request.deflate, deflate);
|
|
291
|
+
apply_option!(set_if_some, builder, request.zstd, zstd);
|
|
292
|
+
|
|
293
|
+
// Query options.
|
|
294
|
+
apply_option!(set_if_some_ref, builder, request.query, query);
|
|
295
|
+
|
|
296
|
+
// Form options.
|
|
297
|
+
apply_option!(set_if_some_ref, builder, request.form, form);
|
|
298
|
+
|
|
299
|
+
// JSON options.
|
|
300
|
+
apply_option!(set_if_some_ref, builder, request.json, json);
|
|
301
|
+
|
|
302
|
+
// Body options.
|
|
303
|
+
if let Some(body) = request.body.take() {
|
|
304
|
+
builder = builder.body(wreq::Body::from(body));
|
|
305
|
+
}
|
|
308
306
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
)
|
|
307
|
+
// Send request.
|
|
308
|
+
builder.send().await.map(Response::new)
|
|
309
|
+
})?
|
|
310
|
+
.map_err(|err| wreq_error(ruby, err))
|
|
314
311
|
}
|