ureq_proto/server/
mod.rs

1//! HTTP/1.1 server protocol
2//!
3//! Sans-IO protocol impl, which means "writing" and "reading" are made via buffers
4//! rather than the Write/Read std traits.
5//!
6//! The [`Reply`] object attempts to encode correct HTTP/1.1 handling using
7//! state variables, for example `Reply<RecvRequest>` to represent the
8//! lifecycle stage where we are to receive a request.
9//!
10//! The states are:
11//!
12//! * **RecvRequest** - Receive the request, which is the method, path,
13//!   version and the request headers
14//! * **Send100** - If there is an `Expect: 100-continue` header, the
15//!   server should send a 100 Continue response before receiving the body
16//! * **RecvBody** - Receive the request body
17//! * **ProvideResponse** - Prepare a response to the request
18//! * **SendResponse** - Send the response status and headers
19//! * **SendBody** - Send the response body
20//! * **Cleanup** - Close the connection or prepare for the next request
21//!
22//! ```text
23//!        ┌──────────────────┐
24//!     ┌──│   RecvRequest    │───────────────┐
25//!     │  └──────────────────┘               │
26//!     │            │                        │
27//!     │            │                        │
28//!     │            ▼                        ▼
29//!     │  ┌──────────────────┐     ┌──────────────────┐
30//!     │  │     RecvBody     │◀────│     Send100      │
31//!     │  └──────────────────┘     └──────────────────┘
32//!     │            │                        │
33//!     │            │                        │
34//!     │            ▼                        │
35//!     └─▶┌──────────────────┐               │
36//!        │  ProvideResponse │             reject
37//!        └──────────────────┘               │
38//!                  │                        │
39//!                  │                        │
40//!                  ▼                        │
41//!        ┌──────────────────┐◀──────────────┘
42//!        │   SendResponse   │──┐
43//!        └──────────────────┘  │
44//!                  │           │
45//!                  │           │
46//!                  ▼           │
47//!        ┌──────────────────┐  │
48//!        │     SendBody     │  │
49//!        └──────────────────┘  │
50//!                  │           │
51//!                  │           │
52//!                  ▼           │
53//!        ┌──────────────────┐  │
54//!        │     Cleanup      │◀─┘
55//!        └──────────────────┘
56//! ```
57//!
58//! # Example
59//!
60//! ```
61//! use ureq_proto::server::*;
62//! use http::{Response, StatusCode, Version};
63//!
64//! // ********************************** RecvRequest
65//!
66//! // Create a new Reply in the RecvRequest state
67//! let mut reply = Reply::new().unwrap();
68//!
69//! // Receive a request from the client
70//! let input = b"POST /my-path HTTP/1.1\r\n\
71//!     host: example.test\r\n\
72//!     transfer-encoding: chunked\r\n\
73//!     expect: 100-continue\r\n\
74//!     \r\n";
75//! let (input_used, request) = reply.try_request(input).unwrap();
76//!
77//! assert_eq!(input_used, 96);
78//! let request = request.unwrap();
79//! assert_eq!(request.uri().path(), "/my-path");
80//! assert_eq!(request.method(), "POST");
81//!
82//! // Check if we can proceed to the next state
83//! // In a real server, you would implement this method
84//! // let can_proceed = reply.can_proceed();
85//!
86//! // Proceed to the next state
87//! let reply = reply.proceed().unwrap();
88//!
89//! // ********************************** Send100
90//!
91//! // In this example, we know the next state is Send100 because
92//! // the request included an "Expect: 100-continue" header.
93//! // A real server needs to match on the variants.
94//! let reply = match reply {
95//!     RecvRequestResult::Send100(v) => v,
96//!     _ => panic!(),
97//! };
98//!
99//! // We can either accept or reject the 100-continue request
100//! // Here we accept it and proceed to receiving the body
101//! let mut output = vec![0_u8; 1024];
102//! let (output_used, reply) = reply.accept(&mut output).unwrap();
103//!
104//! assert_eq!(output_used, 25);
105//! assert_eq!(&output[..output_used], b"HTTP/1.1 100 Continue\r\n\r\n");
106//!
107//! // ********************************** RecvBody
108//!
109//! // Now we can receive the request body
110//! let mut reply = reply;
111//!
112//! // Receive the body in chunks (chunked encoding format)
113//! let input = b"5\r\nhello\r\n0\r\n\r\n";
114//! let mut body_buffer = vec![0_u8; 1024];
115//! let (input_used, output_used) = reply.read(input, &mut body_buffer).unwrap();
116//!
117//! assert_eq!(input_used, 15);
118//! assert_eq!(output_used, 5);
119//! assert_eq!(&body_buffer[..output_used], b"hello");
120//!
121//! // Check if the body is fully received
122//! // In this example, we'll assume it is
123//! assert!(reply.is_ended());
124//!
125//! // Proceed to providing a response
126//! let reply = reply.proceed().unwrap();
127//!
128//! // ********************************** ProvideResponse
129//!
130//! // Create a response
131//! let response = Response::builder()
132//!     .status(StatusCode::OK)
133//!     .header("content-type", "text/plain")
134//!     .body(())
135//!     .unwrap();
136//!
137//! // Provide the response and proceed to sending it
138//! let mut reply = reply.provide(response).unwrap();
139//!
140//! // ********************************** SendResponse
141//!
142//! // Send the response headers
143//! let output_used = reply.write(&mut output).unwrap();
144//!
145//! assert_eq!(&output[..output_used], b"\
146//!     HTTP/1.1 200 OK\r\n\
147//!     transfer-encoding: chunked\r\n\
148//!     content-type: text/plain\r\n\
149//!     \r\n");
150//!
151//! // Check if the response headers are fully sent
152//! assert!(reply.is_finished());
153//!
154//! // Proceed to sending the response body
155//! let SendResponseResult::SendBody(mut reply) = reply.proceed() else {
156//!     panic!("Expected SendBody");
157//! };
158//!
159//! // ********************************** SendBody
160//!
161//! // Send the response body
162//! let (input_used, output_used) = reply.write(b"hello world", &mut output).unwrap();
163//!
164//! assert_eq!(input_used, 11);
165//! assert_eq!(&output[..output_used], b"b\r\nhello world\r\n");
166//!
167//! // Indicate the end of the body with an empty input
168//! let (input_used, output_used) = reply.write(&[], &mut output).unwrap();
169//!
170//! assert_eq!(input_used, 0);
171//! assert_eq!(&output[..output_used], b"0\r\n\r\n");
172//!
173//! // Check if the body is fully sent
174//! assert!(reply.is_finished());
175//!
176//! // ********************************** Cleanup
177//!
178//! // Proceed to cleanup
179//! let reply = reply.proceed();
180//!
181//! // Check if we need to close the connection
182//! if reply.must_close_connection() {
183//!     // connection.close();
184//! } else {
185//!     // Prepare for the next request
186//!     // let new_reply = Reply::new().unwrap();
187//! }
188//! ```
189
190use std::fmt;
191use std::io::Write;
192use std::marker::PhantomData;
193
194use amended::AmendedResponse;
195use http::{Method, Response, StatusCode, Version};
196
197use crate::body::{BodyReader, BodyWriter};
198use crate::ext::{MethodExt, StatusCodeExt};
199use crate::util::Writer;
200use crate::{ArrayVec, CloseReason};
201
202mod amended;
203
204#[cfg(test)]
205mod test;
206
207/// Maximum number of headers to parse from an HTTP request.
208///
209/// This constant defines the upper limit on the number of headers that can be
210/// parsed from an incoming HTTP request. Requests with more headers than this
211/// will be rejected.
212pub const MAX_REQUEST_HEADERS: usize = 128;
213
214/// A state machine for an HTTP request/response cycle.
215///
216/// This type represents a state machine that transitions through various
217/// states during the lifecycle of an HTTP request/response.
218///
219/// The type parameters are:
220/// - `State`: The current state of the state machine (e.g., `RecvRequest`, `SendResponse`, etc.)
221/// - `B`: The type of the response body (defaults to `()`)
222///
223/// See the [state graph][crate::server] in the server module documentation for a
224/// visual representation of the state transitions.
225pub struct Reply<State> {
226    inner: Inner,
227    _ph: PhantomData<State>,
228}
229
230// pub(crate) for tests to inspect state
231#[derive(Debug)]
232pub(crate) struct Inner {
233    pub phase: ResponsePhase,
234    pub state: BodyState,
235    pub response: Option<AmendedResponse>,
236    pub close_reason: ArrayVec<CloseReason, 4>,
237    pub force_recv_body: bool,
238    pub force_send_body: bool,
239    pub method: Option<Method>,
240    pub expect_100: bool,
241    pub expect_100_reject: bool,
242}
243
244#[derive(Clone, Copy, PartialEq, Eq)]
245pub(crate) enum ResponsePhase {
246    Status,
247    Headers(usize),
248    Body,
249}
250
251impl ResponsePhase {
252    fn is_prelude(&self) -> bool {
253        matches!(self, ResponsePhase::Status | ResponsePhase::Headers(_))
254    }
255
256    fn is_body(&self) -> bool {
257        matches!(self, ResponsePhase::Body)
258    }
259}
260
261#[derive(Debug, Default)]
262pub(crate) struct BodyState {
263    reader: Option<BodyReader>,
264    writer: Option<BodyWriter>,
265    stop_on_chunk_boundary: bool,
266}
267
268#[doc(hidden)]
269pub mod state {
270    pub(crate) trait Named {
271        fn name() -> &'static str;
272    }
273
274    macro_rules! reply_state {
275        ($n:tt) => {
276            #[doc(hidden)]
277            pub struct $n(());
278            impl Named for $n {
279                fn name() -> &'static str {
280                    stringify!($n)
281                }
282            }
283        };
284    }
285
286    reply_state!(RecvRequest);
287    reply_state!(Send100);
288    reply_state!(RecvBody);
289    reply_state!(ProvideResponse);
290    reply_state!(SendResponse);
291    reply_state!(SendBody);
292    reply_state!(Cleanup);
293}
294use self::state::*;
295
296impl<S> Reply<S> {
297    fn wrap(inner: Inner) -> Reply<S>
298    where
299        S: Named,
300    {
301        let wrapped = Reply {
302            inner,
303            _ph: PhantomData,
304        };
305
306        debug!("{:?}", wrapped);
307
308        wrapped
309    }
310
311    #[cfg(test)]
312    pub(crate) fn inner(&self) -> &Inner {
313        &self.inner
314    }
315}
316
317// //////////////////////////////////////////////////////////////////////////////////////////// RECV REQUEST
318
319mod recvreq;
320
321/// The possible states after receiving a request.
322///
323/// See [state graph][crate::server]
324pub enum RecvRequestResult {
325    /// Client is expecting a 100-continue response.
326    Send100(Reply<Send100>),
327    /// Receive a request body.
328    RecvBody(Reply<RecvBody>),
329    /// Client did not send a body.
330    ProvideResponse(Reply<ProvideResponse>),
331}
332
333// //////////////////////////////////////////////////////////////////////////////////////////// SEND 100
334
335mod send100;
336
337/// Internal function to append a response to an existing inner state.
338///
339/// This function is used when transitioning from a state that has received a request
340/// to a state that will send a response.
341fn append_request(inner: Inner, response: Response<()>) -> Inner {
342    // unwrap is ok because method is set early.
343    let method_allows_body = inner.method.as_ref().unwrap().allow_request_body();
344    let status_allows_body = response.status().body_allowed();
345
346    let default_body_mode = if method_allows_body && status_allows_body {
347        BodyWriter::new_chunked()
348    } else {
349        BodyWriter::new_none()
350    };
351
352    Inner {
353        phase: inner.phase,
354        state: BodyState {
355            writer: Some(default_body_mode),
356            ..inner.state
357        },
358        response: Some(AmendedResponse::new(response)),
359        force_recv_body: inner.force_recv_body,
360        force_send_body: inner.force_send_body,
361        close_reason: inner.close_reason,
362        method: inner.method,
363        expect_100: inner.expect_100,
364        expect_100_reject: inner.expect_100_reject,
365    }
366}
367
368/// Internal function to write a status line to a writer.
369///
370/// This function is used when sending a response status line.
371fn do_write_send_line(line: (Version, StatusCode), w: &mut Writer, end_head: bool) -> bool {
372    w.try_write(|w| {
373        write!(
374            w,
375            "{:?} {} {}\r\n{}",
376            line.0,
377            line.1.as_str(),
378            line.1.canonical_reason().unwrap_or("Unknown"),
379            if end_head { "\r\n" } else { "" }
380        )
381    })
382}
383
384// //////////////////////////////////////////////////////////////////////////////////////////// RECV BODY
385
386mod provres;
387
388// //////////////////////////////////////////////////////////////////////////////////////////// RECV BODY
389
390mod recvbody;
391
392// //////////////////////////////////////////////////////////////////////////////////////////// SEND RESPONSE
393
394mod sendres;
395
396/// The possible states after sending a response.
397///
398/// After sending the response headers, the reply can transition to one of two states:
399/// - `SendBody`: If the response has a body that needs to be sent
400/// - `Cleanup`: If the response has no body (e.g., HEAD requests, 204 responses)
401///
402/// See the [state graph][crate::server] for a visual representation.
403pub enum SendResponseResult {
404    /// Send the response body.
405    SendBody(Reply<SendBody>),
406    /// Proceed directly to cleanup without sending a body.
407    Cleanup(Reply<Cleanup>),
408}
409
410// //////////////////////////////////////////////////////////////////////////////////////////// SEND RESPONSE
411
412mod sendbody;
413
414// //////////////////////////////////////////////////////////////////////////////////////////// CLEANUP
415
416impl Reply<Cleanup> {
417    /// Tell if we must close the connection.
418    pub fn must_close_connection(&self) -> bool {
419        self.close_reason().is_some()
420    }
421
422    /// If we are closing the connection, give a reason.
423    pub fn close_reason(&self) -> Option<&'static str> {
424        self.inner.close_reason.first().map(|s| s.explain())
425    }
426}
427
428// ////////////////////////////////////////////////////////////////////////////////////////////
429
430impl<State: Named> fmt::Debug for Reply<State> {
431    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
432        write!(f, "Reply<{}>", State::name())
433    }
434}
435
436impl fmt::Debug for ResponsePhase {
437    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
438        match self {
439            ResponsePhase::Status => write!(f, "SendStatus"),
440            ResponsePhase::Headers(_) => write!(f, "SendHeaders"),
441            ResponsePhase::Body => write!(f, "SendBody"),
442        }
443    }
444}
445
446#[cfg(test)]
447mod tests {
448    use super::*;
449    use http::{Response, StatusCode};
450    use std::str;
451
452    #[test]
453    fn get_simple() {
454        // Create a new Reply in RecvRequest state
455        let mut reply = Reply::new().unwrap();
456
457        // Simulate receiving a GET request
458        let input = b"GET /page HTTP/1.1\r\n\
459            host: test.local\r\n\
460            \r\n";
461        let (input_used, request) = reply.try_request(input).unwrap();
462        let request = request.unwrap();
463
464        assert_eq!(input_used, 40);
465        assert_eq!(request.method(), "GET");
466        assert_eq!(request.uri().path(), "/page");
467
468        // Since GET has no body, we should go straight to ProvideResponse
469        let reply = reply.proceed().unwrap();
470        let RecvRequestResult::ProvideResponse(reply) = reply else {
471            panic!("Expected ProvideResponse state");
472        };
473
474        // Create and provide a response
475        let response = Response::builder()
476            .status(StatusCode::OK)
477            .header("content-type", "text/plain")
478            .body(())
479            .unwrap();
480
481        let mut reply = reply.provide(response).unwrap();
482
483        // Write response headers
484        let mut output = vec![0_u8; 1024];
485        let n = reply.write(&mut output).unwrap();
486
487        let s = str::from_utf8(&output[..n]).unwrap();
488        assert_eq!(
489            s,
490            "HTTP/1.1 200 OK\r\n\
491            transfer-encoding: chunked\r\n\
492            content-type: text/plain\r\n\
493            \r\n"
494        );
495    }
496
497    #[test]
498    fn post_with_100_continue() {
499        // Create a new Reply
500        let mut reply = Reply::new().unwrap();
501
502        // Receive POST request with Expect: 100-continue
503        let input = b"POST /upload HTTP/1.1\r\n\
504            host: test.local\r\n\
505            expect: 100-continue\r\n\
506            transfer-encoding: chunked\r\n\
507            \r\n";
508        let (input_used, request) = reply.try_request(input).unwrap();
509        let request = request.unwrap();
510
511        assert_eq!(input_used, 93); // Verify exact bytes consumed
512        assert_eq!(request.method(), "POST");
513        assert_eq!(request.uri().path(), "/upload");
514        assert_eq!(request.headers().get("expect").unwrap(), "100-continue");
515
516        // Proceed to Send100 state and handle the state transition
517        let reply = reply.proceed().unwrap();
518        let reply = match reply {
519            RecvRequestResult::Send100(r) => r,
520            _ => panic!("Expected Send100 state"),
521        };
522
523        // Accept the 100-continue request
524        // Accept the 100-continue request
525        let mut output = vec![0_u8; 1024];
526        let (n, reply) = reply.accept(&mut output).unwrap();
527
528        assert_eq!(&output[..n], b"HTTP/1.1 100 Continue\r\n\r\n");
529
530        // Receive chunked body
531        let mut reply = reply;
532        let mut body_buf = vec![0_u8; 1024];
533
534        // First chunk
535        let input = b"5\r\nhello\r\n";
536        let (input_used, output_used) = reply.read(input, &mut body_buf).unwrap();
537        assert_eq!(input_used, 10);
538        assert_eq!(&body_buf[..output_used], b"hello");
539
540        // Final chunk
541        let input = b"0\r\n\r\n";
542        let (input_used, output_used) = reply.read(input, &mut body_buf[5..]).unwrap();
543        assert_eq!(input_used, 5);
544        assert_eq!(output_used, 0);
545
546        assert!(reply.is_ended());
547    }
548
549    #[test]
550    fn post_with_content_length() {
551        let mut reply = Reply::new().unwrap();
552
553        // Receive POST request with Content-Length
554        let input = b"POST /data HTTP/1.1\r\n\
555            host: test.local\r\n\
556            content-length: 11\r\n\
557            \r\n";
558        let (input_used, request) = reply.try_request(input).unwrap();
559        let request = request.unwrap();
560
561        assert_eq!(input_used, 61); // Verify exact bytes consumed
562        assert_eq!(request.method(), "POST");
563        assert_eq!(request.uri().path(), "/data");
564
565        // Should go to RecvBody state
566        let reply = reply.proceed().unwrap();
567        let mut reply = match reply {
568            RecvRequestResult::RecvBody(r) => r,
569            _ => panic!("Expected RecvBody state"),
570        };
571
572        // Receive fixed-length body
573        let mut body_buf = vec![0_u8; 1024];
574        let input = b"Hello World";
575        let (input_used, output_used) = reply.read(input, &mut body_buf).unwrap();
576
577        assert_eq!(input_used, 11);
578        assert_eq!(&body_buf[..output_used], b"Hello World");
579        assert!(reply.is_ended());
580    }
581
582    #[test]
583    fn head_response_with_body_fails() {
584        let mut reply = Reply::new().unwrap();
585
586        // Receive HEAD request
587        let input = b"HEAD /status HTTP/1.1\r\n\
588            host: test.local\r\n\
589            \r\n";
590        let (input_used, request) = reply.try_request(input).unwrap();
591        let request = request.unwrap();
592
593        assert_eq!(input_used, 43); // Verify exact bytes consumed
594        assert_eq!(request.method(), "HEAD");
595        assert_eq!(request.uri().path(), "/status");
596
597        // Go to ProvideResponse
598        let reply = reply.proceed().unwrap();
599        let RecvRequestResult::ProvideResponse(reply) = reply else {
600            panic!("Expected ProvideResponse state");
601        };
602
603        // Provide a response
604        let response = Response::builder()
605            .status(StatusCode::OK)
606            .header("content-length", "1000") // Even with content-length
607            .body(())
608            .unwrap();
609
610        reply
611            .provide(response)
612            .expect_err("no body allowed on HEAD response");
613    }
614
615    #[test]
616    fn head_response_with_body_and_footgun() {
617        let mut reply = Reply::new().unwrap();
618
619        // Receive HEAD request
620        let input = b"HEAD /status HTTP/1.1\r\n\
621            host: test.local\r\n\
622            \r\n";
623        let (input_used, request) = reply.try_request(input).unwrap();
624        let request = request.unwrap();
625
626        assert_eq!(input_used, 43); // Verify exact bytes consumed
627        assert_eq!(request.method(), "HEAD");
628        assert_eq!(request.uri().path(), "/status");
629
630        // Go to ProvideResponse
631        let reply = reply.proceed().unwrap();
632        let RecvRequestResult::ProvideResponse(mut reply) = reply else {
633            panic!("Expected ProvideResponse state");
634        };
635
636        // Provide a response
637        let response = Response::builder()
638            .status(StatusCode::OK)
639            .header("content-length", "1000") // Even with content-length
640            .body(())
641            .unwrap();
642
643        reply.force_send_body();
644        let mut reply = reply.provide(response).unwrap();
645
646        // Write response headers
647        let mut output = vec![0_u8; 1024];
648        let n = reply.write(&mut output).unwrap();
649
650        // For HEAD requests, we send headers but no body
651        let s = str::from_utf8(&output[..n]).unwrap();
652        assert!(s.contains("content-length: 1000"));
653        assert!(!s.contains("transfer-encoding"));
654    }
655
656    #[test]
657    fn post_streaming() {
658        let mut reply = Reply::new().unwrap();
659
660        // Receive streaming POST request
661        let input = b"POST /upload HTTP/1.1\r\n\
662            host: test.local\r\n\
663            transfer-encoding: chunked\r\n\
664            \r\n";
665        let (input_used, request) = reply.try_request(input).unwrap();
666        let request = request.unwrap();
667
668        assert_eq!(input_used, 71);
669        assert_eq!(request.method(), "POST");
670        assert_eq!(request.uri().path(), "/upload");
671
672        // Should go to RecvBody state
673        let reply = reply.proceed().unwrap();
674        let mut reply = match reply {
675            RecvRequestResult::RecvBody(r) => r,
676            _ => panic!("Expected RecvBody state"),
677        };
678
679        // Receive first chunk
680        let mut body_buf = vec![0_u8; 1024];
681        let input = b"5\r\nhello\r\n";
682        let (input_used, output_used) = reply.read(input, &mut body_buf).unwrap();
683        assert_eq!(input_used, 10);
684        assert_eq!(output_used, 5);
685        assert_eq!(&body_buf[..output_used], b"hello");
686
687        // Receive final chunk
688        let input = b"0\r\n\r\n";
689        let (input_used, output_used) = reply.read(input, &mut body_buf[5..]).unwrap();
690        assert_eq!(input_used, 5);
691        assert_eq!(output_used, 0);
692        assert!(reply.is_ended());
693    }
694
695    #[test]
696    fn post_small_input() {
697        let mut reply = Reply::new().unwrap();
698
699        // Receive POST request headers in small chunks
700        let input1 = b"POST /upload";
701        let (used1, req1) = reply.try_request(input1).unwrap();
702        assert_eq!(used1, 0);
703        assert!(req1.is_none());
704
705        let input2 = b"POST /upload HTTP/1.1\r\n";
706        let (used2, req2) = reply.try_request(input2).unwrap();
707        assert_eq!(used2, 0);
708        assert!(req2.is_none());
709
710        let input3 = b"POST /upload HTTP/1.1\r\n\
711            host: test.local\r\n";
712        let (used3, req3) = reply.try_request(input3).unwrap();
713        assert_eq!(used3, 0);
714        assert!(req3.is_none());
715
716        let input4 = b"POST /upload HTTP/1.1\r\n\
717            host: test.local\r\n\
718            \r\n";
719        let (used4, req4) = reply.try_request(input4).unwrap();
720        assert_eq!(used4, 43);
721        let request = req4.unwrap();
722        assert_eq!(request.method(), "POST");
723        assert_eq!(request.uri().path(), "/upload");
724    }
725
726    #[test]
727    fn post_with_short_content_length() {
728        let mut reply = Reply::new().unwrap();
729
730        // Receive POST request with short content-length
731        let input = b"POST /upload HTTP/1.1\r\n\
732            host: test.local\r\n\
733            content-length: 2\r\n\
734            \r\n";
735        let (input_used, request) = reply.try_request(input).unwrap();
736        let request = request.unwrap();
737
738        assert_eq!(input_used, 62);
739        assert_eq!(request.method(), "POST");
740
741        // Should go to RecvBody state
742        let reply = reply.proceed().unwrap();
743        let mut reply = match reply {
744            RecvRequestResult::RecvBody(r) => r,
745            _ => panic!("Expected RecvBody state"),
746        };
747
748        // Try to receive more data than content-length
749        let mut body_buf = vec![0_u8; 1024];
750        let input = b"hello";
751        let (i1, o1) = reply.read(input, &mut body_buf).unwrap();
752        assert_eq!(i1, 2);
753        assert_eq!(o1, 2);
754
755        assert!(reply.is_ended());
756    }
757
758    #[test]
759    fn post_streaming_too_much() {
760        let mut reply = Reply::new().unwrap();
761
762        // Receive POST request with content-length
763        let input = b"POST /upload HTTP/1.1\r\n\
764            host: test.local\r\n\
765            content-length: 5\r\n\
766            \r\n";
767        let (input_used, request) = reply.try_request(input).unwrap();
768        let request = request.unwrap();
769
770        assert_eq!(input_used, 62);
771        assert_eq!(request.method(), "POST");
772
773        // Should go to RecvBody state
774        let reply = reply.proceed().unwrap();
775        let mut reply = match reply {
776            RecvRequestResult::RecvBody(r) => r,
777            _ => panic!("Expected RecvBody state"),
778        };
779
780        // Try to receive more data than content-length
781        let mut body_buf = vec![0_u8; 1024];
782        let input = b"hello world"; // 11 bytes, but content-length is 5
783        let (input_used, output_used) = reply.read(input, &mut body_buf).unwrap();
784        assert_eq!(input_used, 5);
785        assert_eq!(output_used, 5);
786    }
787
788    #[test]
789    fn post_streaming_after_end() {
790        let mut reply = Reply::new().unwrap();
791
792        // Receive POST request with chunked encoding
793        let input = b"POST /upload HTTP/1.1\r\n\
794            host: test.local\r\n\
795            transfer-encoding: chunked\r\n\
796            \r\n";
797        let (input_used, request) = reply.try_request(input).unwrap();
798        let request = request.unwrap();
799
800        assert_eq!(input_used, 71);
801        assert_eq!(request.method(), "POST");
802
803        // Should go to RecvBody state
804        let reply = reply.proceed().unwrap();
805        let mut reply = match reply {
806            RecvRequestResult::RecvBody(r) => r,
807            _ => panic!("Expected RecvBody state"),
808        };
809
810        // Receive body chunks
811        let mut body_buf = vec![0_u8; 1024];
812
813        // First chunk
814        let input = b"5\r\nhello\r\n";
815        let (input_used, output_used) = reply.read(input, &mut body_buf).unwrap();
816        assert_eq!(input_used, 10);
817        assert_eq!(output_used, 5);
818
819        // Final chunk
820        let input = b"0\r\n\r\n";
821        let (input_used, output_used) = reply.read(input, &mut body_buf[5..]).unwrap();
822        assert_eq!(input_used, 5);
823        assert_eq!(output_used, 0);
824        assert!(reply.is_ended());
825
826        // Try to receive more data after end
827        let input = b"more data";
828        let (i1, o1) = reply.read(input, &mut body_buf).unwrap();
829        assert_eq!(i1, 0);
830        assert_eq!(o1, 0);
831    }
832
833    #[test]
834    fn post_with_short_body_input() {
835        let mut reply = Reply::new().unwrap();
836
837        // Receive POST request with content-length
838        let input = b"POST /upload HTTP/1.1\r\n\
839            host: test.local\r\n\
840            content-length: 11\r\n\
841            \r\n";
842        let (input_used, request) = reply.try_request(input).unwrap();
843        let request = request.unwrap();
844
845        assert_eq!(input_used, 63);
846        assert_eq!(request.method(), "POST");
847
848        // Should go to RecvBody state
849        let reply = reply.proceed().unwrap();
850        let mut reply = match reply {
851            RecvRequestResult::RecvBody(r) => r,
852            _ => panic!("Expected RecvBody state"),
853        };
854
855        // Receive body in small chunks
856        let mut body_buf = vec![0_u8; 1024];
857
858        // First chunk
859        let input = b"He";
860        let (input_used, output_used) = reply.read(input, &mut body_buf).unwrap();
861        assert_eq!(input_used, 2);
862        assert_eq!(output_used, 2);
863        assert_eq!(&body_buf[..output_used], b"He");
864
865        // Second chunk
866        let input = b"llo ";
867        let (input_used, output_used) = reply.read(input, &mut body_buf[2..]).unwrap();
868        assert_eq!(input_used, 4);
869        assert_eq!(output_used, 4);
870        assert_eq!(&body_buf[..6], b"Hello ");
871
872        // Final chunk
873        let input = b"World";
874        let (input_used, output_used) = reply.read(input, &mut body_buf[6..]).unwrap();
875        assert_eq!(input_used, 5);
876        assert_eq!(output_used, 5);
877        assert_eq!(&body_buf[..11], b"Hello World");
878        assert!(reply.is_ended());
879    }
880
881    #[test]
882    fn non_standard_method_is_ok() {
883        let mut reply = Reply::new().unwrap();
884
885        // Try to receive request with non-standard method
886        let input = b"FNORD /page HTTP/1.1\r\n\
887            host: test.local\r\n\
888            \r\n";
889
890        let result = reply.try_request(input);
891        assert!(result.is_ok());
892    }
893
894    #[test]
895    fn ensure_reasonable_stack_sizes() {
896        macro_rules! ensure {
897            ($type:ty, $size:tt) => {
898                let sz = std::mem::size_of::<$type>();
899                assert!(
900                    sz <= $size,
901                    "Stack size of {} is too big {} > {}",
902                    stringify!($type),
903                    sz,
904                    $size
905                );
906            };
907        }
908
909        ensure!(http::Response<()>, 300); // ~224
910        ensure!(AmendedResponse, 400); // ~368
911        ensure!(Inner, 600); // ~512
912        ensure!(Reply<RecvRequest>, 600); // ~512
913    }
914
915    #[test]
916    fn connect() {
917        let mut reply = Reply::new().unwrap();
918
919        let input = b"CONNECT example.com HTTP/1.1\r\nhost: example.com\r\n\r\n";
920
921        let (input_used, request) = reply.try_request(input).unwrap();
922        let request = request.unwrap();
923
924        assert_eq!(input_used, 51);
925        assert_eq!(request.method(), "CONNECT");
926        assert_eq!(request.uri().path(), "");
927
928        // Should go to ProvideReponse state (content-length/transfer-encoding is ignored with CONNECT)
929        let RecvRequestResult::ProvideResponse(reply) = reply.proceed().unwrap() else {
930            panic!("Expected ProvideResponse state");
931        };
932
933        let response = Response::builder().status(StatusCode::OK).body(()).unwrap();
934
935        let mut reply = reply.provide(response).unwrap();
936
937        let mut output = vec![0_u8; 1024];
938        let n = reply.write(&mut output).unwrap();
939
940        // Response should ignore provided content-length/transfer-encoding headers
941        let s = str::from_utf8(&output[..n]).unwrap();
942        assert_eq!(s, "HTTP/1.1 200 OK\r\n\r\n");
943
944        // should go to Cleanup state (content-length/transfer-encoding is ignored with CONNECT)
945        let SendResponseResult::Cleanup(_reply) = reply.proceed() else {
946            panic!("Expected Cleanup state")
947        };
948    }
949
950    #[test]
951    fn connect_read_body() {
952        let mut reply = Reply::new().unwrap();
953        reply.force_recv_body();
954
955        let input =
956            b"CONNECT example.com HTTP/1.1\r\nhost: example.com\r\ncontent-length: 1024\r\n\r\n";
957
958        let (input_used, request) = reply.try_request(input).unwrap();
959        let request = request.unwrap();
960
961        assert_eq!(input_used, 73);
962        assert_eq!(request.method(), "CONNECT");
963        assert_eq!(request.uri().path(), "");
964
965        // Should go to RecvBody state (body reading footgun was enabled)
966        let RecvRequestResult::RecvBody(_reply) = reply.proceed().unwrap() else {
967            panic!("Expected RecvBody state");
968        };
969    }
970
971    #[test]
972    fn connect_send_body_fails() {
973        let mut reply = Reply::new().unwrap();
974
975        let input =
976            b"CONNECT example.com HTTP/1.1\r\nhost: example.com\r\ncontent-length: 1024\r\n\r\n";
977
978        let (input_used, request) = reply.try_request(input).unwrap();
979        let request = request.unwrap();
980
981        assert_eq!(input_used, 73);
982        assert_eq!(request.method(), "CONNECT");
983        assert_eq!(request.uri().path(), "");
984
985        // Should go to ProvideReponse state (content-length/transfer-encoding is ignored with CONNECT)
986        let RecvRequestResult::ProvideResponse(reply) = reply.proceed().unwrap() else {
987            panic!("Expected ProvideResponse state");
988        };
989
990        let response = Response::builder()
991            .status(StatusCode::OK)
992            .header("content-length", 1024)
993            .body(())
994            .unwrap();
995
996        reply
997            .provide(response)
998            .expect_err("no body allowed on CONNECT response");
999    }
1000
1001    #[test]
1002    fn connect_send_body_with_footgun() {
1003        let mut reply = Reply::new().unwrap();
1004
1005        let input =
1006            b"CONNECT example.com HTTP/1.1\r\nhost: example.com\r\ncontent-length: 1024\r\n\r\n";
1007
1008        let (input_used, request) = reply.try_request(input).unwrap();
1009        let request = request.unwrap();
1010
1011        assert_eq!(input_used, 73);
1012        assert_eq!(request.method(), "CONNECT");
1013        assert_eq!(request.uri().path(), "");
1014
1015        // Should go to ProvideReponse state (content-length/transfer-encoding is ignored with CONNECT)
1016        let RecvRequestResult::ProvideResponse(mut reply) = reply.proceed().unwrap() else {
1017            panic!("Expected ProvideResponse state");
1018        };
1019
1020        let response = Response::builder()
1021            .status(StatusCode::OK)
1022            .header("content-length", 1024)
1023            .body(())
1024            .unwrap();
1025
1026        reply.force_send_body();
1027        let mut reply = reply.provide(response).unwrap();
1028
1029        let mut output = vec![0_u8; 1024];
1030        let n = reply.write(&mut output).unwrap();
1031
1032        let s = str::from_utf8(&output[..n]).unwrap();
1033        assert_eq!(s, "HTTP/1.1 200 OK\r\ncontent-length: 1024\r\n\r\n");
1034
1035        let SendResponseResult::SendBody(_reply) = reply.proceed() else {
1036            panic!("Expected SendBody state")
1037        };
1038    }
1039}