wreq 1.2.4 → 1.2.5
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 +3 -7
- data/Cargo.toml +6 -1
- data/crates/wreq-util/.github/FUNDING.yml +15 -0
- data/crates/wreq-util/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
- data/crates/wreq-util/.github/ISSUE_TEMPLATE/custom.md +10 -0
- data/crates/wreq-util/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- data/crates/wreq-util/.github/dependabot.yml +22 -0
- data/crates/wreq-util/.github/workflows/ci.yml +82 -0
- data/crates/wreq-util/.github/workflows/release-plz.yml +52 -0
- data/crates/wreq-util/.gitignore +24 -0
- data/crates/wreq-util/CHANGELOG.md +74 -0
- data/crates/wreq-util/Cargo.toml +94 -0
- data/crates/wreq-util/LICENSE +201 -0
- data/crates/wreq-util/README.md +62 -0
- data/crates/wreq-util/examples/emulate.rs +39 -0
- data/crates/wreq-util/examples/tower_delay.rs +191 -0
- data/crates/wreq-util/release-plz.toml +2 -0
- data/crates/wreq-util/rustfmt.toml +5 -0
- data/crates/wreq-util/src/emulate/compress.rs +85 -0
- data/crates/wreq-util/src/emulate/macros.rs +370 -0
- data/crates/wreq-util/src/emulate/profile/chrome/header.rs +59 -0
- data/crates/wreq-util/src/emulate/profile/chrome/http2.rs +65 -0
- data/crates/wreq-util/src/emulate/profile/chrome/tls.rs +153 -0
- data/crates/wreq-util/src/emulate/profile/chrome.rs +2028 -0
- data/crates/wreq-util/src/emulate/profile/firefox/header.rs +21 -0
- data/crates/wreq-util/src/emulate/profile/firefox/http2.rs +113 -0
- data/crates/wreq-util/src/emulate/profile/firefox/tls.rs +253 -0
- data/crates/wreq-util/src/emulate/profile/firefox.rs +518 -0
- data/crates/wreq-util/src/emulate/profile/okhttp.rs +241 -0
- data/crates/wreq-util/src/emulate/profile/opera/header.rs +30 -0
- data/crates/wreq-util/src/emulate/profile/opera/http2.rs +50 -0
- data/crates/wreq-util/src/emulate/profile/opera/tls.rs +97 -0
- data/crates/wreq-util/src/emulate/profile/opera.rs +299 -0
- data/crates/wreq-util/src/emulate/profile/safari/header.rs +59 -0
- data/crates/wreq-util/src/emulate/profile/safari/http2.rs +116 -0
- data/crates/wreq-util/src/emulate/profile/safari/tls.rs +177 -0
- data/crates/wreq-util/src/emulate/profile/safari.rs +222 -0
- data/crates/wreq-util/src/emulate/profile.rs +47 -0
- data/crates/wreq-util/src/emulate.rs +369 -0
- data/crates/wreq-util/src/lib.rs +14 -0
- data/crates/wreq-util/src/rand.rs +24 -0
- data/crates/wreq-util/src/tower/delay/future.rs +43 -0
- data/crates/wreq-util/src/tower/delay/layer.rs +201 -0
- data/crates/wreq-util/src/tower/delay/service.rs +190 -0
- data/crates/wreq-util/src/tower/delay.rs +98 -0
- data/crates/wreq-util/src/tower.rs +7 -0
- data/crates/wreq-util/tests/client.rs +51 -0
- data/crates/wreq-util/tests/emulate_chrome.rs +270 -0
- data/crates/wreq-util/tests/emulate_firefox.rs +92 -0
- data/crates/wreq-util/tests/emulate_okhttp.rs +46 -0
- data/crates/wreq-util/tests/emulate_opera.rs +113 -0
- data/crates/wreq-util/tests/emulate_safari.rs +151 -0
- data/crates/wreq-util/tests/support/mod.rs +55 -0
- data/crates/wreq-util/tests/support/server.rs +232 -0
- data/examples/emulate_request.rb +1 -1
- data/lib/wreq.rb +20 -18
- data/lib/wreq_ruby/body.rb +3 -0
- data/lib/wreq_ruby/client.rb +18 -18
- data/lib/wreq_ruby/cookie.rb +2 -2
- data/lib/wreq_ruby/emulate.rb +3 -1
- data/lib/wreq_ruby/header.rb +205 -114
- data/lib/wreq_ruby/response.rb +28 -3
- data/src/emulate.rs +1 -0
- data/src/error.rs +2 -6
- data/src/header/helper.rs +112 -0
- data/src/header.rs +198 -133
- data/test/header_test.rb +228 -192
- data/test/stream_test.rb +36 -0
- metadata +54 -1
data/src/header.rs
CHANGED
|
@@ -1,161 +1,257 @@
|
|
|
1
|
+
//! Native support for `Wreq::Headers`.
|
|
2
|
+
//!
|
|
3
|
+
//! Header names are normalized by [`HeaderMap`] and compared without regard to
|
|
4
|
+
//! case, as required by [RFC 9110 section 5.1]. Ruby collection adapters add
|
|
5
|
+
//! construction, indexing, and enumeration without changing the underlying
|
|
6
|
+
//! header representation. Exact wire casing and order remain the responsibility
|
|
7
|
+
//! of the `orig_headers` request option.
|
|
8
|
+
//!
|
|
9
|
+
//! [RFC 9110 section 5.1]: https://www.rfc-editor.org/rfc/rfc9110.html#section-5.1
|
|
10
|
+
|
|
11
|
+
mod helper;
|
|
12
|
+
|
|
1
13
|
use std::cell::RefCell;
|
|
2
14
|
|
|
3
15
|
use bytes::Bytes;
|
|
4
|
-
use http::{HeaderMap,
|
|
16
|
+
use http::{HeaderMap, HeaderValue};
|
|
5
17
|
use magnus::{
|
|
6
|
-
Error,
|
|
7
|
-
|
|
8
|
-
function, method,
|
|
9
|
-
r_hash::ForEach,
|
|
18
|
+
Error, RArray, RModule, RString, Ruby, TryConvert, Value, function, method,
|
|
19
|
+
prelude::*,
|
|
10
20
|
typed_data::{Inspect, Obj},
|
|
11
21
|
};
|
|
12
22
|
use wreq::header::OrigHeaderMap;
|
|
13
23
|
|
|
14
|
-
use crate::error::{
|
|
15
|
-
|
|
24
|
+
use crate::error::{header_value_error_to_magnus, type_value_error_to_magnus};
|
|
25
|
+
|
|
26
|
+
use self::helper::{
|
|
27
|
+
ensure_header_count, from_source, header_count_error, parse_header_name, parse_header_values,
|
|
16
28
|
};
|
|
17
29
|
|
|
18
|
-
/// A
|
|
30
|
+
/// A validated User-Agent header value accepted from Ruby.
|
|
19
31
|
pub struct UserAgent(pub HeaderValue);
|
|
20
32
|
|
|
21
|
-
/// HTTP headers
|
|
33
|
+
/// Mutable HTTP headers exposed as `Wreq::Headers`.
|
|
22
34
|
///
|
|
23
|
-
///
|
|
24
|
-
///
|
|
35
|
+
/// Names are stored in their normalized form and lookups are case-insensitive.
|
|
36
|
+
/// A name can have multiple values, each counted as a separate occurrence.
|
|
25
37
|
#[derive(Clone, Default)]
|
|
26
38
|
#[magnus::wrap(class = "Wreq::Headers", free_immediately, size)]
|
|
27
39
|
pub struct Headers(pub RefCell<HeaderMap>);
|
|
28
40
|
|
|
29
|
-
///
|
|
41
|
+
/// Header casing and order supplied through the `orig_headers` request option.
|
|
30
42
|
pub struct OrigHeaders(pub OrigHeaderMap);
|
|
31
43
|
|
|
32
|
-
struct HeaderIter {
|
|
33
|
-
inner: http::header::IntoIter<HeaderValue>,
|
|
34
|
-
next_name: Option<HeaderName>,
|
|
35
|
-
}
|
|
36
|
-
|
|
37
44
|
// ===== impl UserAgent =====
|
|
38
45
|
|
|
39
46
|
impl TryConvert for UserAgent {
|
|
40
47
|
fn try_convert(value: Value) -> Result<Self, Error> {
|
|
41
48
|
let s = RString::try_convert(value)?;
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
49
|
+
HeaderValue::from_maybe_shared(s.to_bytes())
|
|
50
|
+
.map(Self)
|
|
51
|
+
.map_err(header_value_error_to_magnus)
|
|
45
52
|
}
|
|
46
53
|
}
|
|
47
54
|
|
|
48
55
|
// ===== impl Headers =====
|
|
49
56
|
|
|
50
57
|
impl Headers {
|
|
51
|
-
///
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
58
|
+
/// Return the first value for a String or Symbol header name.
|
|
59
|
+
///
|
|
60
|
+
/// Returns `nil` when the normalized name is not present.
|
|
61
|
+
pub fn get(&self, name: Value) -> Result<Option<Bytes>, Error> {
|
|
62
|
+
let name = parse_header_name(name)?;
|
|
63
|
+
Ok(self.0.borrow().get(name).cloned().map(Bytes::from_owner))
|
|
55
64
|
}
|
|
56
65
|
|
|
57
|
-
///
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
66
|
+
/// Return every value for a String or Symbol header name.
|
|
67
|
+
///
|
|
68
|
+
/// Values retain their append order. A missing name returns an empty Array.
|
|
69
|
+
pub fn get_all(ruby: &Ruby, rb_self: &Self, name: Value) -> Result<RArray, Error> {
|
|
70
|
+
let name = parse_header_name(name)?;
|
|
71
|
+
let headers = rb_self.0.borrow();
|
|
72
|
+
let values = headers.get_all(name).iter().cloned().map(Bytes::from_owner);
|
|
73
|
+
Ok(ruby.ary_from_iter(values))
|
|
61
74
|
}
|
|
62
75
|
|
|
63
|
-
///
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
.
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
76
|
+
/// Replace every value for a header name.
|
|
77
|
+
///
|
|
78
|
+
/// A String stores one occurrence, while an Array stores each String as a
|
|
79
|
+
/// separate occurrence. An empty Array removes the header.
|
|
80
|
+
pub fn set(&self, name: Value, value: Value) -> Result<(), Error> {
|
|
81
|
+
let name = parse_header_name(name)?;
|
|
82
|
+
let values = parse_header_values(value)?;
|
|
83
|
+
let mut headers = self.0.borrow_mut();
|
|
84
|
+
let replaced = headers.get_all(&name).iter().count();
|
|
85
|
+
ensure_header_count(headers.len(), replaced, values.len())?;
|
|
86
|
+
|
|
87
|
+
let mut values = values.into_iter();
|
|
88
|
+
let Some(first) = values.next() else {
|
|
89
|
+
headers.remove(name);
|
|
90
|
+
return Ok(());
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
headers
|
|
94
|
+
.try_insert(name.clone(), first)
|
|
95
|
+
.map_err(|_| header_count_error())?;
|
|
96
|
+
for value in values {
|
|
97
|
+
headers
|
|
98
|
+
.try_append(name.clone(), value)
|
|
99
|
+
.map_err(|_| header_count_error())?;
|
|
100
|
+
}
|
|
86
101
|
Ok(())
|
|
87
102
|
}
|
|
88
103
|
|
|
89
|
-
/// Append
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
let
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
104
|
+
/// Append one or more values without replacing existing occurrences.
|
|
105
|
+
///
|
|
106
|
+
/// Array elements are appended separately and are never comma-folded.
|
|
107
|
+
pub fn append(&self, name: Value, value: Value) -> Result<(), Error> {
|
|
108
|
+
let name = parse_header_name(name)?;
|
|
109
|
+
let values = parse_header_values(value)?;
|
|
110
|
+
let mut headers = self.0.borrow_mut();
|
|
111
|
+
ensure_header_count(headers.len(), 0, values.len())?;
|
|
112
|
+
|
|
113
|
+
for value in values {
|
|
114
|
+
headers
|
|
115
|
+
.try_append(name.clone(), value)
|
|
116
|
+
.map_err(|_| header_count_error())?;
|
|
117
|
+
}
|
|
98
118
|
Ok(())
|
|
99
119
|
}
|
|
100
120
|
|
|
101
|
-
/// Remove
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
121
|
+
/// Remove every value for a header name and return its first value.
|
|
122
|
+
///
|
|
123
|
+
/// Returns `nil` when the normalized name is not present.
|
|
124
|
+
pub fn remove(&self, name: Value) -> Result<Option<Bytes>, Error> {
|
|
125
|
+
let name = parse_header_name(name)?;
|
|
126
|
+
Ok(self.0.borrow_mut().remove(name).map(Bytes::from_owner))
|
|
105
127
|
}
|
|
106
128
|
|
|
107
|
-
///
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
self.0.borrow().contains_key(
|
|
129
|
+
/// Return whether a String or Symbol header name is present.
|
|
130
|
+
pub fn contains(&self, name: Value) -> Result<bool, Error> {
|
|
131
|
+
let name = parse_header_name(name)?;
|
|
132
|
+
Ok(self.0.borrow().contains_key(name))
|
|
111
133
|
}
|
|
112
134
|
|
|
113
|
-
///
|
|
135
|
+
/// Return the total number of header occurrences.
|
|
136
|
+
///
|
|
137
|
+
/// This can be greater than `keys.length` when names have multiple values.
|
|
114
138
|
#[inline]
|
|
115
139
|
pub fn len(&self) -> usize {
|
|
116
140
|
self.0.borrow().len()
|
|
117
141
|
}
|
|
118
142
|
|
|
119
|
-
///
|
|
143
|
+
/// Return whether the collection contains no header occurrences.
|
|
120
144
|
#[inline]
|
|
121
145
|
pub fn is_empty(&self) -> bool {
|
|
122
146
|
self.0.borrow().is_empty()
|
|
123
147
|
}
|
|
124
148
|
|
|
125
|
-
///
|
|
126
|
-
#[inline]
|
|
127
|
-
pub fn clear(&self) {
|
|
128
|
-
self.0.borrow_mut().clear();
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/// Get all header names.
|
|
132
|
-
#[inline]
|
|
149
|
+
/// Return each unique normalized header name.
|
|
133
150
|
pub fn keys(ruby: &Ruby, rb_self: &Self) -> RArray {
|
|
134
151
|
ruby.ary_from_iter(rb_self.0.borrow().keys().cloned().map(Bytes::from_owner))
|
|
135
152
|
}
|
|
136
153
|
|
|
137
|
-
///
|
|
154
|
+
/// Return all values, including duplicate-name occurrences.
|
|
138
155
|
#[inline]
|
|
139
156
|
pub fn values(ruby: &Ruby, rb_self: &Self) -> RArray {
|
|
140
157
|
ruby.ary_from_iter(rb_self.0.borrow().values().cloned().map(Bytes::from_owner))
|
|
141
158
|
}
|
|
142
159
|
|
|
143
|
-
///
|
|
144
|
-
#[inline]
|
|
145
|
-
pub fn each(&self) -> Yield<impl Iterator<Item = (Bytes, Bytes)>> {
|
|
146
|
-
Yield::Iter(HeaderIter {
|
|
147
|
-
inner: self.0.borrow().clone().into_iter(),
|
|
148
|
-
next_name: None,
|
|
149
|
-
})
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/// Convert headers to string representation.
|
|
160
|
+
/// Return the debug representation of the underlying header map.
|
|
153
161
|
#[inline]
|
|
154
162
|
pub fn to_s(&self) -> String {
|
|
155
163
|
self.0.borrow().inspect()
|
|
156
164
|
}
|
|
157
165
|
}
|
|
158
166
|
|
|
167
|
+
// Ruby collection adapters are kept separate from the core HeaderMap operations.
|
|
168
|
+
impl Headers {
|
|
169
|
+
/// Create an empty collection or populate it from a Ruby source.
|
|
170
|
+
///
|
|
171
|
+
/// The optional source may be a Hash, another `Wreq::Headers`, or an
|
|
172
|
+
/// Enumerable whose elements are name-value pairs.
|
|
173
|
+
pub fn new(ruby: &Ruby, args: &[Value]) -> Result<Self, Error> {
|
|
174
|
+
match args {
|
|
175
|
+
[] => Ok(Self::default()),
|
|
176
|
+
[source] => from_source(*source),
|
|
177
|
+
_ => Err(Error::new(
|
|
178
|
+
ruby.exception_arg_error(),
|
|
179
|
+
format!(
|
|
180
|
+
"wrong number of arguments (given {}, expected 0..1)",
|
|
181
|
+
args.len()
|
|
182
|
+
),
|
|
183
|
+
)),
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/// Return a value using Ruby collection semantics.
|
|
188
|
+
///
|
|
189
|
+
/// A missing name returns `nil`, one occurrence returns a String, and
|
|
190
|
+
/// multiple occurrences return an Array of Strings.
|
|
191
|
+
pub fn index(ruby: &Ruby, rb_self: &Self, name: Value) -> Result<Value, Error> {
|
|
192
|
+
let name = parse_header_name(name)?;
|
|
193
|
+
let headers = rb_self.0.borrow();
|
|
194
|
+
let all_values = headers.get_all(name);
|
|
195
|
+
let mut values = all_values.iter();
|
|
196
|
+
|
|
197
|
+
let Some(first) = values.next() else {
|
|
198
|
+
return Ok(ruby.qnil().as_value());
|
|
199
|
+
};
|
|
200
|
+
let Some(second) = values.next() else {
|
|
201
|
+
return Ok(ruby.into_value(Bytes::from_owner(first.clone())));
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
let values = std::iter::once(first)
|
|
205
|
+
.chain(std::iter::once(second))
|
|
206
|
+
.chain(values)
|
|
207
|
+
.cloned()
|
|
208
|
+
.map(Bytes::from_owner);
|
|
209
|
+
Ok(ruby.into_value(ruby.ary_from_iter(values)))
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/// Replace a header and return the assigned Ruby value for `headers[name] = value`.
|
|
213
|
+
pub fn set_index(&self, name: Value, value: Value) -> Result<Value, Error> {
|
|
214
|
+
self.set(name, value)?;
|
|
215
|
+
Ok(value)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/// Remove every occurrence and return the same `Wreq::Headers` object.
|
|
219
|
+
pub fn clear(rb_self: Value) -> Result<Value, Error> {
|
|
220
|
+
let headers = Obj::<Headers>::try_convert(rb_self)?;
|
|
221
|
+
headers.0.borrow_mut().clear();
|
|
222
|
+
Ok(rb_self)
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/// Yield every normalized name-value occurrence.
|
|
226
|
+
///
|
|
227
|
+
/// Returns an Enumerator without a block and returns the collection after
|
|
228
|
+
/// yielding when a block is provided.
|
|
229
|
+
pub fn each(ruby: &Ruby, rb_self: Value) -> Result<Value, Error> {
|
|
230
|
+
if !ruby.block_given() {
|
|
231
|
+
return Ok(ruby.into_value(rb_self.enumeratorize("each", ())));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
let headers = Obj::<Headers>::try_convert(rb_self)?;
|
|
235
|
+
// Release the RefCell borrow before yielding because Ruby code may
|
|
236
|
+
// mutate this collection from inside the block.
|
|
237
|
+
let entries: Vec<_> = headers
|
|
238
|
+
.0
|
|
239
|
+
.borrow()
|
|
240
|
+
.iter()
|
|
241
|
+
.map(|(name, value)| {
|
|
242
|
+
(
|
|
243
|
+
Bytes::from_owner(name.clone()),
|
|
244
|
+
Bytes::from_owner(value.clone()),
|
|
245
|
+
)
|
|
246
|
+
})
|
|
247
|
+
.collect();
|
|
248
|
+
for (name, value) in entries {
|
|
249
|
+
let _: Value = ruby.yield_values((name, value))?;
|
|
250
|
+
}
|
|
251
|
+
Ok(rb_self)
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
159
255
|
impl From<HeaderMap> for Headers {
|
|
160
256
|
fn from(headers: HeaderMap) -> Self {
|
|
161
257
|
Self(RefCell::new(headers))
|
|
@@ -164,32 +260,14 @@ impl From<HeaderMap> for Headers {
|
|
|
164
260
|
|
|
165
261
|
impl TryConvert for Headers {
|
|
166
262
|
fn try_convert(value: Value) -> Result<Self, Error> {
|
|
167
|
-
|
|
168
|
-
let mut headers = HeaderMap::new();
|
|
169
|
-
|
|
170
|
-
rhash.foreach(|name: RString, value: RString| {
|
|
171
|
-
let name = HeaderName::from_bytes(&name.to_bytes())
|
|
172
|
-
.map_err(header_name_error_to_magnus)?;
|
|
173
|
-
let value = HeaderValue::from_maybe_shared(value.to_bytes())
|
|
174
|
-
.map_err(header_value_error_to_magnus)?;
|
|
175
|
-
headers.insert(name, value);
|
|
176
|
-
|
|
177
|
-
Ok(ForEach::Continue)
|
|
178
|
-
})?;
|
|
179
|
-
|
|
180
|
-
return Ok(Self::from(headers));
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
Obj::<Headers>::try_convert(value)
|
|
184
|
-
.map(|headers| headers.0.clone())
|
|
185
|
-
.map(Self)
|
|
263
|
+
from_source(value)
|
|
186
264
|
}
|
|
187
265
|
}
|
|
188
266
|
|
|
189
267
|
// ===== impl OrigHeaders =====
|
|
190
268
|
|
|
191
269
|
impl TryConvert for OrigHeaders {
|
|
192
|
-
fn try_convert(value:
|
|
270
|
+
fn try_convert(value: Value) -> Result<Self, Error> {
|
|
193
271
|
let mut map = OrigHeaderMap::new();
|
|
194
272
|
|
|
195
273
|
let rarray = RArray::from_value(value)
|
|
@@ -203,30 +281,11 @@ impl TryConvert for OrigHeaders {
|
|
|
203
281
|
}
|
|
204
282
|
}
|
|
205
283
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
impl Iterator for HeaderIter {
|
|
209
|
-
type Item = (Bytes, Bytes);
|
|
210
|
-
fn next(&mut self) -> Option<Self::Item> {
|
|
211
|
-
let (name, value) = self.inner.next()?;
|
|
212
|
-
match (&self.next_name, name) {
|
|
213
|
-
(Some(next_name), None) => Some((
|
|
214
|
-
Bytes::from_owner(next_name.clone()),
|
|
215
|
-
Bytes::from_owner(value),
|
|
216
|
-
)),
|
|
217
|
-
(_, Some(name)) => {
|
|
218
|
-
self.next_name = Some(name.clone());
|
|
219
|
-
Some((Bytes::from_owner(name), Bytes::from_owner(value)))
|
|
220
|
-
}
|
|
221
|
-
(None, None) => None,
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
284
|
+
/// Register `Wreq::Headers` and its native methods with Ruby.
|
|
226
285
|
pub fn include(ruby: &Ruby, gem_module: &RModule) -> Result<(), Error> {
|
|
227
|
-
// Define Headers class with methods
|
|
228
286
|
let headers_class = gem_module.define_class("Headers", ruby.class_object())?;
|
|
229
|
-
|
|
287
|
+
|
|
288
|
+
// Core bindings expose direct HeaderMap operations.
|
|
230
289
|
headers_class.define_method("get", method!(Headers::get, 1))?;
|
|
231
290
|
headers_class.define_method("get_all", method!(Headers::get_all, 1))?;
|
|
232
291
|
headers_class.define_method("set", method!(Headers::set, 2))?;
|
|
@@ -236,10 +295,16 @@ pub fn include(ruby: &Ruby, gem_module: &RModule) -> Result<(), Error> {
|
|
|
236
295
|
headers_class.define_method("key?", method!(Headers::contains, 1))?;
|
|
237
296
|
headers_class.define_method("length", method!(Headers::len, 0))?;
|
|
238
297
|
headers_class.define_method("empty?", method!(Headers::is_empty, 0))?;
|
|
239
|
-
headers_class.define_method("clear", method!(Headers::clear, 0))?;
|
|
240
298
|
headers_class.define_method("keys", method!(Headers::keys, 0))?;
|
|
241
299
|
headers_class.define_method("values", method!(Headers::values, 0))?;
|
|
242
|
-
headers_class.define_method("each", method!(Headers::each, 0))?;
|
|
243
300
|
headers_class.define_method("to_s", method!(Headers::to_s, 0))?;
|
|
301
|
+
|
|
302
|
+
// Ruby collection bindings cover construction, indexing, and block semantics.
|
|
303
|
+
headers_class.include_module(ruby.module_enumerable())?;
|
|
304
|
+
headers_class.define_singleton_method("new", function!(Headers::new, -1))?;
|
|
305
|
+
headers_class.define_method("[]", method!(Headers::index, 1))?;
|
|
306
|
+
headers_class.define_method("[]=", method!(Headers::set_index, 2))?;
|
|
307
|
+
headers_class.define_method("clear", method!(Headers::clear, 0))?;
|
|
308
|
+
headers_class.define_method("each", method!(Headers::each, 0))?;
|
|
244
309
|
Ok(())
|
|
245
310
|
}
|