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/client/req.rs
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
use std::{net::IpAddr, time::Duration};
|
|
2
|
+
|
|
3
|
+
use http::{HeaderValue, header};
|
|
4
|
+
use magnus::{RHash, TryConvert, typed_data::Obj, value::ReprValue};
|
|
5
|
+
use serde::Deserialize;
|
|
6
|
+
use wreq::{
|
|
7
|
+
Client, Proxy, Version,
|
|
8
|
+
header::{HeaderMap, OrigHeaderMap},
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
use super::body::{Body, Form, Json};
|
|
12
|
+
use crate::{
|
|
13
|
+
client::{query::Query, resp::Response},
|
|
14
|
+
emulation::Emulation,
|
|
15
|
+
error::wreq_error_to_magnus,
|
|
16
|
+
extractor::Extractor,
|
|
17
|
+
http::Method,
|
|
18
|
+
rt,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/// The parameters for a request.
|
|
22
|
+
#[derive(Default, Deserialize)]
|
|
23
|
+
#[non_exhaustive]
|
|
24
|
+
pub struct Request {
|
|
25
|
+
/// The emulation option for the request.
|
|
26
|
+
#[serde(skip)]
|
|
27
|
+
emulation: Option<Emulation>,
|
|
28
|
+
|
|
29
|
+
/// The proxy to use for the request.
|
|
30
|
+
#[serde(skip)]
|
|
31
|
+
proxy: Option<Proxy>,
|
|
32
|
+
|
|
33
|
+
/// Bind to a local IP Address.
|
|
34
|
+
local_address: Option<IpAddr>,
|
|
35
|
+
|
|
36
|
+
/// Bind to an interface by `SO_BINDTODEVICE`.
|
|
37
|
+
interface: Option<String>,
|
|
38
|
+
|
|
39
|
+
/// The timeout to use for the request.
|
|
40
|
+
timeout: Option<u64>,
|
|
41
|
+
|
|
42
|
+
/// The read timeout to use for the request.
|
|
43
|
+
read_timeout: Option<u64>,
|
|
44
|
+
|
|
45
|
+
/// The HTTP version to use for the request.
|
|
46
|
+
#[serde(skip)]
|
|
47
|
+
version: Option<Version>,
|
|
48
|
+
|
|
49
|
+
/// The headers to use for the request.
|
|
50
|
+
#[serde(skip)]
|
|
51
|
+
headers: Option<HeaderMap>,
|
|
52
|
+
|
|
53
|
+
/// The original headers to use for the request.
|
|
54
|
+
#[serde(skip)]
|
|
55
|
+
orig_headers: Option<OrigHeaderMap>,
|
|
56
|
+
|
|
57
|
+
/// The option enables default headers.
|
|
58
|
+
default_headers: Option<bool>,
|
|
59
|
+
|
|
60
|
+
/// The cookies to use for the request.
|
|
61
|
+
#[serde(skip)]
|
|
62
|
+
cookies: Option<Vec<HeaderValue>>,
|
|
63
|
+
|
|
64
|
+
/// Whether to allow redirects.
|
|
65
|
+
allow_redirects: Option<bool>,
|
|
66
|
+
|
|
67
|
+
/// The maximum number of redirects to follow.
|
|
68
|
+
max_redirects: Option<usize>,
|
|
69
|
+
|
|
70
|
+
/// Sets gzip as an accepted encoding.
|
|
71
|
+
gzip: Option<bool>,
|
|
72
|
+
|
|
73
|
+
/// Sets brotli as an accepted encoding.
|
|
74
|
+
brotli: Option<bool>,
|
|
75
|
+
|
|
76
|
+
/// Sets deflate as an accepted encoding.
|
|
77
|
+
deflate: Option<bool>,
|
|
78
|
+
|
|
79
|
+
/// Sets zstd as an accepted encoding.
|
|
80
|
+
zstd: Option<bool>,
|
|
81
|
+
|
|
82
|
+
/// The authentication to use for the request.
|
|
83
|
+
auth: Option<String>,
|
|
84
|
+
|
|
85
|
+
/// The bearer authentication to use for the request.
|
|
86
|
+
bearer_auth: Option<String>,
|
|
87
|
+
|
|
88
|
+
/// The basic authentication to use for the request.
|
|
89
|
+
basic_auth: Option<(String, Option<String>)>,
|
|
90
|
+
|
|
91
|
+
/// The query parameters to use for the request.
|
|
92
|
+
query: Option<Query>,
|
|
93
|
+
|
|
94
|
+
/// The form parameters to use for the request.
|
|
95
|
+
form: Option<Form>,
|
|
96
|
+
|
|
97
|
+
/// The JSON body to use for the request.
|
|
98
|
+
json: Option<Json>,
|
|
99
|
+
|
|
100
|
+
/// The body to use for the request.
|
|
101
|
+
#[serde(skip)]
|
|
102
|
+
body: Option<Body>,
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
impl Request {
|
|
106
|
+
/// Create a new [`Request`] from Ruby keyword arguments.
|
|
107
|
+
pub fn new(ruby: &magnus::Ruby, hash: RHash) -> Result<Self, magnus::Error> {
|
|
108
|
+
let kwargs = hash.as_value();
|
|
109
|
+
let mut builder: Self = serde_magnus::deserialize(ruby, kwargs)?;
|
|
110
|
+
|
|
111
|
+
// extra emulation handling
|
|
112
|
+
if let Some(v) = hash.get(ruby.to_symbol("emulation")) {
|
|
113
|
+
let emulation_obj = Obj::<Emulation>::try_convert(v)?;
|
|
114
|
+
builder.emulation = Some((*emulation_obj).clone());
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// extra version handling
|
|
118
|
+
builder.version = Extractor::<Version>::try_convert(kwargs)?.into_inner();
|
|
119
|
+
|
|
120
|
+
// extra headers handling
|
|
121
|
+
builder.headers = Extractor::<HeaderMap>::try_convert(kwargs)?.into_inner();
|
|
122
|
+
|
|
123
|
+
// extra original headers handling
|
|
124
|
+
builder.orig_headers = Extractor::<OrigHeaderMap>::try_convert(kwargs)?.into_inner();
|
|
125
|
+
|
|
126
|
+
// extra cookies handling
|
|
127
|
+
builder.cookies = Extractor::<Vec<HeaderValue>>::try_convert(kwargs)?.into_inner();
|
|
128
|
+
|
|
129
|
+
// extra proxy handling
|
|
130
|
+
builder.proxy = Extractor::<Proxy>::try_convert(kwargs)?.into_inner();
|
|
131
|
+
|
|
132
|
+
// extra body handling
|
|
133
|
+
if let Some(body) = hash.get(ruby.to_symbol("body")) {
|
|
134
|
+
builder.body = Some(Body::try_convert(body)?);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
Ok(builder)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
pub fn execute_request<U: AsRef<str>>(
|
|
142
|
+
client: Client,
|
|
143
|
+
method: Method,
|
|
144
|
+
url: U,
|
|
145
|
+
mut request: Request,
|
|
146
|
+
) -> Result<Response, magnus::Error> {
|
|
147
|
+
rt::try_block_on(async move {
|
|
148
|
+
let mut builder = client.request(method.into_ffi(), url.as_ref());
|
|
149
|
+
|
|
150
|
+
// Emulation options.
|
|
151
|
+
apply_option!(set_if_some_inner, builder, request.emulation, emulation);
|
|
152
|
+
|
|
153
|
+
// Version options.
|
|
154
|
+
apply_option!(set_if_some, builder, request.version, version);
|
|
155
|
+
|
|
156
|
+
// Timeout options.
|
|
157
|
+
apply_option!(
|
|
158
|
+
set_if_some_map,
|
|
159
|
+
builder,
|
|
160
|
+
request.timeout,
|
|
161
|
+
timeout,
|
|
162
|
+
Duration::from_secs
|
|
163
|
+
);
|
|
164
|
+
apply_option!(
|
|
165
|
+
set_if_some_map,
|
|
166
|
+
builder,
|
|
167
|
+
request.read_timeout,
|
|
168
|
+
read_timeout,
|
|
169
|
+
Duration::from_secs
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
// Network options.
|
|
173
|
+
apply_option!(set_if_some, builder, request.proxy, proxy);
|
|
174
|
+
apply_option!(set_if_some, builder, request.local_address, local_address);
|
|
175
|
+
apply_option!(set_if_some, builder, request.interface, interface);
|
|
176
|
+
|
|
177
|
+
// Headers options.
|
|
178
|
+
apply_option!(set_if_some, builder, request.headers, headers);
|
|
179
|
+
apply_option!(set_if_some, builder, request.orig_headers, orig_headers);
|
|
180
|
+
apply_option!(
|
|
181
|
+
set_if_some,
|
|
182
|
+
builder,
|
|
183
|
+
request.default_headers,
|
|
184
|
+
default_headers
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
// Authentication options.
|
|
188
|
+
apply_option!(
|
|
189
|
+
set_if_some_map_ref,
|
|
190
|
+
builder,
|
|
191
|
+
request.auth,
|
|
192
|
+
auth,
|
|
193
|
+
AsRef::<str>::as_ref
|
|
194
|
+
);
|
|
195
|
+
apply_option!(set_if_some, builder, request.bearer_auth, bearer_auth);
|
|
196
|
+
if let Some(basic_auth) = request.basic_auth.take() {
|
|
197
|
+
builder = builder.basic_auth(basic_auth.0, basic_auth.1);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Cookies options.
|
|
201
|
+
if let Some(cookies) = request.cookies.take() {
|
|
202
|
+
for cookie in cookies {
|
|
203
|
+
builder = builder.header(header::COOKIE, cookie);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Allow redirects options.
|
|
208
|
+
match request.allow_redirects {
|
|
209
|
+
Some(false) => {
|
|
210
|
+
builder = builder.redirect(wreq::redirect::Policy::none());
|
|
211
|
+
}
|
|
212
|
+
Some(true) => {
|
|
213
|
+
builder = builder.redirect(
|
|
214
|
+
request
|
|
215
|
+
.max_redirects
|
|
216
|
+
.take()
|
|
217
|
+
.map(wreq::redirect::Policy::limited)
|
|
218
|
+
.unwrap_or_default(),
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
None => {}
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
// Compression options.
|
|
225
|
+
apply_option!(set_if_some, builder, request.gzip, gzip);
|
|
226
|
+
apply_option!(set_if_some, builder, request.brotli, brotli);
|
|
227
|
+
apply_option!(set_if_some, builder, request.deflate, deflate);
|
|
228
|
+
apply_option!(set_if_some, builder, request.zstd, zstd);
|
|
229
|
+
|
|
230
|
+
// Query options.
|
|
231
|
+
apply_option!(set_if_some_ref, builder, request.query, query);
|
|
232
|
+
|
|
233
|
+
// Form options.
|
|
234
|
+
apply_option!(set_if_some_ref, builder, request.form, form);
|
|
235
|
+
|
|
236
|
+
// JSON options.
|
|
237
|
+
apply_option!(set_if_some_ref, builder, request.json, json);
|
|
238
|
+
|
|
239
|
+
// Body options.
|
|
240
|
+
if let Some(body) = request.body.take() {
|
|
241
|
+
builder = builder.body(wreq::Body::from(body));
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Send request.
|
|
245
|
+
builder
|
|
246
|
+
.send()
|
|
247
|
+
.await
|
|
248
|
+
.map(Response::new)
|
|
249
|
+
.map_err(wreq_error_to_magnus)
|
|
250
|
+
})
|
|
251
|
+
}
|
data/src/client/resp.rs
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
use std::{net::SocketAddr, sync::Arc};
|
|
2
|
+
|
|
3
|
+
use arc_swap::ArcSwapOption;
|
|
4
|
+
use bytes::Bytes;
|
|
5
|
+
use futures_util::TryFutureExt;
|
|
6
|
+
use http::{Extensions, HeaderMap, response::Response as HttpResponse};
|
|
7
|
+
use http_body_util::BodyExt;
|
|
8
|
+
use magnus::{Error, Module, RArray, RModule, Ruby, Value, block::Yield};
|
|
9
|
+
use wreq::Uri;
|
|
10
|
+
|
|
11
|
+
use crate::{
|
|
12
|
+
client::body::{BodyReceiver, Json},
|
|
13
|
+
cookie::Cookie,
|
|
14
|
+
error::{memory_error, wreq_error_to_magnus},
|
|
15
|
+
gvl,
|
|
16
|
+
header::Headers,
|
|
17
|
+
http::{StatusCode, Version},
|
|
18
|
+
rt,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/// A response from a request.
|
|
22
|
+
#[magnus::wrap(class = "Wreq::Response", free_immediately, size)]
|
|
23
|
+
pub struct Response {
|
|
24
|
+
uri: Uri,
|
|
25
|
+
version: Version,
|
|
26
|
+
status: StatusCode,
|
|
27
|
+
content_length: Option<u64>,
|
|
28
|
+
headers: HeaderMap,
|
|
29
|
+
local_addr: Option<SocketAddr>,
|
|
30
|
+
remote_addr: Option<SocketAddr>,
|
|
31
|
+
body: ArcSwapOption<Body>,
|
|
32
|
+
extensions: Extensions,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/// Represents the state of the HTTP response body.
|
|
36
|
+
enum Body {
|
|
37
|
+
/// The body can be streamed once (not yet buffered).
|
|
38
|
+
Streamable(wreq::Body),
|
|
39
|
+
/// The body has been fully read into memory and can be reused.
|
|
40
|
+
Reusable(Bytes),
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
impl Response {
|
|
44
|
+
/// Create a new [`Response`] instance.
|
|
45
|
+
pub fn new(response: wreq::Response) -> Self {
|
|
46
|
+
let uri = response.uri().clone();
|
|
47
|
+
let content_length = response.content_length();
|
|
48
|
+
let local_addr = response.local_addr();
|
|
49
|
+
let remote_addr = response.remote_addr();
|
|
50
|
+
let response = HttpResponse::from(response);
|
|
51
|
+
let (parts, body) = response.into_parts();
|
|
52
|
+
|
|
53
|
+
Response {
|
|
54
|
+
uri,
|
|
55
|
+
local_addr,
|
|
56
|
+
remote_addr,
|
|
57
|
+
content_length,
|
|
58
|
+
extensions: parts.extensions,
|
|
59
|
+
version: Version::from_ffi(parts.version),
|
|
60
|
+
status: StatusCode::from(parts.status),
|
|
61
|
+
headers: parts.headers,
|
|
62
|
+
body: ArcSwapOption::from_pointee(Body::Streamable(body)),
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/// Internal method to get the wreq::Response, optionally streaming the body.
|
|
67
|
+
fn response(&self, stream: bool) -> Result<wreq::Response, Error> {
|
|
68
|
+
let build_response = |body: wreq::Body| -> wreq::Response {
|
|
69
|
+
let mut response = HttpResponse::new(body);
|
|
70
|
+
*response.version_mut() = self.version.into_ffi();
|
|
71
|
+
*response.status_mut() = self.status.0;
|
|
72
|
+
*response.headers_mut() = self.headers.clone();
|
|
73
|
+
*response.extensions_mut() = self.extensions.clone();
|
|
74
|
+
wreq::Response::from(response)
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
if let Some(arc) = self.body.swap(None) {
|
|
78
|
+
match Arc::try_unwrap(arc) {
|
|
79
|
+
Ok(Body::Streamable(body)) => {
|
|
80
|
+
return if stream {
|
|
81
|
+
Ok(build_response(body))
|
|
82
|
+
} else {
|
|
83
|
+
let bytes = rt::try_block_on(
|
|
84
|
+
BodyExt::collect(body)
|
|
85
|
+
.map_ok(|buf| buf.to_bytes())
|
|
86
|
+
.map_err(wreq_error_to_magnus),
|
|
87
|
+
)?;
|
|
88
|
+
|
|
89
|
+
self.body
|
|
90
|
+
.store(Some(Arc::new(Body::Reusable(bytes.clone()))));
|
|
91
|
+
|
|
92
|
+
Ok(build_response(wreq::Body::from(bytes)))
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
Ok(Body::Reusable(bytes)) => {
|
|
96
|
+
self.body
|
|
97
|
+
.store(Some(Arc::new(Body::Reusable(bytes.clone()))));
|
|
98
|
+
|
|
99
|
+
if !stream {
|
|
100
|
+
return Ok(build_response(wreq::Body::from(bytes)));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
_ => {}
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
Err(memory_error())
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
impl Response {
|
|
112
|
+
/// Get the response status code as a u16.
|
|
113
|
+
#[inline]
|
|
114
|
+
pub fn code(&self) -> u16 {
|
|
115
|
+
self.status.0.as_u16()
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/// Get the response status code.
|
|
119
|
+
#[inline]
|
|
120
|
+
pub fn status(&self) -> StatusCode {
|
|
121
|
+
self.status
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/// Get the response HTTP version.
|
|
125
|
+
#[inline]
|
|
126
|
+
pub fn version(&self) -> Version {
|
|
127
|
+
self.version
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/// Get the response URL.
|
|
131
|
+
#[inline]
|
|
132
|
+
pub fn url(&self) -> String {
|
|
133
|
+
self.uri.to_string()
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/// Get the content length of the response, if known.
|
|
137
|
+
#[inline]
|
|
138
|
+
pub fn content_length(&self) -> Option<u64> {
|
|
139
|
+
self.content_length
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/// Get the response cookies.
|
|
143
|
+
pub fn cookies(ruby: &Ruby, rb_self: &Self) -> Result<RArray, Error> {
|
|
144
|
+
let cookies = Cookie::extract_headers_cookies(&rb_self.headers);
|
|
145
|
+
let ary = ruby.ary_new_capa(cookies.len());
|
|
146
|
+
for cookie in cookies {
|
|
147
|
+
ary.push(cookie)?;
|
|
148
|
+
}
|
|
149
|
+
Ok(ary)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/// Get the response headers.
|
|
153
|
+
#[inline]
|
|
154
|
+
pub fn headers(&self) -> Headers {
|
|
155
|
+
Headers::from(self.headers.clone())
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/// Get the local socket address, if available.
|
|
159
|
+
#[inline]
|
|
160
|
+
pub fn local_addr(&self) -> Option<String> {
|
|
161
|
+
self.local_addr.map(|addr| addr.to_string())
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/// Get the remote socket address, if available.
|
|
165
|
+
#[inline]
|
|
166
|
+
pub fn remote_addr(&self) -> Option<String> {
|
|
167
|
+
self.remote_addr.map(|addr| addr.to_string())
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/// Get the response body as bytes.
|
|
171
|
+
pub fn bytes(&self) -> Result<Bytes, Error> {
|
|
172
|
+
let response = self.response(false)?;
|
|
173
|
+
rt::try_block_on(response.bytes().map_err(wreq_error_to_magnus))
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/// Get the response body as a UTF-8 string.
|
|
177
|
+
pub fn text(&self) -> Result<String, Error> {
|
|
178
|
+
let response = self.response(false)?;
|
|
179
|
+
rt::try_block_on(response.text().map_err(wreq_error_to_magnus))
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/// Get the full response text given a specific encoding.
|
|
183
|
+
pub fn text_with_charset(&self, default_encoding: String) -> Result<String, Error> {
|
|
184
|
+
let response = self.response(false)?;
|
|
185
|
+
rt::try_block_on(
|
|
186
|
+
response
|
|
187
|
+
.text_with_charset(default_encoding)
|
|
188
|
+
.map_err(wreq_error_to_magnus),
|
|
189
|
+
)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/// Get the response body as JSON.
|
|
193
|
+
pub fn json(ruby: &Ruby, rb_self: &Self) -> Result<Value, Error> {
|
|
194
|
+
let response = rb_self.response(false)?;
|
|
195
|
+
rt::try_block_on(async move {
|
|
196
|
+
let json = response
|
|
197
|
+
.json::<Json>()
|
|
198
|
+
.await
|
|
199
|
+
.map_err(wreq_error_to_magnus)?;
|
|
200
|
+
serde_magnus::serialize(ruby, &json)
|
|
201
|
+
})
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/// Get a chunk iterator for the response body.
|
|
205
|
+
pub fn chunks(&self) -> Result<Yield<BodyReceiver>, Error> {
|
|
206
|
+
self.response(true)
|
|
207
|
+
.map(wreq::Response::bytes_stream)
|
|
208
|
+
.map(BodyReceiver::new)
|
|
209
|
+
.map(Yield::Iter)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/// Close the response body, dropping any resources.
|
|
213
|
+
#[inline]
|
|
214
|
+
pub fn close(&self) {
|
|
215
|
+
gvl::nogvl(|| self.body.swap(None));
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
impl Drop for Response {
|
|
220
|
+
fn drop(&mut self) {
|
|
221
|
+
// Ensure body is dropped in GVL
|
|
222
|
+
self.body.swap(None);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
pub fn include(ruby: &Ruby, gem_module: &RModule) -> Result<(), Error> {
|
|
227
|
+
let response_class = gem_module.define_class("Response", ruby.class_object())?;
|
|
228
|
+
response_class.define_method("code", magnus::method!(Response::code, 0))?;
|
|
229
|
+
response_class.define_method("status", magnus::method!(Response::status, 0))?;
|
|
230
|
+
response_class.define_method("version", magnus::method!(Response::version, 0))?;
|
|
231
|
+
response_class.define_method("url", magnus::method!(Response::url, 0))?;
|
|
232
|
+
response_class.define_method(
|
|
233
|
+
"content_length",
|
|
234
|
+
magnus::method!(Response::content_length, 0),
|
|
235
|
+
)?;
|
|
236
|
+
response_class.define_method("cookies", magnus::method!(Response::cookies, 0))?;
|
|
237
|
+
response_class.define_method("headers", magnus::method!(Response::headers, 0))?;
|
|
238
|
+
response_class.define_method("local_addr", magnus::method!(Response::local_addr, 0))?;
|
|
239
|
+
response_class.define_method("remote_addr", magnus::method!(Response::remote_addr, 0))?;
|
|
240
|
+
response_class.define_method("bytes", magnus::method!(Response::bytes, 0))?;
|
|
241
|
+
response_class.define_method("text", magnus::method!(Response::text, 0))?;
|
|
242
|
+
response_class.define_method(
|
|
243
|
+
"text_with_charset",
|
|
244
|
+
magnus::method!(Response::text_with_charset, 1),
|
|
245
|
+
)?;
|
|
246
|
+
response_class.define_method("json", magnus::method!(Response::json, 0))?;
|
|
247
|
+
response_class.define_method("chunks", magnus::method!(Response::chunks, 0))?;
|
|
248
|
+
response_class.define_method("close", magnus::method!(Response::close, 0))?;
|
|
249
|
+
Ok(())
|
|
250
|
+
}
|