wreq-rb 0.3.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.
Files changed (167) hide show
  1. checksums.yaml +7 -0
  2. data/Cargo.lock +2688 -0
  3. data/Cargo.toml +6 -0
  4. data/README.md +179 -0
  5. data/ext/wreq_rb/Cargo.toml +39 -0
  6. data/ext/wreq_rb/extconf.rb +22 -0
  7. data/ext/wreq_rb/src/client.rs +565 -0
  8. data/ext/wreq_rb/src/error.rs +25 -0
  9. data/ext/wreq_rb/src/lib.rs +20 -0
  10. data/ext/wreq_rb/src/response.rs +132 -0
  11. data/lib/wreq-rb/version.rb +5 -0
  12. data/lib/wreq-rb.rb +17 -0
  13. data/patches/0001-add-transfer-size-tracking.patch +292 -0
  14. data/vendor/wreq/Cargo.toml +306 -0
  15. data/vendor/wreq/LICENSE +202 -0
  16. data/vendor/wreq/README.md +122 -0
  17. data/vendor/wreq/examples/cert_store.rs +77 -0
  18. data/vendor/wreq/examples/connect_via_lower_priority_tokio_runtime.rs +258 -0
  19. data/vendor/wreq/examples/emulation.rs +118 -0
  20. data/vendor/wreq/examples/form.rs +14 -0
  21. data/vendor/wreq/examples/http1_websocket.rs +37 -0
  22. data/vendor/wreq/examples/http2_websocket.rs +45 -0
  23. data/vendor/wreq/examples/json_dynamic.rs +41 -0
  24. data/vendor/wreq/examples/json_typed.rs +47 -0
  25. data/vendor/wreq/examples/keylog.rs +16 -0
  26. data/vendor/wreq/examples/request_with_emulation.rs +115 -0
  27. data/vendor/wreq/examples/request_with_interface.rs +37 -0
  28. data/vendor/wreq/examples/request_with_local_address.rs +16 -0
  29. data/vendor/wreq/examples/request_with_proxy.rs +13 -0
  30. data/vendor/wreq/examples/request_with_redirect.rs +22 -0
  31. data/vendor/wreq/examples/request_with_version.rs +15 -0
  32. data/vendor/wreq/examples/tor_socks.rs +24 -0
  33. data/vendor/wreq/examples/unix_socket.rs +33 -0
  34. data/vendor/wreq/src/client/body.rs +304 -0
  35. data/vendor/wreq/src/client/conn/conn.rs +231 -0
  36. data/vendor/wreq/src/client/conn/connector.rs +549 -0
  37. data/vendor/wreq/src/client/conn/http.rs +1023 -0
  38. data/vendor/wreq/src/client/conn/proxy/socks.rs +233 -0
  39. data/vendor/wreq/src/client/conn/proxy/tunnel.rs +260 -0
  40. data/vendor/wreq/src/client/conn/proxy.rs +39 -0
  41. data/vendor/wreq/src/client/conn/tls_info.rs +98 -0
  42. data/vendor/wreq/src/client/conn/uds.rs +44 -0
  43. data/vendor/wreq/src/client/conn/verbose.rs +149 -0
  44. data/vendor/wreq/src/client/conn.rs +323 -0
  45. data/vendor/wreq/src/client/core/body/incoming.rs +485 -0
  46. data/vendor/wreq/src/client/core/body/length.rs +118 -0
  47. data/vendor/wreq/src/client/core/body.rs +34 -0
  48. data/vendor/wreq/src/client/core/common/buf.rs +149 -0
  49. data/vendor/wreq/src/client/core/common/rewind.rs +141 -0
  50. data/vendor/wreq/src/client/core/common/watch.rs +76 -0
  51. data/vendor/wreq/src/client/core/common.rs +3 -0
  52. data/vendor/wreq/src/client/core/conn/http1.rs +342 -0
  53. data/vendor/wreq/src/client/core/conn/http2.rs +307 -0
  54. data/vendor/wreq/src/client/core/conn.rs +11 -0
  55. data/vendor/wreq/src/client/core/dispatch.rs +299 -0
  56. data/vendor/wreq/src/client/core/error.rs +435 -0
  57. data/vendor/wreq/src/client/core/ext.rs +201 -0
  58. data/vendor/wreq/src/client/core/http1.rs +178 -0
  59. data/vendor/wreq/src/client/core/http2.rs +483 -0
  60. data/vendor/wreq/src/client/core/proto/h1/conn.rs +988 -0
  61. data/vendor/wreq/src/client/core/proto/h1/decode.rs +1170 -0
  62. data/vendor/wreq/src/client/core/proto/h1/dispatch.rs +684 -0
  63. data/vendor/wreq/src/client/core/proto/h1/encode.rs +580 -0
  64. data/vendor/wreq/src/client/core/proto/h1/io.rs +879 -0
  65. data/vendor/wreq/src/client/core/proto/h1/role.rs +694 -0
  66. data/vendor/wreq/src/client/core/proto/h1.rs +104 -0
  67. data/vendor/wreq/src/client/core/proto/h2/client.rs +650 -0
  68. data/vendor/wreq/src/client/core/proto/h2/ping.rs +539 -0
  69. data/vendor/wreq/src/client/core/proto/h2.rs +379 -0
  70. data/vendor/wreq/src/client/core/proto/headers.rs +138 -0
  71. data/vendor/wreq/src/client/core/proto.rs +58 -0
  72. data/vendor/wreq/src/client/core/rt/bounds.rs +57 -0
  73. data/vendor/wreq/src/client/core/rt/timer.rs +150 -0
  74. data/vendor/wreq/src/client/core/rt/tokio.rs +99 -0
  75. data/vendor/wreq/src/client/core/rt.rs +25 -0
  76. data/vendor/wreq/src/client/core/upgrade.rs +267 -0
  77. data/vendor/wreq/src/client/core.rs +16 -0
  78. data/vendor/wreq/src/client/emulation.rs +161 -0
  79. data/vendor/wreq/src/client/http/client/error.rs +142 -0
  80. data/vendor/wreq/src/client/http/client/exec.rs +29 -0
  81. data/vendor/wreq/src/client/http/client/extra.rs +77 -0
  82. data/vendor/wreq/src/client/http/client/lazy.rs +79 -0
  83. data/vendor/wreq/src/client/http/client/pool.rs +1105 -0
  84. data/vendor/wreq/src/client/http/client/util.rs +104 -0
  85. data/vendor/wreq/src/client/http/client.rs +1003 -0
  86. data/vendor/wreq/src/client/http/future.rs +99 -0
  87. data/vendor/wreq/src/client/http.rs +1629 -0
  88. data/vendor/wreq/src/client/layer/config/options.rs +156 -0
  89. data/vendor/wreq/src/client/layer/config.rs +116 -0
  90. data/vendor/wreq/src/client/layer/cookie.rs +161 -0
  91. data/vendor/wreq/src/client/layer/decoder.rs +139 -0
  92. data/vendor/wreq/src/client/layer/redirect/future.rs +270 -0
  93. data/vendor/wreq/src/client/layer/redirect/policy.rs +63 -0
  94. data/vendor/wreq/src/client/layer/redirect.rs +145 -0
  95. data/vendor/wreq/src/client/layer/retry/classify.rs +105 -0
  96. data/vendor/wreq/src/client/layer/retry/scope.rs +51 -0
  97. data/vendor/wreq/src/client/layer/retry.rs +151 -0
  98. data/vendor/wreq/src/client/layer/timeout/body.rs +233 -0
  99. data/vendor/wreq/src/client/layer/timeout/future.rs +90 -0
  100. data/vendor/wreq/src/client/layer/timeout.rs +177 -0
  101. data/vendor/wreq/src/client/layer.rs +15 -0
  102. data/vendor/wreq/src/client/multipart.rs +717 -0
  103. data/vendor/wreq/src/client/request.rs +818 -0
  104. data/vendor/wreq/src/client/response.rs +534 -0
  105. data/vendor/wreq/src/client/ws/json.rs +99 -0
  106. data/vendor/wreq/src/client/ws/message.rs +453 -0
  107. data/vendor/wreq/src/client/ws.rs +714 -0
  108. data/vendor/wreq/src/client.rs +27 -0
  109. data/vendor/wreq/src/config.rs +140 -0
  110. data/vendor/wreq/src/cookie.rs +579 -0
  111. data/vendor/wreq/src/dns/gai.rs +249 -0
  112. data/vendor/wreq/src/dns/hickory.rs +78 -0
  113. data/vendor/wreq/src/dns/resolve.rs +180 -0
  114. data/vendor/wreq/src/dns.rs +69 -0
  115. data/vendor/wreq/src/error.rs +502 -0
  116. data/vendor/wreq/src/ext.rs +398 -0
  117. data/vendor/wreq/src/hash.rs +143 -0
  118. data/vendor/wreq/src/header.rs +506 -0
  119. data/vendor/wreq/src/into_uri.rs +187 -0
  120. data/vendor/wreq/src/lib.rs +586 -0
  121. data/vendor/wreq/src/proxy/mac.rs +82 -0
  122. data/vendor/wreq/src/proxy/matcher.rs +806 -0
  123. data/vendor/wreq/src/proxy/uds.rs +66 -0
  124. data/vendor/wreq/src/proxy/win.rs +31 -0
  125. data/vendor/wreq/src/proxy.rs +569 -0
  126. data/vendor/wreq/src/redirect.rs +575 -0
  127. data/vendor/wreq/src/retry.rs +198 -0
  128. data/vendor/wreq/src/sync.rs +129 -0
  129. data/vendor/wreq/src/tls/conn/cache.rs +123 -0
  130. data/vendor/wreq/src/tls/conn/cert_compression.rs +125 -0
  131. data/vendor/wreq/src/tls/conn/ext.rs +82 -0
  132. data/vendor/wreq/src/tls/conn/macros.rs +34 -0
  133. data/vendor/wreq/src/tls/conn/service.rs +138 -0
  134. data/vendor/wreq/src/tls/conn.rs +681 -0
  135. data/vendor/wreq/src/tls/keylog/handle.rs +64 -0
  136. data/vendor/wreq/src/tls/keylog.rs +99 -0
  137. data/vendor/wreq/src/tls/options.rs +464 -0
  138. data/vendor/wreq/src/tls/x509/identity.rs +122 -0
  139. data/vendor/wreq/src/tls/x509/parser.rs +71 -0
  140. data/vendor/wreq/src/tls/x509/store.rs +228 -0
  141. data/vendor/wreq/src/tls/x509.rs +68 -0
  142. data/vendor/wreq/src/tls.rs +154 -0
  143. data/vendor/wreq/src/trace.rs +55 -0
  144. data/vendor/wreq/src/util.rs +122 -0
  145. data/vendor/wreq/tests/badssl.rs +228 -0
  146. data/vendor/wreq/tests/brotli.rs +350 -0
  147. data/vendor/wreq/tests/client.rs +1098 -0
  148. data/vendor/wreq/tests/connector_layers.rs +227 -0
  149. data/vendor/wreq/tests/cookie.rs +306 -0
  150. data/vendor/wreq/tests/deflate.rs +347 -0
  151. data/vendor/wreq/tests/emulation.rs +260 -0
  152. data/vendor/wreq/tests/gzip.rs +347 -0
  153. data/vendor/wreq/tests/layers.rs +261 -0
  154. data/vendor/wreq/tests/multipart.rs +165 -0
  155. data/vendor/wreq/tests/proxy.rs +438 -0
  156. data/vendor/wreq/tests/redirect.rs +629 -0
  157. data/vendor/wreq/tests/retry.rs +135 -0
  158. data/vendor/wreq/tests/support/delay_server.rs +117 -0
  159. data/vendor/wreq/tests/support/error.rs +16 -0
  160. data/vendor/wreq/tests/support/layer.rs +183 -0
  161. data/vendor/wreq/tests/support/mod.rs +9 -0
  162. data/vendor/wreq/tests/support/server.rs +232 -0
  163. data/vendor/wreq/tests/timeouts.rs +281 -0
  164. data/vendor/wreq/tests/unix_socket.rs +135 -0
  165. data/vendor/wreq/tests/upgrade.rs +98 -0
  166. data/vendor/wreq/tests/zstd.rs +559 -0
  167. metadata +225 -0
