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/lib/wreq_ruby/response.rb
CHANGED
|
@@ -88,26 +88,16 @@ unless defined?(Wreq)
|
|
|
88
88
|
def bytes
|
|
89
89
|
end
|
|
90
90
|
|
|
91
|
-
# Get the response body as text.
|
|
92
|
-
#
|
|
93
|
-
# @return [String] Response body decoded as UTF-8 text
|
|
94
|
-
# @example
|
|
95
|
-
# html = response.text
|
|
96
|
-
# puts html
|
|
97
|
-
# @raise [Wreq::DecodingError] if body cannot be decoded as binary
|
|
98
|
-
def text
|
|
99
|
-
end
|
|
100
|
-
|
|
101
91
|
# Get the response body as text with a specific charset.
|
|
102
92
|
# This method allows you to specify a default encoding
|
|
103
93
|
# to use when decoding the response body.
|
|
104
94
|
# # @param default_encoding [String] Default encoding to use (e.g., "UTF-8")
|
|
105
95
|
# # @return [String] Response body decoded as text using the specified encoding
|
|
106
96
|
# @example
|
|
107
|
-
# html = response.
|
|
97
|
+
# html = response.text("ISO-8859-1")
|
|
108
98
|
# puts html
|
|
109
99
|
# @raise [Wreq::DecodingError] if body cannot be decoded with the specified encoding
|
|
110
|
-
def
|
|
100
|
+
def text(default_encoding: "UTF-8")
|
|
111
101
|
end
|
|
112
102
|
|
|
113
103
|
# Parse the response body as JSON.
|
|
@@ -120,14 +110,17 @@ unless defined?(Wreq)
|
|
|
120
110
|
def json
|
|
121
111
|
end
|
|
122
112
|
|
|
123
|
-
#
|
|
113
|
+
# Stream the response body, yielding each chunk to the given block.
|
|
124
114
|
#
|
|
125
115
|
# This method allows you to process large HTTP responses efficiently,
|
|
126
116
|
# by yielding each chunk of the body as it arrives, without loading
|
|
127
117
|
# the entire response into memory.
|
|
128
118
|
#
|
|
129
|
-
# @return
|
|
119
|
+
# @return [nil]
|
|
130
120
|
# @yield [chunk] Each chunk of the response body as a binary String
|
|
121
|
+
# @raise [LocalJumpError] if called without a block
|
|
122
|
+
# @raise [Wreq::TimeoutError, Wreq::BodyError, Wreq::ConnectionResetError, Wreq::RequestError]
|
|
123
|
+
# if streaming fails while reading the response body
|
|
131
124
|
# @example Save response to file
|
|
132
125
|
# File.open("output.bin", "wb") do |f|
|
|
133
126
|
# response.chunks { |chunk| f.write(chunk) }
|
|
@@ -137,7 +130,7 @@ unless defined?(Wreq)
|
|
|
137
130
|
# response.chunks { |chunk| total += chunk.bytesize }
|
|
138
131
|
# puts "Downloaded #{total} bytes"
|
|
139
132
|
#
|
|
140
|
-
#
|
|
133
|
+
# Exceptions raised inside the block are propagated to the caller.
|
|
141
134
|
def chunks
|
|
142
135
|
end
|
|
143
136
|
|
|
@@ -156,27 +149,35 @@ end
|
|
|
156
149
|
|
|
157
150
|
module Wreq
|
|
158
151
|
class Response
|
|
159
|
-
# Returns
|
|
152
|
+
# Returns the response body as a string.
|
|
153
|
+
#
|
|
154
|
+
# @return [String] Response body text
|
|
155
|
+
# @example
|
|
156
|
+
# puts response.to_s
|
|
157
|
+
# puts response
|
|
158
|
+
# File.write("page.html", response)
|
|
159
|
+
def to_s
|
|
160
|
+
text
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# Returns a compact string representation for debugging.
|
|
160
164
|
#
|
|
161
165
|
# Format: #<Wreq::Response STATUS content-type="..." body=SIZE>
|
|
162
166
|
#
|
|
163
167
|
# @return [String] Compact formatted response information
|
|
164
168
|
# @example
|
|
165
|
-
#
|
|
169
|
+
# p response
|
|
166
170
|
# # => #<Wreq::Response 200 content-type="application/json" body=456B>
|
|
167
|
-
def
|
|
171
|
+
def inspect
|
|
168
172
|
parts = ["#<Wreq::Response"]
|
|
169
173
|
|
|
170
|
-
# Status code
|
|
171
174
|
parts << code.to_s
|
|
172
175
|
|
|
173
|
-
# Content-Type header if present
|
|
174
176
|
if headers.respond_to?(:get)
|
|
175
177
|
content_type = headers.get("content-type")
|
|
176
178
|
parts << "content-type=#{content_type.inspect}" if content_type
|
|
177
179
|
end
|
|
178
180
|
|
|
179
|
-
# Body size
|
|
180
181
|
if content_length
|
|
181
182
|
parts << "body=#{format_bytes(content_length)}"
|
|
182
183
|
end
|
data/src/client/body/stream.rs
CHANGED
|
@@ -13,7 +13,7 @@ use tokio::sync::{
|
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
use crate::{
|
|
16
|
-
error::{memory_error, mpsc_send_error_to_magnus},
|
|
16
|
+
error::{memory_error, mpsc_send_error_to_magnus, wreq_error_to_magnus},
|
|
17
17
|
rt,
|
|
18
18
|
};
|
|
19
19
|
|
|
@@ -37,20 +37,15 @@ impl BodyReceiver {
|
|
|
37
37
|
pub fn new(stream: impl Stream<Item = wreq::Result<Bytes>> + Send + 'static) -> BodyReceiver {
|
|
38
38
|
BodyReceiver(Mutex::new(Box::pin(stream)))
|
|
39
39
|
}
|
|
40
|
-
}
|
|
41
40
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
.as_mut()
|
|
51
|
-
.next()
|
|
52
|
-
.await
|
|
53
|
-
.and_then(|r| r.ok())
|
|
41
|
+
/// Read the next body chunk, converting stream errors into Ruby errors.
|
|
42
|
+
pub fn next(&self) -> Result<Option<Bytes>, Error> {
|
|
43
|
+
rt::try_block_on(async {
|
|
44
|
+
match self.0.lock().await.as_mut().next().await {
|
|
45
|
+
Some(Ok(data)) => Ok(Some(data)),
|
|
46
|
+
Some(Err(err)) => Err(wreq_error_to_magnus(err)),
|
|
47
|
+
None => Ok(None),
|
|
48
|
+
}
|
|
54
49
|
})
|
|
55
50
|
}
|
|
56
51
|
}
|
data/src/client/req.rs
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
use std::{net::IpAddr, time::Duration};
|
|
2
2
|
|
|
3
|
-
use http::
|
|
3
|
+
use http::header;
|
|
4
4
|
use magnus::{RHash, TryConvert, typed_data::Obj, value::ReprValue};
|
|
5
5
|
use serde::Deserialize;
|
|
6
|
-
use wreq::{
|
|
7
|
-
Client, Proxy, Version,
|
|
8
|
-
header::{HeaderMap, OrigHeaderMap},
|
|
9
|
-
};
|
|
6
|
+
use wreq::{Client, Proxy, Version, header::OrigHeaderMap};
|
|
10
7
|
|
|
11
8
|
use super::body::{Body, Form, Json};
|
|
12
9
|
use crate::{
|
|
13
10
|
client::{query::Query, resp::Response},
|
|
14
|
-
|
|
11
|
+
cookie::Cookies,
|
|
12
|
+
emulate::Emulation,
|
|
15
13
|
error::wreq_error_to_magnus,
|
|
16
14
|
extractor::Extractor,
|
|
15
|
+
header::Headers,
|
|
17
16
|
http::Method,
|
|
18
17
|
rt,
|
|
19
18
|
};
|
|
@@ -46,20 +45,20 @@ pub struct Request {
|
|
|
46
45
|
#[serde(skip)]
|
|
47
46
|
version: Option<Version>,
|
|
48
47
|
|
|
48
|
+
/// The option enables default headers.
|
|
49
|
+
default_headers: Option<bool>,
|
|
50
|
+
|
|
49
51
|
/// The headers to use for the request.
|
|
50
52
|
#[serde(skip)]
|
|
51
|
-
headers: Option<
|
|
53
|
+
headers: Option<Headers>,
|
|
52
54
|
|
|
53
55
|
/// The original headers to use for the request.
|
|
54
56
|
#[serde(skip)]
|
|
55
57
|
orig_headers: Option<OrigHeaderMap>,
|
|
56
58
|
|
|
57
|
-
/// The option enables default headers.
|
|
58
|
-
default_headers: Option<bool>,
|
|
59
|
-
|
|
60
59
|
/// The cookies to use for the request.
|
|
61
60
|
#[serde(skip)]
|
|
62
|
-
cookies: Option<
|
|
61
|
+
cookies: Option<Cookies>,
|
|
63
62
|
|
|
64
63
|
/// Whether to allow redirects.
|
|
65
64
|
allow_redirects: Option<bool>,
|
|
@@ -105,35 +104,30 @@ pub struct Request {
|
|
|
105
104
|
impl Request {
|
|
106
105
|
/// Create a new [`Request`] from Ruby keyword arguments.
|
|
107
106
|
pub fn new(ruby: &magnus::Ruby, hash: RHash) -> Result<Self, magnus::Error> {
|
|
108
|
-
let
|
|
109
|
-
let mut builder: Self = serde_magnus::deserialize(ruby,
|
|
107
|
+
let keyword = hash.as_value();
|
|
108
|
+
let mut builder: Self = serde_magnus::deserialize(ruby, keyword)?;
|
|
110
109
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
builder.emulation = Some((*emulation_obj).clone());
|
|
110
|
+
if let Some(v) = hash.get(ruby.to_symbol(stringify!(emulation))) {
|
|
111
|
+
let obj = Obj::<Emulation>::try_convert(v)?;
|
|
112
|
+
builder.emulation = Some((*obj).clone());
|
|
115
113
|
}
|
|
116
114
|
|
|
117
|
-
|
|
118
|
-
|
|
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();
|
|
115
|
+
if let Some(v) = hash.get(ruby.to_symbol(stringify!(headers))) {
|
|
116
|
+
builder.headers = Some(Headers::try_convert(v)?);
|
|
117
|
+
}
|
|
128
118
|
|
|
129
|
-
|
|
130
|
-
|
|
119
|
+
if let Some(v) = hash.get(ruby.to_symbol(stringify!(cookies))) {
|
|
120
|
+
builder.cookies = Some(Cookies::try_convert(v)?);
|
|
121
|
+
}
|
|
131
122
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
builder.body = Some(Body::try_convert(body)?);
|
|
123
|
+
if let Some(v) = hash.get(ruby.to_symbol(stringify!(body))) {
|
|
124
|
+
builder.body = Some(Body::try_convert(v)?);
|
|
135
125
|
}
|
|
136
126
|
|
|
127
|
+
builder.proxy = Extractor::<Proxy>::try_convert(keyword)?.into_inner();
|
|
128
|
+
builder.version = Extractor::<Version>::try_convert(keyword)?.into_inner();
|
|
129
|
+
builder.orig_headers = Extractor::<OrigHeaderMap>::try_convert(keyword)?.into_inner();
|
|
130
|
+
|
|
137
131
|
Ok(builder)
|
|
138
132
|
}
|
|
139
133
|
}
|
|
@@ -175,7 +169,7 @@ pub fn execute_request<U: AsRef<str>>(
|
|
|
175
169
|
apply_option!(set_if_some, builder, request.interface, interface);
|
|
176
170
|
|
|
177
171
|
// Headers options.
|
|
178
|
-
apply_option!(
|
|
172
|
+
apply_option!(set_if_some_into_inner, builder, request.headers, headers);
|
|
179
173
|
apply_option!(set_if_some, builder, request.orig_headers, orig_headers);
|
|
180
174
|
apply_option!(
|
|
181
175
|
set_if_some,
|
|
@@ -184,6 +178,13 @@ pub fn execute_request<U: AsRef<str>>(
|
|
|
184
178
|
default_headers
|
|
185
179
|
);
|
|
186
180
|
|
|
181
|
+
// Cookies options.
|
|
182
|
+
if let Some(cookies) = request.cookies.take() {
|
|
183
|
+
for cookie in cookies.0 {
|
|
184
|
+
builder = builder.header(header::COOKIE, cookie);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
187
188
|
// Authentication options.
|
|
188
189
|
apply_option!(
|
|
189
190
|
set_if_some_map_ref,
|
|
@@ -197,13 +198,6 @@ pub fn execute_request<U: AsRef<str>>(
|
|
|
197
198
|
builder = builder.basic_auth(basic_auth.0, basic_auth.1);
|
|
198
199
|
}
|
|
199
200
|
|
|
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
201
|
// Allow redirects options.
|
|
208
202
|
match request.allow_redirects {
|
|
209
203
|
Some(false) => {
|
data/src/client/resp.rs
CHANGED
|
@@ -5,14 +5,14 @@ use bytes::Bytes;
|
|
|
5
5
|
use futures_util::TryFutureExt;
|
|
6
6
|
use http::{Extensions, HeaderMap, response::Response as HttpResponse};
|
|
7
7
|
use http_body_util::BodyExt;
|
|
8
|
-
use magnus::{Error, Module, RArray, RModule, Ruby, Value,
|
|
8
|
+
use magnus::{Error, Module, RArray, RModule, Ruby, Value, scan_args::scan_args};
|
|
9
9
|
use wreq::Uri;
|
|
10
10
|
|
|
11
11
|
use crate::{
|
|
12
12
|
client::body::{BodyReceiver, Json},
|
|
13
13
|
cookie::Cookie,
|
|
14
|
-
error::{memory_error, wreq_error_to_magnus},
|
|
15
|
-
gvl,
|
|
14
|
+
error::{memory_error, no_block_given_error, wreq_error_to_magnus},
|
|
15
|
+
gvl::{self, nogvl},
|
|
16
16
|
header::Headers,
|
|
17
17
|
http::{StatusCode, Version},
|
|
18
18
|
rt,
|
|
@@ -173,20 +173,18 @@ impl Response {
|
|
|
173
173
|
rt::try_block_on(response.bytes().map_err(wreq_error_to_magnus))
|
|
174
174
|
}
|
|
175
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
176
|
/// Get the full response text given a specific encoding.
|
|
183
|
-
pub fn
|
|
177
|
+
pub fn text(&self, args: &[Value]) -> Result<String, Error> {
|
|
178
|
+
let args = scan_args::<(), (Option<String>,), (), (), (), ()>(args)?;
|
|
184
179
|
let response = self.response(false)?;
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
180
|
+
match args.optional.0 {
|
|
181
|
+
Some(encoding) => rt::try_block_on(
|
|
182
|
+
response
|
|
183
|
+
.text_with_charset(encoding)
|
|
184
|
+
.map_err(wreq_error_to_magnus),
|
|
185
|
+
),
|
|
186
|
+
None => rt::try_block_on(response.text().map_err(wreq_error_to_magnus)),
|
|
187
|
+
}
|
|
190
188
|
}
|
|
191
189
|
|
|
192
190
|
/// Get the response body as JSON.
|
|
@@ -201,12 +199,24 @@ impl Response {
|
|
|
201
199
|
})
|
|
202
200
|
}
|
|
203
201
|
|
|
204
|
-
///
|
|
205
|
-
pub fn chunks(&
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
202
|
+
/// Yield response body chunks to the given Ruby block.
|
|
203
|
+
pub fn chunks(ruby: &Ruby, rb_self: &Self) -> Result<(), Error> {
|
|
204
|
+
if !ruby.block_given() {
|
|
205
|
+
return Err(no_block_given_error());
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
let receiver = nogvl(|| {
|
|
209
|
+
rb_self
|
|
210
|
+
.response(true)
|
|
211
|
+
.map(wreq::Response::bytes_stream)
|
|
212
|
+
.map(BodyReceiver::new)
|
|
213
|
+
})?;
|
|
214
|
+
|
|
215
|
+
while let Some(chunk) = receiver.next()? {
|
|
216
|
+
let _: Value = ruby.yield_value(chunk)?;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
Ok(())
|
|
210
220
|
}
|
|
211
221
|
|
|
212
222
|
/// Close the response body, dropping any resources.
|
|
@@ -224,27 +234,23 @@ impl Drop for Response {
|
|
|
224
234
|
}
|
|
225
235
|
|
|
226
236
|
pub fn include(ruby: &Ruby, gem_module: &RModule) -> Result<(), Error> {
|
|
227
|
-
let
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
237
|
+
let response = gem_module.define_class("Response", ruby.class_object())?;
|
|
238
|
+
response.define_method("code", magnus::method!(Response::code, 0))?;
|
|
239
|
+
response.define_method("status", magnus::method!(Response::status, 0))?;
|
|
240
|
+
response.define_method("version", magnus::method!(Response::version, 0))?;
|
|
241
|
+
response.define_method("url", magnus::method!(Response::url, 0))?;
|
|
242
|
+
response.define_method(
|
|
233
243
|
"content_length",
|
|
234
244
|
magnus::method!(Response::content_length, 0),
|
|
235
245
|
)?;
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
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))?;
|
|
246
|
+
response.define_method("cookies", magnus::method!(Response::cookies, 0))?;
|
|
247
|
+
response.define_method("headers", magnus::method!(Response::headers, 0))?;
|
|
248
|
+
response.define_method("local_addr", magnus::method!(Response::local_addr, 0))?;
|
|
249
|
+
response.define_method("remote_addr", magnus::method!(Response::remote_addr, 0))?;
|
|
250
|
+
response.define_method("bytes", magnus::method!(Response::bytes, 0))?;
|
|
251
|
+
response.define_method("text", magnus::method!(Response::text, -1))?;
|
|
252
|
+
response.define_method("json", magnus::method!(Response::json, 0))?;
|
|
253
|
+
response.define_method("chunks", magnus::method!(Response::chunks, 0))?;
|
|
254
|
+
response.define_method("close", magnus::method!(Response::close, 0))?;
|
|
249
255
|
Ok(())
|
|
250
256
|
}
|
data/src/client.rs
CHANGED
|
@@ -12,16 +12,17 @@ use magnus::{
|
|
|
12
12
|
use serde::Deserialize;
|
|
13
13
|
use wreq::{
|
|
14
14
|
Proxy,
|
|
15
|
-
header::{
|
|
15
|
+
header::{HeaderValue, OrigHeaderMap},
|
|
16
16
|
};
|
|
17
17
|
|
|
18
18
|
use crate::{
|
|
19
19
|
client::{req::execute_request, resp::Response},
|
|
20
20
|
cookie::Jar,
|
|
21
|
-
|
|
21
|
+
emulate::Emulation,
|
|
22
22
|
error::wreq_error_to_magnus,
|
|
23
23
|
extractor::Extractor,
|
|
24
24
|
gvl,
|
|
25
|
+
header::Headers,
|
|
25
26
|
http::Method,
|
|
26
27
|
};
|
|
27
28
|
|
|
@@ -36,7 +37,7 @@ struct Builder {
|
|
|
36
37
|
user_agent: Option<HeaderValue>,
|
|
37
38
|
/// The headers to use for the client.
|
|
38
39
|
#[serde(skip)]
|
|
39
|
-
headers: Option<
|
|
40
|
+
headers: Option<Headers>,
|
|
40
41
|
/// The original headers to use for the client.
|
|
41
42
|
#[serde(skip)]
|
|
42
43
|
orig_headers: Option<OrigHeaderMap>,
|
|
@@ -83,7 +84,7 @@ struct Builder {
|
|
|
83
84
|
/// Sets the maximum idle connection per host allowed in the pool.
|
|
84
85
|
pool_max_idle_per_host: Option<usize>,
|
|
85
86
|
/// Sets the maximum number of connections in the pool.
|
|
86
|
-
pool_max_size: Option<
|
|
87
|
+
pool_max_size: Option<usize>,
|
|
87
88
|
|
|
88
89
|
// ========= Protocol options =========
|
|
89
90
|
/// Whether to use the HTTP/1 protocol only.
|
|
@@ -128,35 +129,33 @@ pub struct Client(wreq::Client);
|
|
|
128
129
|
impl Builder {
|
|
129
130
|
/// Create a new [`Builder`] from Ruby keyword arguments.
|
|
130
131
|
fn new(ruby: &magnus::Ruby, keyword: &Value) -> Result<Self, magnus::Error> {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
if let Some(v) = hash.get(ruby.to_symbol("emulation")) {
|
|
135
|
-
let emulation_obj = Obj::<Emulation>::try_convert(v)?;
|
|
136
|
-
builder.emulation = Some((*emulation_obj).clone());
|
|
137
|
-
}
|
|
132
|
+
let Ok(hash) = RHash::try_convert(*keyword) else {
|
|
133
|
+
return Ok(Default::default());
|
|
134
|
+
};
|
|
138
135
|
|
|
139
|
-
|
|
140
|
-
builder.user_agent = Extractor::<HeaderValue>::try_convert(*keyword)?.into_inner();
|
|
136
|
+
let mut builder: Self = serde_magnus::deserialize(ruby, hash)?;
|
|
141
137
|
|
|
142
|
-
|
|
143
|
-
builder.
|
|
144
|
-
|
|
145
|
-
// extra original headers handling
|
|
146
|
-
builder.orig_headers = Extractor::<OrigHeaderMap>::try_convert(*keyword)?.into_inner();
|
|
138
|
+
if let Some(v) = hash.get(ruby.to_symbol(stringify!(emulation))) {
|
|
139
|
+
builder.emulation = Some((*Obj::<Emulation>::try_convert(v)?).clone());
|
|
140
|
+
}
|
|
147
141
|
|
|
148
|
-
|
|
149
|
-
builder.
|
|
142
|
+
if let Some(v) = hash.get(ruby.to_symbol(stringify!(headers))) {
|
|
143
|
+
builder.headers = Some(Headers::try_convert(v)?);
|
|
144
|
+
}
|
|
150
145
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
146
|
+
if let Some(v) = hash.get(ruby.to_symbol(stringify!(cookie_provider))) {
|
|
147
|
+
builder.cookie_provider = Some((*Obj::<Jar>::try_convert(v)?).clone());
|
|
148
|
+
}
|
|
155
149
|
|
|
156
|
-
|
|
150
|
+
if let Some(jar) = hash.get(ruby.to_symbol(stringify!(cookie_provider))) {
|
|
151
|
+
builder.cookie_provider = Some((*Obj::<Jar>::try_convert(jar)?).clone());
|
|
157
152
|
}
|
|
158
153
|
|
|
159
|
-
|
|
154
|
+
builder.user_agent = Extractor::<HeaderValue>::try_convert(*keyword)?.into_inner();
|
|
155
|
+
builder.orig_headers = Extractor::<OrigHeaderMap>::try_convert(*keyword)?.into_inner();
|
|
156
|
+
builder.proxy = Extractor::<Proxy>::try_convert(*keyword)?.into_inner();
|
|
157
|
+
|
|
158
|
+
Ok(builder)
|
|
160
159
|
}
|
|
161
160
|
}
|
|
162
161
|
|
|
@@ -164,9 +163,9 @@ impl Builder {
|
|
|
164
163
|
|
|
165
164
|
impl Client {
|
|
166
165
|
/// Create a new [`Client`] with the given keyword arguments.
|
|
167
|
-
pub fn new(ruby: &Ruby,
|
|
168
|
-
if let Some(
|
|
169
|
-
let mut params = Builder::new(ruby,
|
|
166
|
+
pub fn new(ruby: &Ruby, keyword: &[Value]) -> Result<Self, magnus::Error> {
|
|
167
|
+
if let Some(keyword) = keyword.first() {
|
|
168
|
+
let mut params = Builder::new(ruby, keyword)?;
|
|
170
169
|
gvl::nogvl(|| {
|
|
171
170
|
let mut builder = wreq::Client::builder();
|
|
172
171
|
|
|
@@ -177,7 +176,12 @@ impl Client {
|
|
|
177
176
|
apply_option!(set_if_some, builder, params.user_agent, user_agent);
|
|
178
177
|
|
|
179
178
|
// Default headers options.
|
|
180
|
-
apply_option!(
|
|
179
|
+
apply_option!(
|
|
180
|
+
set_if_some_into_inner,
|
|
181
|
+
builder,
|
|
182
|
+
params.headers,
|
|
183
|
+
default_headers
|
|
184
|
+
);
|
|
181
185
|
apply_option!(set_if_some, builder, params.orig_headers, orig_headers);
|
|
182
186
|
|
|
183
187
|
// Allow redirects options.
|
|
@@ -286,7 +290,7 @@ impl Client {
|
|
|
286
290
|
apply_option!(set_if_some, builder, params.https_only, https_only);
|
|
287
291
|
|
|
288
292
|
// TLS options.
|
|
289
|
-
apply_option!(set_if_some, builder, params.verify,
|
|
293
|
+
apply_option!(set_if_some, builder, params.verify, tls_cert_verification);
|
|
290
294
|
|
|
291
295
|
// Network options.
|
|
292
296
|
apply_option!(set_if_some, builder, params.proxy, proxy);
|
data/src/cookie.rs
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
use std::{fmt, sync::Arc, time::SystemTime};
|
|
2
2
|
|
|
3
|
+
use bytes::Bytes;
|
|
3
4
|
use cookie::{Cookie as RawCookie, Expiration, ParseError, time::Duration};
|
|
4
5
|
use magnus::{
|
|
5
|
-
Error, Module, Object, RModule, Ruby, Value, function, method,
|
|
6
|
-
value::ReprValue,
|
|
6
|
+
Error, Module, Object, RHash, RModule, RString, Ruby, TryConvert, Value, function, method,
|
|
7
|
+
r_hash::ForEach, typed_data::Obj, value::ReprValue,
|
|
7
8
|
};
|
|
8
9
|
use wreq::header::{self, HeaderMap, HeaderValue};
|
|
9
10
|
|
|
10
|
-
use crate::gvl;
|
|
11
|
+
use crate::{error::header_value_error_to_magnus, gvl};
|
|
11
12
|
|
|
12
13
|
define_ruby_enum!(
|
|
13
14
|
/// The Cookie SameSite attribute.
|
|
@@ -25,6 +26,10 @@ define_ruby_enum!(
|
|
|
25
26
|
#[magnus::wrap(class = "Wreq::Cookie", free_immediately, size)]
|
|
26
27
|
pub struct Cookie(RawCookie<'static>);
|
|
27
28
|
|
|
29
|
+
/// A collection of HTTP cookies.
|
|
30
|
+
#[derive(Default)]
|
|
31
|
+
pub struct Cookies(pub Vec<HeaderValue>);
|
|
32
|
+
|
|
28
33
|
/// A good default `CookieStore` implementation.
|
|
29
34
|
///
|
|
30
35
|
/// This is the implementation used when simply calling `cookie_store(true)`.
|
|
@@ -197,6 +202,36 @@ impl fmt::Display for Cookie {
|
|
|
197
202
|
}
|
|
198
203
|
}
|
|
199
204
|
|
|
205
|
+
// ===== impl Cookies =====
|
|
206
|
+
|
|
207
|
+
impl TryConvert for Cookies {
|
|
208
|
+
fn try_convert(value: magnus::Value) -> Result<Self, magnus::Error> {
|
|
209
|
+
// try extract uncompressed cookies
|
|
210
|
+
if let Some(rhash) = RHash::from_value(value) {
|
|
211
|
+
let mut cookies = Vec::new();
|
|
212
|
+
rhash.foreach(|name: RString, value: RString| {
|
|
213
|
+
let cookie = format!("{name}={value}");
|
|
214
|
+
let header_value = HeaderValue::from_maybe_shared(Bytes::from(cookie))
|
|
215
|
+
.map_err(header_value_error_to_magnus)?;
|
|
216
|
+
cookies.push(header_value);
|
|
217
|
+
Ok(ForEach::Continue)
|
|
218
|
+
})?;
|
|
219
|
+
|
|
220
|
+
return Ok(Self(cookies));
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// try extract compressed cookies
|
|
224
|
+
if let Some(cookies) = RString::from_value(value) {
|
|
225
|
+
return Ok(Self(vec![
|
|
226
|
+
HeaderValue::from_maybe_shared(cookies.to_bytes())
|
|
227
|
+
.map_err(header_value_error_to_magnus)?,
|
|
228
|
+
]));
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
Ok(Self::default())
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
200
235
|
// ===== impl Jar =====
|
|
201
236
|
|
|
202
237
|
impl Jar {
|
|
@@ -221,13 +256,14 @@ impl Jar {
|
|
|
221
256
|
}
|
|
222
257
|
|
|
223
258
|
/// Add a cookie to this jar.
|
|
224
|
-
pub fn
|
|
225
|
-
|
|
226
|
-
|
|
259
|
+
pub fn add(&self, cookie: Value, url: String) {
|
|
260
|
+
if let Ok(cookie) = Obj::<Cookie>::try_convert(cookie) {
|
|
261
|
+
gvl::nogvl(|| self.0.add(cookie.0.clone(), &url))
|
|
262
|
+
}
|
|
227
263
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
264
|
+
if let Ok(cookie_str) = String::try_convert(cookie) {
|
|
265
|
+
gvl::nogvl(|| self.0.add(cookie_str.as_ref(), &url))
|
|
266
|
+
}
|
|
231
267
|
}
|
|
232
268
|
|
|
233
269
|
/// Remove a cookie from this jar by name and URL.
|
|
@@ -268,8 +304,7 @@ pub fn include(ruby: &Ruby, gem_module: &RModule) -> Result<(), Error> {
|
|
|
268
304
|
let jar_class = gem_module.define_class("Jar", ruby.class_object())?;
|
|
269
305
|
jar_class.define_singleton_method("new", function!(Jar::new, 0))?;
|
|
270
306
|
jar_class.define_method("get_all", method!(Jar::get_all, 0))?;
|
|
271
|
-
jar_class.define_method("
|
|
272
|
-
jar_class.define_method("add_cookie_str", method!(Jar::add_cookie_str, 2))?;
|
|
307
|
+
jar_class.define_method("add", method!(Jar::add, 2))?;
|
|
273
308
|
jar_class.define_method("remove", method!(Jar::remove, 2))?;
|
|
274
309
|
jar_class.define_method("clear", method!(Jar::clear, 0))?;
|
|
275
310
|
|