1use bytes::{Bytes, BytesMut};
27
28pub use httparse::{Header, Response};
29
30use crate::error::WireError;
31use crate::util::{chunked_body_len, is_chunked_slice, version_to_str};
32use crate::{WireDecode, WireEncode};
33
34impl<B> WireEncode for http::Response<B>
35where
36 B: http_body_util::BodyExt,
37 B::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
38{
39 fn encode(self) -> Result<Bytes, WireError> {
40 let version = self.version();
41 if version != http::Version::HTTP_11 && version != http::Version::HTTP_10 {
42 return Err(WireError::UnsupportedVersion);
43 }
44
45 let (parts, body) = self.into_parts();
46 let status = parts.status;
47 let reason = status.canonical_reason().unwrap_or("Unknown");
48
49 let body_bytes = futures::executor::block_on(body.collect())
53 .map_err(|e| WireError::Collection(e.into()))?
54 .to_bytes();
55
56 let mut buf = BytesMut::with_capacity(
61 parts.headers.len() * 48 + body_bytes.len() + 16,
62 );
63
64 buf.extend_from_slice(version_to_str(version).as_bytes());
66 buf.extend_from_slice(b" ");
67 buf.extend_from_slice(status.as_str().as_bytes());
68 buf.extend_from_slice(b" ");
69 buf.extend_from_slice(reason.as_bytes());
70 buf.extend_from_slice(b"\r\n");
71
72 for (name, value) in &parts.headers {
74 buf.extend_from_slice(name.as_str().as_bytes());
75 buf.extend_from_slice(b": ");
76 buf.extend_from_slice(value.as_bytes());
77 buf.extend_from_slice(b"\r\n");
78 }
79
80 buf.extend_from_slice(b"\r\n");
82
83 buf.extend_from_slice(&body_bytes);
85
86 Ok(buf.freeze())
87 }
88}
89
90pub struct FullResponse<'headers, 'buf> {
111 pub head: httparse::Response<'headers, 'buf>,
117 pub body: &'buf [u8],
123}
124
125impl<'headers, 'buf> FullResponse<'headers, 'buf> {
126 pub fn parse(&mut self, buf: &'buf [u8]) -> Result<usize, WireError> {
148 match self.head.parse(buf) {
149 Ok(httparse::Status::Complete(headers_len)) => {
150 let code = self.head.code.unwrap_or(200);
151
152 if code == 204 || code == 304 || (100..200).contains(&code) {
154 self.body = &[];
155 return Ok(headers_len);
156 }
157
158 let mut content_len: Option<usize> = None;
159 let mut is_chunked = false;
160
161 for header in self.head.headers.iter() {
163 let name = header.name.as_bytes();
164 if name.len() == 14 && name.eq_ignore_ascii_case(b"Content-Length") {
165 content_len = std::str::from_utf8(header.value)
166 .ok()
167 .and_then(|s| s.parse().ok());
168 } else if name.len() == 17 && name.eq_ignore_ascii_case(b"Transfer-Encoding") {
169 is_chunked = is_chunked_slice(header.value);
170 }
171 }
172
173 if is_chunked {
175 let body_len = chunked_body_len(&buf[headers_len..])
176 .ok_or(WireError::InvalidChunkedBody)?;
177 self.body = &buf[headers_len..headers_len + body_len];
178 Ok(headers_len + body_len)
179 } else {
180 let body_len = content_len.unwrap_or(0);
182 let total = headers_len + body_len;
183 if buf.len() >= total {
184 self.body = &buf[headers_len..total];
185 Ok(total)
186 } else {
187 Err(WireError::IncompleteBody(total - buf.len()))
188 }
189 }
190 }
191 Ok(httparse::Status::Partial) => Err(WireError::PartialHead),
192 Err(err) => Err(err.into()),
193 }
194 }
195}
196
197impl<'headers, 'buf> WireDecode<'headers, 'buf> for FullResponse<'headers, 'buf> {
198 fn decode(
199 buf: &'buf [u8],
200 headers: &'headers mut [Header<'buf>],
201 ) -> Result<(Self, usize), WireError> {
202 let mut full_response = FullResponse {
203 head: httparse::Response::new(headers),
204 body: &[],
205 };
206
207 let total = full_response.parse(buf)?;
208 Ok((full_response, total))
209 }
210
211 }
215
216#[cfg(test)]
217mod tests {
218 use super::*;
219 use bytes::Bytes;
220 use http_body_util::{Empty, Full};
221
222 #[test]
225 fn test_response_encode_200() {
226 let response = http::Response::builder()
227 .status(200)
228 .header("Content-Type", "text/plain")
229 .body(Full::new(Bytes::from("Hello")))
230 .unwrap();
231
232 let bytes = response.encode().unwrap();
233 let output = String::from_utf8_lossy(&bytes);
234
235 assert!(output.starts_with("HTTP/1.1 200 OK\r\n"));
236 assert!(output.contains("content-type: text/plain\r\n"));
237 assert!(output.contains("\r\n\r\n"));
238 assert!(output.ends_with("Hello"));
239 }
240
241 #[test]
242 fn test_response_encode_404() {
243 let response = http::Response::builder()
244 .status(404)
245 .body(Full::new(Bytes::from("Not Found")))
246 .unwrap();
247
248 let bytes = response.encode().unwrap();
249 let output = String::from_utf8_lossy(&bytes);
250
251 assert!(output.starts_with("HTTP/1.1 404 Not Found\r\n"));
252 assert!(output.ends_with("Not Found"));
253 }
254
255 #[test]
256 fn test_response_encode_no_body() {
257 let response = http::Response::builder()
258 .status(204)
259 .header("Server", "http_wire")
260 .body(Empty::<Bytes>::new())
261 .unwrap();
262
263 let bytes = response.encode().unwrap();
264 let output = String::from_utf8_lossy(&bytes);
265
266 assert!(output.starts_with("HTTP/1.1 204 No Content\r\n"));
267 assert!(output.contains("server: http_wire\r\n"));
268 let parts: Vec<&str> = output.splitn(2, "\r\n\r\n").collect();
270 assert_eq!(parts.len(), 2);
271 assert!(parts[1].is_empty());
272 }
273
274 #[test]
275 fn test_response_encode_http10() {
276 let response = http::Response::builder()
277 .status(200)
278 .version(http::Version::HTTP_10)
279 .body(Empty::<Bytes>::new())
280 .unwrap();
281
282 let bytes = response.encode().unwrap();
283 let output = String::from_utf8_lossy(&bytes);
284
285 assert!(output.starts_with("HTTP/1.0 200 OK\r\n"));
286 }
287
288 #[test]
289 fn test_response_encode_http2_rejected() {
290 let response = http::Response::builder()
291 .status(200)
292 .version(http::Version::HTTP_2)
293 .body(Full::new(Bytes::from("Hello")))
294 .unwrap();
295
296 let result = response.encode();
297 assert!(matches!(result, Err(WireError::UnsupportedVersion)));
298 }
299
300 #[test]
301 fn test_response_encode_header_body_separator() {
302 let body = "Hello World";
303 let response = http::Response::builder()
304 .status(200)
305 .header("Content-Type", "text/plain")
306 .body(Full::new(Bytes::from(body)))
307 .unwrap();
308
309 let bytes = response.encode().unwrap();
310 let output = String::from_utf8_lossy(&bytes);
311
312 assert!(output.contains("\r\n\r\n"));
314 let parts: Vec<&str> = output.splitn(2, "\r\n\r\n").collect();
315 assert_eq!(parts.len(), 2, "response must have a headers section and a body section");
316 assert_eq!(parts[1], body, "body must appear verbatim after the separator");
317 }
318
319 #[test]
320 fn test_response_encode_multiple_headers() {
321 let response = http::Response::builder()
322 .status(200)
323 .header("Content-Type", "application/json")
324 .header("X-Request-Id", "abc-123")
325 .header("Cache-Control", "no-cache")
326 .body(Empty::<Bytes>::new())
327 .unwrap();
328
329 let bytes = response.encode().unwrap();
330 let output = String::from_utf8_lossy(&bytes);
331
332 assert!(output.contains("content-type: application/json\r\n"));
333 assert!(output.contains("x-request-id: abc-123\r\n"));
334 assert!(output.contains("cache-control: no-cache\r\n"));
335 }
336
337 #[test]
340 fn test_decode_response_no_body() {
341 let raw = b"HTTP/1.1 204 No Content\r\nServer: test\r\n\r\n";
342 let mut headers = [httparse::EMPTY_HEADER; 16];
343 let result = FullResponse::decode(raw, &mut headers);
344 assert!(result.is_ok());
345 let (response, len) = result.unwrap();
346 assert_eq!(response.head.code, Some(204));
347 assert_eq!(len, raw.len());
348 assert_eq!(response.body.len(), 0);
349 }
350
351 #[test]
352 fn test_decode_response_with_content_length() {
353 let raw = b"HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nhello";
354 let mut headers = [httparse::EMPTY_HEADER; 16];
355 let result = FullResponse::decode(raw, &mut headers);
356 assert!(result.is_ok());
357 let (response, len) = result.unwrap();
358 assert_eq!(response.head.code, Some(200));
359 assert_eq!(len, raw.len());
360 assert_eq!(response.body, b"hello");
361 }
362
363 #[test]
364 fn test_decode_response_incomplete_body() {
365 let raw = b"HTTP/1.1 200 OK\r\nContent-Length: 10\r\n\r\nhello";
367 let mut headers = [httparse::EMPTY_HEADER; 16];
368 let result = FullResponse::decode(raw, &mut headers);
369 assert!(matches!(result, Err(WireError::IncompleteBody(_))));
370 }
371
372 #[test]
373 fn test_decode_response_incomplete_headers() {
374 let raw = b"HTTP/1.1 200 OK\r\nContent-Length: 5\r\n";
375 let mut headers = [httparse::EMPTY_HEADER; 16];
376 let result = FullResponse::decode(raw, &mut headers);
377 assert!(matches!(result, Err(WireError::PartialHead)));
378 }
379
380 #[test]
381 fn test_decode_response_chunked_encoding() {
382 let raw = b"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nhello\r\n0\r\n\r\n";
383 let mut headers = [httparse::EMPTY_HEADER; 16];
384 let result = FullResponse::decode(raw, &mut headers);
385 assert!(result.is_ok());
386 let (response, len) = result.unwrap();
387 assert_eq!(response.head.code, Some(200));
388 assert_eq!(len, raw.len());
389 }
390
391 #[test]
392 fn test_decode_response_chunked_multiple_chunks() {
393 let raw = b"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nhello\r\n6\r\n world\r\n0\r\n\r\n";
394 let mut headers = [httparse::EMPTY_HEADER; 16];
395 let result = FullResponse::decode(raw, &mut headers);
396 assert!(result.is_ok());
397 let (response, len) = result.unwrap();
398 assert_eq!(response.head.code, Some(200));
399 assert_eq!(len, raw.len());
400 }
401
402 #[test]
403 fn test_decode_response_chunked_incomplete() {
404 let raw = b"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nhello\r\n";
406 let mut headers = [httparse::EMPTY_HEADER; 16];
407 let result = FullResponse::decode(raw, &mut headers);
408 assert!(matches!(result, Err(WireError::InvalidChunkedBody)));
409 }
410
411 #[test]
412 fn test_decode_response_extra_data_after() {
413 let response = b"HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nhello";
415 let mut raw = response.to_vec();
416 raw.extend_from_slice(b"extra garbage data");
417 let mut headers = [httparse::EMPTY_HEADER; 16];
418 let (_, len) = FullResponse::decode(&raw, &mut headers).unwrap();
419 assert_eq!(len, response.len());
420 }
421
422 #[test]
423 fn test_decode_response_chunked_case_insensitive() {
424 let raw = b"HTTP/1.1 200 OK\r\nTransfer-Encoding: CHUNKED\r\n\r\n5\r\nhello\r\n0\r\n\r\n";
425 let mut headers = [httparse::EMPTY_HEADER; 16];
426 let result = FullResponse::decode(raw, &mut headers);
427 assert!(result.is_ok());
428 let (response, len) = result.unwrap();
429 assert_eq!(response.head.code, Some(200));
430 assert_eq!(len, raw.len());
431 }
432
433 #[test]
434 fn test_decode_response_304_no_body() {
435 let raw = b"HTTP/1.1 304 Not Modified\r\nETag: \"abc\"\r\n\r\n";
437 let mut headers = [httparse::EMPTY_HEADER; 16];
438 let result = FullResponse::decode(raw, &mut headers);
439 assert!(result.is_ok());
440 let (response, len) = result.unwrap();
441 assert_eq!(response.head.code, Some(304));
442 assert_eq!(len, raw.len());
443 assert_eq!(response.body.len(), 0);
444 }
445
446 #[test]
447 fn test_decode_response_1xx_no_body() {
448 let raw = b"HTTP/1.1 100 Continue\r\n\r\n";
450 let mut headers = [httparse::EMPTY_HEADER; 16];
451 let result = FullResponse::decode(raw, &mut headers);
452 assert!(result.is_ok());
453 let (response, len) = result.unwrap();
454 assert_eq!(response.head.code, Some(100));
455 assert_eq!(len, raw.len());
456 assert_eq!(response.body.len(), 0);
457 }
458
459 #[test]
460 fn test_decode_response_fields_access() {
461 let raw =
462 b"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: 11\r\n\r\nHello World";
463 let mut headers = [httparse::EMPTY_HEADER; 16];
464 let (response, total_len) = FullResponse::decode(raw, &mut headers).unwrap();
465
466 assert_eq!(response.head.code, Some(200));
467 assert_eq!(response.head.reason, Some("OK"));
468 assert_eq!(response.head.version, Some(1));
469
470 assert_eq!(response.head.headers.len(), 2);
471 assert_eq!(response.head.headers[0].name, "Content-Type");
472 assert_eq!(response.head.headers[0].value, b"text/plain");
473 assert_eq!(response.head.headers[1].name, "Content-Length");
474 assert_eq!(response.head.headers[1].value, b"11");
475
476 assert_eq!(response.body, b"Hello World");
477 assert_eq!(total_len, raw.len());
478 }
479
480 #[test]
481 #[should_panic(
482 expected = "decode_uninit is not available for this type due to missing parse_with_uninit_headers method"
483 )]
484 fn test_decode_response_uninit_panics() {
485 let raw = b"HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nhello";
486 let mut headers = [const { std::mem::MaybeUninit::uninit() }; 16];
487 let _result = FullResponse::decode_uninit(raw, &mut headers);
488 }
489}