@@ -0,0 +1,506 @@
1
+ //! HTTP header types
2
+ //!
3
+ //! This module provides [`HeaderName`], [`HeaderMap`], [`OrigHeaderMap`], [`OrigHeaderName`], and a
4
+ //! number of types used for interacting with `HeaderMap`. These types allow representing both
5
+ //! HTTP/1 and HTTP/2 headers.
6
+
7
+ pub use http::header::*;
8
+ pub use name::OrigHeaderName;
9
+
10
+ /// Trait for types that can be converted into an [`OrigHeaderName`] (case-preserved header).
11
+ ///
12
+ /// This trait is sealed, so only known types can implement it.
13
+ /// Supported types:
14
+ /// - `&'static str`
15
+ /// - `String`
16
+ /// - `Bytes`
17
+ /// - `HeaderName`
18
+ /// - `&HeaderName`
19
+ /// - `OrigHeaderName`
20
+ /// - `&OrigHeaderName`
21
+ pub trait IntoOrigHeaderName: sealed::Sealed {
22
+ /// Converts the type into an [`OrigHeaderName`].
23
+ fn into_orig_header_name(self) -> OrigHeaderName;
24
+ }
25
+
26
+ /// A map from header names to their original casing as received in an HTTP message.
27
+ ///
28
+ /// [`OrigHeaderMap`] not only preserves the original case of each header name as it appeared
29
+ /// in the request or response, but also maintains the insertion order of headers. This makes
30
+ /// it suitable for use cases where the order of headers matters, such as HTTP/1.x message
31
+ /// serialization, proxying, or reproducing requests/responses exactly as received.
32
+ #[derive(Debug, Clone, Default)]
33
+ pub struct OrigHeaderMap(HeaderMap<OrigHeaderName>);
34
+
35
+ // ===== impl OrigHeaderMap =====
36
+
37
+ impl OrigHeaderMap {
38
+ /// Creates a new, empty [`OrigHeaderMap`].
39
+ #[inline]
40
+ pub fn new() -> Self {
41
+ Self(HeaderMap::default())
42
+ }
43
+
44
+ /// Creates an empty [`OrigHeaderMap`] with the specified capacity.
45
+ #[inline]
46
+ pub fn with_capacity(size: usize) -> Self {
47
+ Self(HeaderMap::with_capacity(size))
48
+ }
49
+
50
+ /// Insert a new header name into the collection.
51
+ ///
52
+ /// If the map did not previously have this key present, then `false` is
53
+ /// returned.
54
+ ///
55
+ /// If the map did have this key present, the new value is pushed to the end
56
+ /// of the list of values currently associated with the key. The key is not
57
+ /// updated, though; this matters for types that can be `==` without being
58
+ /// identical.
59
+ #[inline]
60
+ pub fn insert<N>(&mut self, orig: N) -> bool
61
+ where
62
+ N: IntoOrigHeaderName,
63
+ {
64
+ let orig_header_name = orig.into_orig_header_name();
65
+ match &orig_header_name.kind {
66
+ name::Kind::Cased(bytes) => HeaderName::from_bytes(bytes)
67
+ .map(|name| self.0.append(name, orig_header_name))
68
+ .unwrap_or(false),
69
+ name::Kind::Standard(header_name) => {
70
+ self.0.append(header_name.clone(), orig_header_name)
71
+ }
72
+ }
73
+ }
74
+
75
+ /// Extends the map with all entries from another [`OrigHeaderMap`], preserving order.
76
+ #[inline]
77
+ pub fn extend(&mut self, iter: OrigHeaderMap) {
78
+ self.0.extend(iter.0);
79
+ }
80
+
81
+ /// Returns the number of headers stored in the map.
82
+ ///
83
+ /// This number represents the total number of **values** stored in the map.
84
+ /// This number can be greater than or equal to the number of **keys**
85
+ /// stored given that a single key may have more than one associated value.
86
+ #[inline]
87
+ pub fn len(&self) -> usize {
88
+ self.0.len()
89
+ }
90
+
91
+ /// Returns true if the map contains no elements.
92
+ #[inline]
93
+ pub fn is_empty(&self) -> bool {
94
+ self.0.is_empty()
95
+ }
96
+
97
+ /// Returns an iterator over all header names and their original spellings, in insertion order.
98
+ #[inline]
99
+ pub fn iter(&self) -> impl Iterator<Item = (&HeaderName, &OrigHeaderName)> {
100
+ self.0.iter()
101
+ }
102
+ }
103
+
104
+ impl OrigHeaderMap {
105
+ /// Sorts headers by this map, preserving original casing.
106
+ /// Headers in the map come first, others follow.
107
+ pub(crate) fn sort_headers(&self, headers: &mut HeaderMap) {
108
+ if headers.len() <= 1 || self.0.is_empty() {
109
+ return;
110
+ }
111
+
112
+ // Create a new header map to store the sorted headers
113
+ let mut sorted_headers = HeaderMap::with_capacity(headers.keys_len());
114
+
115
+ // First insert headers in the specified order
116
+ for name in self.0.keys() {
117
+ for value in headers.get_all(name) {
118
+ sorted_headers.append(name.clone(), value.clone());
119
+ }
120
+ headers.remove(name);
121
+ }
122
+
123
+ // Then insert any remaining headers that were not ordered
124
+ let mut prev_name: Option<HeaderName> = None;
125
+ for (name, value) in headers.drain() {
126
+ match (name, &prev_name) {
127
+ (Some(name), _) => {
128
+ prev_name.replace(name.clone());
129
+ sorted_headers.insert(name, value);
130
+ }
131
+ (None, Some(prev_name)) => {
132
+ sorted_headers.append(prev_name, value);
133
+ }
134
+ _ => {}
135
+ }
136
+ }
137
+
138
+ std::mem::swap(headers, &mut sorted_headers);
139
+ }
140
+
141
+ /// Calls the given function for each header in this map's order, preserving original casing.
142
+ /// Headers in the map are processed first, others follow.
143
+ pub(crate) fn sort_headers_for_each<F>(&self, headers: &mut HeaderMap, mut dst: F)
144
+ where
145
+ F: FnMut(&[u8], &HeaderValue),
146
+ {
147
+ // First, sort headers according to the order defined in this map
148
+ for (name, orig_name) in self.iter() {
149
+ for value in headers.get_all(name) {
150
+ dst(orig_name.as_ref(), value);
151
+ }
152
+
153
+ headers.remove(name);
154
+ }
155
+
156
+ // After processing all ordered headers, append any remaining headers
157
+ let mut prev_name: Option<OrigHeaderName> = None;
158
+ for (name, value) in headers.drain() {
159
+ match (name, &prev_name) {
160
+ (Some(name), _) => {
161
+ dst(name.as_ref(), &value);
162
+ prev_name.replace(name.into_orig_header_name());
163
+ }
164
+ (None, Some(prev_name)) => {
165
+ dst(prev_name.as_ref(), &value);
166
+ }
167
+ _ => (),
168
+ };
169
+ }
170
+ }
171
+ }
172
+
173
+ impl<'a> IntoIterator for &'a OrigHeaderMap {
174
+ type Item = (&'a HeaderName, &'a OrigHeaderName);
175
+ type IntoIter = <&'a HeaderMap<OrigHeaderName> as IntoIterator>::IntoIter;
176
+
177
+ #[inline]
178
+ fn into_iter(self) -> Self::IntoIter {
179
+ self.0.iter()
180
+ }
181
+ }
182
+
183
+ impl IntoIterator for OrigHeaderMap {
184
+ type Item = (Option<HeaderName>, OrigHeaderName);
185
+ type IntoIter = <HeaderMap<OrigHeaderName> as IntoIterator>::IntoIter;
186
+
187
+ #[inline]
188
+ fn into_iter(self) -> Self::IntoIter {
189
+ self.0.into_iter()
190
+ }
191
+ }
192
+
193
+ impl_request_config_value!(OrigHeaderMap);
194
+
195
+ mod name {
196
+ use bytes::Bytes;
197
+ use http::HeaderName;
198
+
199
+ use super::IntoOrigHeaderName;
200
+
201
+ /// An HTTP header name with both normalized and original casing.
202
+ ///
203
+ /// While HTTP headers are case-insensitive, this type stores both
204
+ /// the canonical `HeaderName` and the original casing as received,
205
+ /// useful for preserving header order and formatting in proxies,
206
+ /// debugging, or exact HTTP message reproduction.
207
+ #[derive(Debug, Clone, PartialEq, Eq)]
208
+ pub struct OrigHeaderName {
209
+ pub(super) kind: Kind,
210
+ }
211
+
212
+ #[derive(Debug, Clone, PartialEq, Eq)]
213
+ pub(super) enum Kind {
214
+ /// The original casing of the header name as received.
215
+ Cased(Bytes),
216
+ /// The canonical (normalized, lowercased) header name.
217
+ Standard(HeaderName),
218
+ }
219
+
220
+ impl AsRef<[u8]> for OrigHeaderName {
221
+ #[inline]
222
+ fn as_ref(&self) -> &[u8] {
223
+ match &self.kind {
224
+ Kind::Standard(name) => name.as_ref(),
225
+ Kind::Cased(orig) => orig.as_ref(),
226
+ }
227
+ }
228
+ }
229
+
230
+ impl IntoOrigHeaderName for &'static str {
231
+ #[inline]
232
+ fn into_orig_header_name(self) -> OrigHeaderName {
233
+ Bytes::from_static(self.as_bytes()).into_orig_header_name()
234
+ }
235
+ }
236
+
237
+ impl IntoOrigHeaderName for String {
238
+ #[inline]
239
+ fn into_orig_header_name(self) -> OrigHeaderName {
240
+ Bytes::from(self).into_orig_header_name()
241
+ }
242
+ }
243
+
244
+ impl IntoOrigHeaderName for Bytes {
245
+ #[inline]
246
+ fn into_orig_header_name(self) -> OrigHeaderName {
247
+ OrigHeaderName {
248
+ kind: Kind::Cased(self),
249
+ }
250
+ }
251
+ }
252
+
253
+ impl IntoOrigHeaderName for &HeaderName {
254
+ #[inline]
255
+ fn into_orig_header_name(self) -> OrigHeaderName {
256
+ OrigHeaderName {
257
+ kind: Kind::Standard(self.clone()),
258
+ }
259
+ }
260
+ }
261
+
262
+ impl IntoOrigHeaderName for HeaderName {
263
+ #[inline]
264
+ fn into_orig_header_name(self) -> OrigHeaderName {
265
+ OrigHeaderName {
266
+ kind: Kind::Standard(self),
267
+ }
268
+ }
269
+ }
270
+
271
+ impl IntoOrigHeaderName for OrigHeaderName {
272
+ #[inline]
273
+ fn into_orig_header_name(self) -> OrigHeaderName {
274
+ self
275
+ }
276
+ }
277
+
278
+ impl IntoOrigHeaderName for &OrigHeaderName {
279
+ #[inline]
280
+ fn into_orig_header_name(self) -> OrigHeaderName {
281
+ self.clone()
282
+ }
283
+ }
284
+ }
285
+
286
+ mod sealed {
287
+
288
+ use bytes::Bytes;
289
+ use http::HeaderName;
290
+
291
+ use crate::header::OrigHeaderName;
292
+
293
+ pub trait Sealed {}
294
+
295
+ impl Sealed for &'static str {}
296
+ impl Sealed for String {}
297
+ impl Sealed for Bytes {}
298
+ impl Sealed for &HeaderName {}
299
+ impl Sealed for HeaderName {}
300
+ impl Sealed for &OrigHeaderName {}
301
+ impl Sealed for OrigHeaderName {}
302
+ }
303
+
304
+ #[cfg(test)]
305
+ mod test {
306
+ use http::{HeaderMap, HeaderName, HeaderValue};
307
+
308
+ use super::OrigHeaderMap;
309
+
310
+ /// Returns a view of all spellings associated with that header name,
311
+ /// in the order they were found.
312
+ #[inline]
313
+ pub(crate) fn get_all<'a>(
314
+ orig_headers: &'a OrigHeaderMap,
315
+ name: &HeaderName,
316
+ ) -> impl Iterator<Item = impl AsRef<[u8]> + 'a> + 'a {
317
+ orig_headers.0.get_all(name).into_iter()
318
+ }
319
+
320
+ #[test]
321
+ fn test_header_order() {
322
+ let mut headers = OrigHeaderMap::new();
323
+
324
+ // Insert headers with different cases and order
325
+ headers.insert("X-Test");
326
+ headers.insert("X-Another");
327
+ headers.insert("x-test2");
328
+
329
+ // Check order and case
330
+ let mut iter = headers.iter();
331
+ assert_eq!(iter.next().unwrap().1.as_ref(), b"X-Test");
332
+ assert_eq!(iter.next().unwrap().1.as_ref(), b"X-Another");
333
+ assert_eq!(iter.next().unwrap().1.as_ref(), b"x-test2");
334
+ }
335
+
336
+ #[test]
337
+ fn test_extend_preserves_order() {
338
+ use super::OrigHeaderMap;
339
+
340
+ let mut map1 = OrigHeaderMap::new();
341
+ map1.insert("A-Header");
342
+ map1.insert("B-Header");
343
+
344
+ let mut map2 = OrigHeaderMap::new();
345
+ map2.insert("C-Header");
346
+ map2.insert("D-Header");
347
+
348
+ map1.extend(map2);
349
+
350
+ let names: Vec<_> = map1.iter().map(|(_, orig)| orig.as_ref()).collect();
351
+ assert_eq!(
352
+ names,
353
+ vec![b"A-Header", b"B-Header", b"C-Header", b"D-Header"]
354
+ );
355
+ }
356
+
357
+ #[test]
358
+ fn test_header_case() {
359
+ let mut headers = OrigHeaderMap::new();
360
+
361
+ // Insert headers with different cases
362
+ headers.insert("X-Test");
363
+ headers.insert("x-test");
364
+
365
+ // Check that both headers are stored
366
+ let all_x_test: Vec<_> = get_all(&headers, &"X-Test".parse().unwrap()).collect();
367
+ assert_eq!(all_x_test.len(), 2);
368
+ assert!(all_x_test.iter().any(|v| v.as_ref() == b"X-Test"));
369
+ assert!(all_x_test.iter().any(|v| v.as_ref() == b"x-test"));
370
+ }
371
+
372
+ #[test]
373
+ fn test_header_multiple_cases() {
374
+ let mut headers = OrigHeaderMap::new();
375
+
376
+ // Insert multiple headers with the same name but different cases
377
+ headers.insert("X-test");
378
+ headers.insert("x-test");
379
+ headers.insert("X-test");
380
+
381
+ // Check that all variations are stored
382
+ let all_x_test: Vec<_> = get_all(&headers, &"x-test".parse().unwrap()).collect();
383
+ assert_eq!(all_x_test.len(), 3);
384
+ assert!(all_x_test.iter().any(|v| v.as_ref() == b"X-test"));
385
+ assert!(all_x_test.iter().any(|v| v.as_ref() == b"x-test"));
386
+ assert!(all_x_test.iter().any(|v| v.as_ref() == b"X-test"));
387
+ }
388
+
389
+ #[test]
390
+ fn test_sort_headers_preserves_multiple_cookie_values() {
391
+ // Create original header map for ordering
392
+ let mut orig_headers = OrigHeaderMap::new();
393
+ orig_headers.insert("Cookie");
394
+ orig_headers.insert("User-Agent");
395
+ orig_headers.insert("Accept");
396
+
397
+ // Create headers with multiple Cookie values
398
+ let mut headers = HeaderMap::new();
399
+
400
+ // Add multiple Cookie headers (this simulates how cookies are often sent)
401
+ headers.append("cookie", HeaderValue::from_static("session=abc123"));
402
+ headers.append("cookie", HeaderValue::from_static("theme=dark"));
403
+ headers.append("cookie", HeaderValue::from_static("lang=en"));
404
+
405
+ // Add other headers
406
+ headers.insert("user-agent", HeaderValue::from_static("Mozilla/5.0"));
407
+ headers.insert("accept", HeaderValue::from_static("text/html"));
408
+ headers.insert("host", HeaderValue::from_static("example.com"));
409
+
410
+ // Record original cookie values for comparison
411
+ let original_cookies: Vec<_> = headers
412
+ .get_all("cookie")
413
+ .iter()
414
+ .map(|v| v.to_str().unwrap().to_string())
415
+ .collect();
416
+
417
+ // Sort headers according to orig_headers order
418
+ orig_headers.sort_headers(&mut headers);
419
+
420
+ // Verify all cookie values are preserved
421
+ let sorted_cookies: Vec<_> = headers
422
+ .get_all("cookie")
423
+ .iter()
424
+ .map(|v| v.to_str().unwrap().to_string())
425
+ .collect();
426
+
427
+ assert_eq!(
428
+ original_cookies.len(),
429
+ sorted_cookies.len(),
430
+ "Cookie count should be preserved"
431
+ );
432
+ assert_eq!(original_cookies.len(), 3, "Should have 3 cookie values");
433
+
434
+ // Verify all original cookies are still present (order might change but content preserved)
435
+ for original_cookie in &original_cookies {
436
+ assert!(
437
+ sorted_cookies.contains(original_cookie),
438
+ "Cookie '{original_cookie}' should be preserved"
439
+ );
440
+ }
441
+
442
+ // Verify header ordering - Cookie should come first
443
+ let header_names: Vec<_> = headers.keys().collect();
444
+ assert_eq!(
445
+ header_names[0].as_str(),
446
+ "cookie",
447
+ "Cookie should be first header"
448
+ );
449
+
450
+ // Verify all headers are preserved
451
+ assert_eq!(
452
+ headers.len(),
453
+ 6,
454
+ "Should have 6 total header values (3 cookies + 3 others)"
455
+ );
456
+ assert!(headers.contains_key("user-agent"));
457
+ assert!(headers.contains_key("accept"));
458
+ assert!(headers.contains_key("host"));
459
+ }
460
+
461
+ #[test]
462
+ fn test_sort_headers_multiple_values_different_headers() {
463
+ let mut orig_headers = OrigHeaderMap::new();
464
+ orig_headers.insert("Accept");
465
+ orig_headers.insert("Cookie");
466
+
467
+ let mut headers = HeaderMap::new();
468
+
469
+ // Multiple Accept headers
470
+ headers.append("accept", HeaderValue::from_static("text/html"));
471
+ headers.append("accept", HeaderValue::from_static("application/json"));
472
+
473
+ // Multiple Cookie headers
474
+ headers.append("cookie", HeaderValue::from_static("a=1"));
475
+ headers.append("cookie", HeaderValue::from_static("b=2"));
476
+
477
+ // Single header
478
+ headers.insert("host", HeaderValue::from_static("example.com"));
479
+
480
+ let total_before = headers.len();
481
+
482
+ orig_headers.sort_headers(&mut headers);
483
+
484
+ // Verify all values preserved
485
+ assert_eq!(
486
+ headers.len(),
487
+ total_before,
488
+ "Total header count should be preserved"
489
+ );
490
+ assert_eq!(
491
+ headers.get_all("accept").iter().count(),
492
+ 2,
493
+ "Accept headers should be preserved"
494
+ );
495
+ assert_eq!(
496
+ headers.get_all("cookie").iter().count(),
497
+ 2,
498
+ "Cookie headers should be preserved"
499
+ );
500
+ assert_eq!(
501
+ headers.get_all("host").iter().count(),
502
+ 1,
503
+ "Host header should be preserved"
504
+ );
505
+ }
506
+ }
@@ -0,0 +1,187 @@
1
+ //! URI conversion utilities.
2
+ //!
3
+ //! This module provides the [`IntoUri`] trait, allowing various types
4
+ //! (such as `&str`, `String`, `Vec<u8>`, etc.) to be fallibly converted into an [`http::Uri`].
5
+ //! The conversion is based on `TryFrom<T> for Uri` and ensures the resulting URI is valid and
6
+ //! contains a host.
7
+ //!
8
+ //! Internally, the trait is sealed to prevent
9
+
10
+ use bytes::Bytes;
11
+ use http::Uri;
12
+
13
+ use crate::{Error, Result};
14
+
15
+ /// Converts a value into a [`Uri`] with error handling.
16
+ ///
17
+ /// This trait is implemented for common types such as [`Uri`], [`String`], [`&str`], and byte
18
+ /// slices, as well as any type that can be fallibly converted into a [`Uri`] via [`TryFrom`].
19
+ pub trait IntoUri: IntoUriSealed {}
20
+
21
+ impl IntoUri for Uri {}
22
+ impl IntoUri for &Uri {}
23
+ impl IntoUri for &str {}
24
+ impl IntoUri for String {}
25
+ impl IntoUri for &String {}
26
+ impl IntoUri for Vec<u8> {}
27
+ impl IntoUri for &[u8] {}
28
+
29
+ pub trait IntoUriSealed {
30
+ // Besides parsing as a valid `Uri`.
31
+ fn into_uri(self) -> Result<Uri>;
32
+ }
33
+
34
+ impl IntoUriSealed for &[u8] {
35
+ fn into_uri(self) -> Result<Uri> {
36
+ Uri::try_from(self)
37
+ .or_else(|_| internal::parse(internal::Kind::Bytes(self)))
38
+ .and_then(IntoUriSealed::into_uri)
39
+ }
40
+ }
41
+
42
+ impl IntoUriSealed for Vec<u8> {
43
+ fn into_uri(self) -> Result<Uri> {
44
+ let bytes = Bytes::from(self);
45
+ Uri::from_maybe_shared(bytes.clone())
46
+ .or_else(|_| internal::parse(internal::Kind::Bytes(&bytes)))
47
+ .and_then(IntoUriSealed::into_uri)
48
+ }
49
+ }
50
+
51
+ impl IntoUriSealed for &str {
52
+ fn into_uri(self) -> Result<Uri> {
53
+ Uri::try_from(self)
54
+ .or_else(|_| internal::parse(internal::Kind::Str(self)))
55
+ .and_then(IntoUriSealed::into_uri)
56
+ }
57
+ }
58
+
59
+ impl IntoUriSealed for String {
60
+ #[inline]
61
+ fn into_uri(self) -> Result<Uri> {
62
+ self.into_bytes().into_uri()
63
+ }
64
+ }
65
+
66
+ impl IntoUriSealed for &String {
67
+ #[inline]
68
+ fn into_uri(self) -> Result<Uri> {
69
+ IntoUriSealed::into_uri(self.as_str())
70
+ }
71
+ }
72
+
73
+ impl IntoUriSealed for Uri {
74
+ fn into_uri(self) -> Result<Uri> {
75
+ match (self.scheme(), self.authority()) {
76
+ (Some(_), Some(_)) => Ok(self),
77
+ _ => Err(Error::uri_bad_scheme(self)),
78
+ }
79
+ }
80
+ }
81
+
82
+ impl IntoUriSealed for &Uri {
83
+ fn into_uri(self) -> Result<Uri> {
84
+ match (self.scheme(), self.authority()) {
85
+ (Some(_), Some(_)) => Ok(self.clone()),
86
+ _ => Err(Error::uri_bad_scheme(self.clone())),
87
+ }
88
+ }
89
+ }
90
+
91
+ mod internal {
92
+ use http::Uri;
93
+ use url::Url;
94
+
95
+ use crate::{Error, Result};
96
+
97
+ pub(super) enum Kind<'a> {
98
+ Bytes(&'a [u8]),
99
+ Str(&'a str),
100
+ }
101
+
102
+ pub(super) fn parse(s: Kind) -> Result<Uri> {
103
+ let s = match s {
104
+ Kind::Bytes(bytes) => std::str::from_utf8(bytes).map_err(Error::decode),
105
+ Kind::Str(s) => Ok(s),
106
+ }?;
107
+
108
+ Url::parse(s)
109
+ .map(String::from)
110
+ .map_err(Error::builder)
111
+ .and_then(|s| Uri::try_from(s).map_err(Error::builder))
112
+ }
113
+ }
114
+
115
+ #[cfg(test)]
116
+ mod tests {
117
+ use super::IntoUriSealed;
118
+
119
+ #[test]
120
+ fn into_uri_bad_scheme() {
121
+ let err = "/hello/world".into_uri().unwrap_err();
122
+ assert_eq!(
123
+ err.to_string(),
124
+ "builder error for uri (/hello/world): URI scheme is not allowed"
125
+ );
126
+
127
+ let err = "127.0.0.1".into_uri().unwrap_err();
128
+ assert_eq!(
129
+ err.to_string(),
130
+ "builder error for uri (127.0.0.1): URI scheme is not allowed"
131
+ );
132
+ }
133
+
134
+ #[test]
135
+ fn into_uri_with_space_in_path() {
136
+ let uri = "http://example.com/hello world".into_uri().unwrap();
137
+ assert_eq!(uri, "http://example.com/hello%20world");
138
+ }
139
+
140
+ #[test]
141
+ fn into_uri_with_unicode_in_path() {
142
+ let uri = "http://example.com/文件/测试".into_uri().unwrap();
143
+ assert_eq!(uri, "http://example.com/文件/测试");
144
+ }
145
+
146
+ #[test]
147
+ fn into_uri_with_special_chars_in_path() {
148
+ let uri = "http://example.com/path<>{}".into_uri().unwrap();
149
+ assert_eq!(uri, "http://example.com/path%3C%3E%7B%7D");
150
+ }
151
+
152
+ #[test]
153
+ fn into_uri_with_query_preserved() {
154
+ let uri = "http://example.com/path?key=value&foo=bar"
155
+ .into_uri()
156
+ .unwrap();
157
+ assert_eq!(uri, "http://example.com/path?key=value&foo=bar");
158
+ }
159
+
160
+ #[test]
161
+ fn into_uri_bytes_with_encoding() {
162
+ let bytes = b"http://example.com/hello world";
163
+ let uri = bytes.into_uri().unwrap();
164
+ assert_eq!(uri, "http://example.com/hello%20world");
165
+ }
166
+
167
+ #[test]
168
+ fn test_bytes_with_query() {
169
+ let bytes = b"http://example.com/path?key=hello%20world";
170
+ let uri = bytes.into_uri().unwrap();
171
+ assert_eq!(uri.to_string(), "http://example.com/path?key=hello%20world");
172
+ }
173
+
174
+ #[test]
175
+ fn test_bytes_with_unicode() {
176
+ let bytes = b"http://example.com/\xE6\xB5\x8B\xE8\xAF\x95";
177
+ let uri = bytes.into_uri().unwrap();
178
+ assert_eq!(uri, "http://example.com/测试");
179
+ }
180
+
181
+ #[test]
182
+ fn test_bytes_minimal() {
183
+ let bytes = b"http://example.com";
184
+ let uri = bytes.into_uri().unwrap();
185
+ assert_eq!(uri, "http://example.com");
186
+ }
187
+ }