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,580 @@
1
+ use std::{collections::HashSet, fmt, io::IoSlice};
2
+
3
+ use bytes::{
4
+ Buf, Bytes,
5
+ buf::{Chain, Take},
6
+ };
7
+ use http::{
8
+ HeaderMap, HeaderName,
9
+ header::{
10
+ AUTHORIZATION, CACHE_CONTROL, CONTENT_ENCODING, CONTENT_LENGTH, CONTENT_RANGE,
11
+ CONTENT_TYPE, HOST, MAX_FORWARDS, SET_COOKIE, TE, TRAILER, TRANSFER_ENCODING,
12
+ },
13
+ };
14
+
15
+ use super::{io::WriteBuf, role::write_headers};
16
+
17
+ type StaticBuf = &'static [u8];
18
+
19
+ /// Encoders to handle different Transfer-Encodings.
20
+ #[derive(Debug, Clone, PartialEq)]
21
+ pub(crate) struct Encoder {
22
+ kind: Kind,
23
+ is_last: bool,
24
+ }
25
+
26
+ #[derive(Debug)]
27
+ pub(crate) struct EncodedBuf<B> {
28
+ kind: BufKind<B>,
29
+ }
30
+
31
+ #[derive(Debug)]
32
+ pub(crate) struct NotEof(u64);
33
+
34
+ #[derive(Debug, PartialEq, Clone)]
35
+ enum Kind {
36
+ /// An Encoder for when Transfer-Encoding includes `chunked`.
37
+ Chunked(Option<Vec<HeaderName>>),
38
+ /// An Encoder for when Content-Length is set.
39
+ ///
40
+ /// Enforces that the body is not longer than the Content-Length header.
41
+ Length(u64),
42
+ }
43
+
44
+ #[derive(Debug)]
45
+ enum BufKind<B> {
46
+ Exact(B),
47
+ Limited(Take<B>),
48
+ Chunked(Chain<Chain<ChunkSize, B>, StaticBuf>),
49
+ ChunkedEnd(StaticBuf),
50
+ Trailers(Chain<Chain<StaticBuf, Bytes>, StaticBuf>),
51
+ }
52
+
53
+ impl Encoder {
54
+ fn new(kind: Kind) -> Encoder {
55
+ Encoder {
56
+ kind,
57
+ is_last: false,
58
+ }
59
+ }
60
+ pub(crate) fn chunked() -> Encoder {
61
+ Encoder::new(Kind::Chunked(None))
62
+ }
63
+
64
+ pub(crate) fn length(len: u64) -> Encoder {
65
+ Encoder::new(Kind::Length(len))
66
+ }
67
+
68
+ pub(crate) fn into_chunked_with_trailing_fields(self, trailers: Vec<HeaderName>) -> Encoder {
69
+ match self.kind {
70
+ Kind::Chunked(_) => Encoder {
71
+ kind: Kind::Chunked(Some(trailers)),
72
+ is_last: self.is_last,
73
+ },
74
+ _ => self,
75
+ }
76
+ }
77
+
78
+ pub(crate) fn is_eof(&self) -> bool {
79
+ matches!(self.kind, Kind::Length(0))
80
+ }
81
+
82
+ pub(crate) fn is_last(&self) -> bool {
83
+ self.is_last
84
+ }
85
+
86
+ pub(crate) fn is_close_delimited(&self) -> bool {
87
+ false
88
+ }
89
+
90
+ pub(crate) fn is_chunked(&self) -> bool {
91
+ matches!(self.kind, Kind::Chunked(_))
92
+ }
93
+
94
+ pub(crate) fn end<B>(&self) -> Result<Option<EncodedBuf<B>>, NotEof> {
95
+ match self.kind {
96
+ Kind::Length(0) => Ok(None),
97
+ Kind::Chunked(_) => Ok(Some(EncodedBuf {
98
+ kind: BufKind::ChunkedEnd(b"0\r\n\r\n"),
99
+ })),
100
+ Kind::Length(n) => Err(NotEof(n)),
101
+ }
102
+ }
103
+
104
+ pub(crate) fn encode<B>(&mut self, msg: B) -> EncodedBuf<B>
105
+ where
106
+ B: Buf,
107
+ {
108
+ let len = msg.remaining();
109
+ debug_assert!(len > 0, "encode() called with empty buf");
110
+
111
+ let kind = match self.kind {
112
+ Kind::Chunked(_) => {
113
+ trace!("encoding chunked {}B", len);
114
+ let buf = ChunkSize::new(len)
115
+ .chain(msg)
116
+ .chain(b"\r\n" as &'static [u8]);
117
+ BufKind::Chunked(buf)
118
+ }
119
+ Kind::Length(ref mut remaining) => {
120
+ trace!("sized write, len = {}", len);
121
+ if len as u64 > *remaining {
122
+ let limit = *remaining as usize;
123
+ *remaining = 0;
124
+ BufKind::Limited(msg.take(limit))
125
+ } else {
126
+ *remaining -= len as u64;
127
+ BufKind::Exact(msg)
128
+ }
129
+ }
130
+ };
131
+ EncodedBuf { kind }
132
+ }
133
+
134
+ pub(crate) fn encode_trailers<B>(&self, trailers: HeaderMap) -> Option<EncodedBuf<B>> {
135
+ trace!("encoding trailers");
136
+ match &self.kind {
137
+ Kind::Chunked(Some(allowed_trailer_fields)) => {
138
+ let allowed_set: HashSet<&HeaderName> = allowed_trailer_fields.iter().collect();
139
+
140
+ let mut cur_name = None;
141
+ let mut allowed_trailers = HeaderMap::new();
142
+
143
+ for (opt_name, value) in trailers {
144
+ if let Some(n) = opt_name {
145
+ cur_name = Some(n);
146
+ }
147
+ let name = cur_name.as_ref().expect("current header name");
148
+
149
+ if allowed_set.contains(name) {
150
+ if is_valid_trailer_field(name) {
151
+ allowed_trailers.insert(name, value);
152
+ } else {
153
+ debug!("trailer field is not valid: {}", &name);
154
+ }
155
+ } else {
156
+ debug!("trailer header name not found in trailer header: {}", &name);
157
+ }
158
+ }
159
+
160
+ let mut buf = Vec::new();
161
+ write_headers(&allowed_trailers, &mut buf);
162
+
163
+ if buf.is_empty() {
164
+ return None;
165
+ }
166
+
167
+ Some(EncodedBuf {
168
+ kind: BufKind::Trailers(b"0\r\n".chain(Bytes::from(buf)).chain(b"\r\n")),
169
+ })
170
+ }
171
+ Kind::Chunked(None) => {
172
+ debug!("attempted to encode trailers, but the trailer header is not set");
173
+ None
174
+ }
175
+ _ => {
176
+ debug!("attempted to encode trailers for non-chunked response");
177
+ None
178
+ }
179
+ }
180
+ }
181
+
182
+ pub(super) fn encode_and_end<B>(&self, msg: B, dst: &mut WriteBuf<EncodedBuf<B>>) -> bool
183
+ where
184
+ B: Buf,
185
+ {
186
+ let len = msg.remaining();
187
+ debug_assert!(len > 0, "encode() called with empty buf");
188
+
189
+ match self.kind {
190
+ Kind::Chunked(_) => {
191
+ trace!("encoding chunked {}B", len);
192
+ let buf = ChunkSize::new(len)
193
+ .chain(msg)
194
+ .chain(b"\r\n0\r\n\r\n" as &'static [u8]);
195
+ dst.buffer(buf);
196
+ !self.is_last
197
+ }
198
+ Kind::Length(remaining) => {
199
+ use std::cmp::Ordering;
200
+
201
+ trace!("sized write, len = {}", len);
202
+ match (len as u64).cmp(&remaining) {
203
+ Ordering::Equal => {
204
+ dst.buffer(msg);
205
+ !self.is_last
206
+ }
207
+ Ordering::Greater => {
208
+ dst.buffer(msg.take(remaining as usize));
209
+ !self.is_last
210
+ }
211
+ Ordering::Less => {
212
+ dst.buffer(msg);
213
+ false
214
+ }
215
+ }
216
+ }
217
+ }
218
+ }
219
+ }
220
+
221
+ fn is_valid_trailer_field(name: &HeaderName) -> bool {
222
+ !matches!(
223
+ *name,
224
+ AUTHORIZATION
225
+ | CACHE_CONTROL
226
+ | CONTENT_ENCODING
227
+ | CONTENT_LENGTH
228
+ | CONTENT_RANGE
229
+ | CONTENT_TYPE
230
+ | HOST
231
+ | MAX_FORWARDS
232
+ | SET_COOKIE
233
+ | TRAILER
234
+ | TRANSFER_ENCODING
235
+ | TE
236
+ )
237
+ }
238
+
239
+ impl<B> Buf for EncodedBuf<B>
240
+ where
241
+ B: Buf,
242
+ {
243
+ #[inline]
244
+ fn remaining(&self) -> usize {
245
+ match self.kind {
246
+ BufKind::Exact(ref b) => b.remaining(),
247
+ BufKind::Limited(ref b) => b.remaining(),
248
+ BufKind::Chunked(ref b) => b.remaining(),
249
+ BufKind::ChunkedEnd(ref b) => b.remaining(),
250
+ BufKind::Trailers(ref b) => b.remaining(),
251
+ }
252
+ }
253
+
254
+ #[inline]
255
+ fn chunk(&self) -> &[u8] {
256
+ match self.kind {
257
+ BufKind::Exact(ref b) => b.chunk(),
258
+ BufKind::Limited(ref b) => b.chunk(),
259
+ BufKind::Chunked(ref b) => b.chunk(),
260
+ BufKind::ChunkedEnd(ref b) => b.chunk(),
261
+ BufKind::Trailers(ref b) => b.chunk(),
262
+ }
263
+ }
264
+
265
+ #[inline]
266
+ fn advance(&mut self, cnt: usize) {
267
+ match self.kind {
268
+ BufKind::Exact(ref mut b) => b.advance(cnt),
269
+ BufKind::Limited(ref mut b) => b.advance(cnt),
270
+ BufKind::Chunked(ref mut b) => b.advance(cnt),
271
+ BufKind::ChunkedEnd(ref mut b) => b.advance(cnt),
272
+ BufKind::Trailers(ref mut b) => b.advance(cnt),
273
+ }
274
+ }
275
+
276
+ #[inline]
277
+ fn chunks_vectored<'t>(&'t self, dst: &mut [IoSlice<'t>]) -> usize {
278
+ match self.kind {
279
+ BufKind::Exact(ref b) => b.chunks_vectored(dst),
280
+ BufKind::Limited(ref b) => b.chunks_vectored(dst),
281
+ BufKind::Chunked(ref b) => b.chunks_vectored(dst),
282
+ BufKind::ChunkedEnd(ref b) => b.chunks_vectored(dst),
283
+ BufKind::Trailers(ref b) => b.chunks_vectored(dst),
284
+ }
285
+ }
286
+ }
287
+
288
+ #[cfg(target_pointer_width = "32")]
289
+ const USIZE_BYTES: usize = 4;
290
+
291
+ #[cfg(target_pointer_width = "64")]
292
+ const USIZE_BYTES: usize = 8;
293
+
294
+ // each byte will become 2 hex
295
+ const CHUNK_SIZE_MAX_BYTES: usize = USIZE_BYTES * 2;
296
+
297
+ #[derive(Clone, Copy)]
298
+ struct ChunkSize {
299
+ bytes: [u8; CHUNK_SIZE_MAX_BYTES + 2],
300
+ pos: u8,
301
+ len: u8,
302
+ }
303
+
304
+ impl ChunkSize {
305
+ fn new(len: usize) -> ChunkSize {
306
+ use std::fmt::Write;
307
+ let mut size = ChunkSize {
308
+ bytes: [0; CHUNK_SIZE_MAX_BYTES + 2],
309
+ pos: 0,
310
+ len: 0,
311
+ };
312
+ write!(&mut size, "{len:X}\r\n").expect("CHUNK_SIZE_MAX_BYTES should fit any usize");
313
+ size
314
+ }
315
+ }
316
+
317
+ impl Buf for ChunkSize {
318
+ #[inline]
319
+ fn remaining(&self) -> usize {
320
+ (self.len - self.pos).into()
321
+ }
322
+
323
+ #[inline]
324
+ fn chunk(&self) -> &[u8] {
325
+ &self.bytes[self.pos.into()..self.len.into()]
326
+ }
327
+
328
+ #[inline]
329
+ fn advance(&mut self, cnt: usize) {
330
+ assert!(cnt <= self.remaining());
331
+ self.pos += cnt as u8; // just asserted cnt fits in u8
332
+ }
333
+ }
334
+
335
+ impl fmt::Debug for ChunkSize {
336
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
337
+ f.debug_struct("ChunkSize")
338
+ .field("bytes", &&self.bytes[..self.len.into()])
339
+ .field("pos", &self.pos)
340
+ .finish()
341
+ }
342
+ }
343
+
344
+ impl fmt::Write for ChunkSize {
345
+ fn write_str(&mut self, num: &str) -> fmt::Result {
346
+ use std::io::Write;
347
+ (&mut self.bytes[self.len.into()..])
348
+ .write_all(num.as_bytes())
349
+ .expect("&mut [u8].write() cannot error");
350
+ self.len += num.len() as u8; // safe because bytes is never bigger than 256
351
+ Ok(())
352
+ }
353
+ }
354
+
355
+ impl<B: Buf> From<B> for EncodedBuf<B> {
356
+ fn from(buf: B) -> Self {
357
+ EncodedBuf {
358
+ kind: BufKind::Exact(buf),
359
+ }
360
+ }
361
+ }
362
+
363
+ impl<B: Buf> From<Take<B>> for EncodedBuf<B> {
364
+ fn from(buf: Take<B>) -> Self {
365
+ EncodedBuf {
366
+ kind: BufKind::Limited(buf),
367
+ }
368
+ }
369
+ }
370
+
371
+ impl<B: Buf> From<Chain<Chain<ChunkSize, B>, StaticBuf>> for EncodedBuf<B> {
372
+ fn from(buf: Chain<Chain<ChunkSize, B>, StaticBuf>) -> Self {
373
+ EncodedBuf {
374
+ kind: BufKind::Chunked(buf),
375
+ }
376
+ }
377
+ }
378
+
379
+ impl fmt::Display for NotEof {
380
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
381
+ write!(f, "early end, expected {} more bytes", self.0)
382
+ }
383
+ }
384
+
385
+ impl std::error::Error for NotEof {}
386
+
387
+ #[cfg(test)]
388
+ mod tests {
389
+ use bytes::BufMut;
390
+ use http::{
391
+ HeaderMap, HeaderName, HeaderValue,
392
+ header::{
393
+ AUTHORIZATION, CACHE_CONTROL, CONTENT_ENCODING, CONTENT_LENGTH, CONTENT_RANGE,
394
+ CONTENT_TYPE, HOST, MAX_FORWARDS, SET_COOKIE, TE, TRAILER, TRANSFER_ENCODING,
395
+ },
396
+ };
397
+
398
+ use super::{super::io::Cursor, Encoder};
399
+
400
+ #[test]
401
+ fn chunked() {
402
+ let mut encoder = Encoder::chunked();
403
+ let mut dst = Vec::new();
404
+
405
+ let msg1 = b"foo bar".as_ref();
406
+ let buf1 = encoder.encode(msg1);
407
+ dst.put(buf1);
408
+ assert_eq!(dst, b"7\r\nfoo bar\r\n");
409
+
410
+ let msg2 = b"baz quux herp".as_ref();
411
+ let buf2 = encoder.encode(msg2);
412
+ dst.put(buf2);
413
+
414
+ assert_eq!(dst, b"7\r\nfoo bar\r\nD\r\nbaz quux herp\r\n");
415
+
416
+ let end = encoder.end::<Cursor<Vec<u8>>>().unwrap().unwrap();
417
+ dst.put(end);
418
+
419
+ assert_eq!(
420
+ dst,
421
+ b"7\r\nfoo bar\r\nD\r\nbaz quux herp\r\n0\r\n\r\n".as_ref()
422
+ );
423
+ }
424
+
425
+ #[test]
426
+ fn length() {
427
+ let max_len = 8;
428
+ let mut encoder = Encoder::length(max_len as u64);
429
+ let mut dst = Vec::new();
430
+
431
+ let msg1 = b"foo bar".as_ref();
432
+ let buf1 = encoder.encode(msg1);
433
+ dst.put(buf1);
434
+
435
+ assert_eq!(dst, b"foo bar");
436
+ assert!(!encoder.is_eof());
437
+ encoder.end::<()>().unwrap_err();
438
+
439
+ let msg2 = b"baz".as_ref();
440
+ let buf2 = encoder.encode(msg2);
441
+ dst.put(buf2);
442
+
443
+ assert_eq!(dst.len(), max_len);
444
+ assert_eq!(dst, b"foo barb");
445
+ assert!(encoder.is_eof());
446
+ assert!(encoder.end::<()>().unwrap().is_none());
447
+ }
448
+
449
+ #[test]
450
+ fn chunked_with_valid_trailers() {
451
+ let encoder = Encoder::chunked();
452
+ let trailers = vec![HeaderName::from_static("chunky-trailer")];
453
+ let encoder = encoder.into_chunked_with_trailing_fields(trailers);
454
+
455
+ let headers = HeaderMap::from_iter(vec![
456
+ (
457
+ HeaderName::from_static("chunky-trailer"),
458
+ HeaderValue::from_static("header data"),
459
+ ),
460
+ (
461
+ HeaderName::from_static("should-not-be-included"),
462
+ HeaderValue::from_static("oops"),
463
+ ),
464
+ ]);
465
+
466
+ let buf1 = encoder.encode_trailers::<&[u8]>(headers).unwrap();
467
+
468
+ let mut dst = Vec::new();
469
+ dst.put(buf1);
470
+ assert_eq!(dst, b"0\r\nchunky-trailer: header data\r\n\r\n");
471
+ }
472
+
473
+ #[test]
474
+ fn chunked_with_multiple_trailer_headers() {
475
+ let encoder = Encoder::chunked();
476
+ let trailers = vec![
477
+ HeaderName::from_static("chunky-trailer"),
478
+ HeaderName::from_static("chunky-trailer-2"),
479
+ ];
480
+ let encoder = encoder.into_chunked_with_trailing_fields(trailers);
481
+
482
+ let headers = HeaderMap::from_iter(vec![
483
+ (
484
+ HeaderName::from_static("chunky-trailer"),
485
+ HeaderValue::from_static("header data"),
486
+ ),
487
+ (
488
+ HeaderName::from_static("chunky-trailer-2"),
489
+ HeaderValue::from_static("more header data"),
490
+ ),
491
+ ]);
492
+
493
+ let buf1 = encoder.encode_trailers::<&[u8]>(headers).unwrap();
494
+
495
+ let mut dst = Vec::new();
496
+ dst.put(buf1);
497
+ assert_eq!(
498
+ dst,
499
+ b"0\r\nchunky-trailer: header data\r\nchunky-trailer-2: more header data\r\n\r\n"
500
+ );
501
+ }
502
+
503
+ #[test]
504
+ fn chunked_with_no_trailer_header() {
505
+ let encoder = Encoder::chunked();
506
+
507
+ let headers = HeaderMap::from_iter(vec![(
508
+ HeaderName::from_static("chunky-trailer"),
509
+ HeaderValue::from_static("header data"),
510
+ )]);
511
+
512
+ assert!(encoder.encode_trailers::<&[u8]>(headers.clone()).is_none());
513
+
514
+ let trailers = vec![];
515
+ let encoder = encoder.into_chunked_with_trailing_fields(trailers);
516
+
517
+ assert!(encoder.encode_trailers::<&[u8]>(headers).is_none());
518
+ }
519
+
520
+ #[test]
521
+ fn chunked_with_invalid_trailers() {
522
+ let encoder = Encoder::chunked();
523
+
524
+ let trailers = vec![
525
+ AUTHORIZATION,
526
+ CACHE_CONTROL,
527
+ CONTENT_ENCODING,
528
+ TRAILER,
529
+ TRANSFER_ENCODING,
530
+ TE,
531
+ ];
532
+ let encoder = encoder.into_chunked_with_trailing_fields(trailers);
533
+
534
+ let mut headers = HeaderMap::new();
535
+ headers.insert(AUTHORIZATION, HeaderValue::from_static("header data"));
536
+ headers.insert(CACHE_CONTROL, HeaderValue::from_static("header data"));
537
+ headers.insert(CONTENT_ENCODING, HeaderValue::from_static("header data"));
538
+ headers.insert(CONTENT_LENGTH, HeaderValue::from_static("header data"));
539
+ headers.insert(CONTENT_RANGE, HeaderValue::from_static("header data"));
540
+ headers.insert(CONTENT_TYPE, HeaderValue::from_static("header data"));
541
+ headers.insert(HOST, HeaderValue::from_static("header data"));
542
+ headers.insert(MAX_FORWARDS, HeaderValue::from_static("header data"));
543
+ headers.insert(SET_COOKIE, HeaderValue::from_static("header data"));
544
+ headers.insert(TRAILER, HeaderValue::from_static("header data"));
545
+ headers.insert(TRANSFER_ENCODING, HeaderValue::from_static("header data"));
546
+ headers.insert(TE, HeaderValue::from_static("header data"));
547
+
548
+ assert!(encoder.encode_trailers::<&[u8]>(headers).is_none());
549
+ }
550
+
551
+ #[test]
552
+ fn chunked_trailers_case_insensitive_matching() {
553
+ // Regression test for issue #4010: HTTP/1.1 trailers are case-sensitive
554
+ //
555
+ // Previously, the Trailer header values were stored as HeaderValue (preserving case)
556
+ // and compared against HeaderName (which is always lowercase). This caused trailers
557
+ // declared as "Chunky-Trailer" to not match actual trailers sent as "chunky-trailer".
558
+ //
559
+ // The fix converts Trailer header values to HeaderName during parsing, which
560
+ // normalizes the case and enables proper case-insensitive matching.
561
+ //
562
+ // Note: HeaderName::from_static() requires lowercase input. In real usage,
563
+ // HeaderName::from_bytes() is used to parse the Trailer header value, which
564
+ // normalizes mixed-case input like "Chunky-Trailer" to "chunky-trailer".
565
+ let encoder = Encoder::chunked();
566
+ let trailers = vec![HeaderName::from_static("chunky-trailer")];
567
+ let encoder = encoder.into_chunked_with_trailing_fields(trailers);
568
+
569
+ // The actual trailer being sent
570
+ let headers = HeaderMap::from_iter(vec![(
571
+ HeaderName::from_static("chunky-trailer"),
572
+ HeaderValue::from_static("trailer value"),
573
+ )]);
574
+
575
+ let buf = encoder.encode_trailers::<&[u8]>(headers).unwrap();
576
+ let mut dst = Vec::new();
577
+ dst.put(buf);
578
+ assert_eq!(dst, b"0\r\nchunky-trailer: trailer value\r\n\r\n");
579
+ }
580
+ }