ureq_proto/client/
mod.rs

1//! HTTP/1.1 client 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 [`Call`] object attempts to encode correct HTTP/1.1 handling using
7//! state variables, for example `Call<'a, SendRequest>` to represent the
8//! lifecycle stage where we are to send the request.
9//!
10//! The states are:
11//!
12//! * **Prepare** - Preparing a request means 1) adding headers such as
13//!   cookies. 2) acquiring the connection from a pool or opening a new
14//!   socket (potentially wrappping in TLS)
15//! * **SendRequest** - Send the first row, which is the method, path
16//!   and version as well as the request headers
17//! * **SendBody** - Send the request body
18//! * **Await100** - If there is an `Expect: 100-continue` header, the
19//!   client should pause before sending the body
20//! * **RecvResponse** - Receive the response, meaning the status and
21//!   version and the response headers
22//! * **RecvBody** - Receive the response body
23//! * **Redirect** - Handle redirects, potentially spawning new requests
24//! * **Cleanup** - Return the connection to the pool or close it
25//!
26//!
27//! ```text
28//!                            ┌──────────────────┐
29//! ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ▶│     Prepare      │
30//!                            └──────────────────┘
31//! │                                    │
32//!                                      ▼
33//! │                          ┌──────────────────┐
34//!                         ┌──│   SendRequest    │──────────────┐
35//! │                       │  └──────────────────┘              │
36//!                         │            │                       │
37//! │                       │            ▼                       ▼
38//!                         │  ┌──────────────────┐    ┌──────────────────┐
39//! │                       │  │     SendBody     │◀───│     Await100     │
40//!                         │  └──────────────────┘    └──────────────────┘
41//! │                       │            │                       │
42//!                         │            ▼                       │
43//! │                       └─▶┌──────────────────┐◀─────────────┘
44//!              ┌─────────────│   RecvResponse   │──┐
45//! │            │             └──────────────────┘  │
46//!              │                       │           │
47//! │            ▼                       ▼           │
48//!    ┌──────────────────┐    ┌──────────────────┐  │
49//! └ ─│     Redirect     │◀───│     RecvBody     │  │
50//!    └──────────────────┘    └──────────────────┘  │
51//!              │                       │           │
52//!              │                       ▼           │
53//!              │             ┌──────────────────┐  │
54//!              └────────────▶│     Cleanup      │◀─┘
55//!                            └──────────────────┘
56//! ```
57//!
58//! # Example
59//!
60//! ```
61//! use ureq_proto::client::*;
62//! use ureq_proto::http::Request;
63//!
64//! let request = Request::put("https://example.test/my-path")
65//!     .header("Expect", "100-continue")
66//!     .header("x-foo", "bar")
67//!     .body(())
68//!     .unwrap();
69//!
70//! // ********************************** Prepare
71//!
72//! let mut call = Call::new(request).unwrap();
73//!
74//! // Prepare with state from cookie jar. The uri
75//! // is used to key the cookies.
76//! let uri = call.uri();
77//!
78//! // call.header("Cookie", "my_cookie1=value1");
79//! // call.header("Cookie", "my_cookie2=value2");
80//!
81//! // Obtain a connection for the uri, either a
82//! // pooled connection from a previous http/1.1
83//! // keep-alive, or open a new. The connection
84//! // must be TLS wrapped if the scheme so indicate.
85//! // let connection = todo!();
86//!
87//! // Sans-IO means it does not use any
88//! // Write trait or similar. Requests and request
89//! // bodies are written to a buffer that in turn
90//! // should be sent via the connection.
91//! let mut output = vec![0_u8; 1024];
92//!
93//! // ********************************** SendRequest
94//!
95//! // Proceed to the next state writing the request.
96//! let mut call = call.proceed();
97//!
98//! let output_used = call.write(&mut output).unwrap();
99//! assert_eq!(output_used, 107);
100//!
101//! assert_eq!(&output[..output_used], b"\
102//!     PUT /my-path HTTP/1.1\r\n\
103//!     host: example.test\r\n\
104//!     transfer-encoding: chunked\r\n\
105//!     expect: 100-continue\r\n\
106//!     x-foo: bar\r\n\
107//!     \r\n");
108//!
109//! // Check we can continue to send the body
110//! assert!(call.can_proceed());
111//!
112//! // ********************************** Await100
113//!
114//! // In this example, we know the next state is Await100.
115//! // A real client needs to match on the variants.
116//! let mut call = match call.proceed() {
117//!     Ok(Some(SendRequestResult::Await100(v))) => v,
118//!     _ => panic!(),
119//! };
120//!
121//! // When awaiting 100, the client should run a timer and
122//! // proceed to sending the body either when the server
123//! // indicates it can receive the body, or the timer runs out.
124//!
125//! // This boolean can be checked whether there's any point
126//! // in keeping waiting for the timer to run out.
127//! assert!(call.can_keep_await_100());
128//!
129//! let input = b"HTTP/1.1 100 Continue\r\n\r\n";
130//! let input_used = call.try_read_100(input).unwrap();
131//!
132//! assert_eq!(input_used, 25);
133//! assert!(!call.can_keep_await_100());
134//!
135//! // ********************************** SendBody
136//!
137//! // Proceeding is possible regardless of whether the
138//! // can_keep_await_100() is true or false.
139//! // A real client needs to match on the variants.
140//! let mut call = match call.proceed() {
141//!     Ok(Await100Result::SendBody(v)) => v,
142//!     _ => panic!(),
143//! };
144//!
145//! let (input_used, o1) =
146//!     call.write(b"hello", &mut output).unwrap();
147//!
148//! assert_eq!(input_used, 5);
149//!
150//! // When doing transfer-encoding: chunked,
151//! // the end of body must be signaled with
152//! // an empty input. This is also valid for
153//! // regular content-length body.
154//! assert!(!call.can_proceed());
155//!
156//! let (_, o2) = call.write(&[], &mut output[o1..]).unwrap();
157//!
158//! let output_used = o1 + o2;
159//! assert_eq!(output_used, 15);
160//!
161//! assert_eq!(&output[..output_used], b"\
162//!     5\r\n\
163//!     hello\
164//!     \r\n\
165//!     0\r\n\
166//!     \r\n");
167//!
168//! assert!(call.can_proceed());
169//!
170//! // ********************************** RecvRequest
171//!
172//! // Proceed to read the request.
173//! let mut call = call.proceed().unwrap();
174//!
175//! let part = b"HTTP/1.1 200 OK\r\nContent-Len";
176//! let full = b"HTTP/1.1 200 OK\r\nContent-Length: 9\r\n\r\n";
177//!
178//! // try_response can be used repeatedly until we
179//! // get enough content including all headers.
180//! let (input_used, maybe_response) =
181//!     call.try_response(part, false).unwrap();
182//!
183//! assert_eq!(input_used, 0);
184//! assert!(maybe_response.is_none());
185//!
186//! let (input_used, maybe_response) =
187//!     call.try_response(full, false).unwrap();
188//!
189//! assert_eq!(input_used, 38);
190//! let response = maybe_response.unwrap();
191//!
192//! // ********************************** RecvBody
193//!
194//! // It's not possible to proceed until we
195//! // have read a response.
196//! let mut call = match call.proceed() {
197//!     Some(RecvResponseResult::RecvBody(v)) => v,
198//!     _ => panic!(),
199//! };
200//!
201//! let(input_used, output_used) =
202//!     call.read(b"hi there!", &mut output).unwrap();
203//!
204//! assert_eq!(input_used, 9);
205//! assert_eq!(output_used, 9);
206//!
207//! assert_eq!(&output[..output_used], b"hi there!");
208//!
209//! // ********************************** Cleanup
210//!
211//! let call = match call.proceed() {
212//!     Some(RecvBodyResult::Cleanup(v)) => v,
213//!     _ => panic!(),
214//! };
215//!
216//! if call.must_close_connection() {
217//!     // connection.close();
218//! } else {
219//!     // connection.return_to_pool();
220//! }
221//!
222//! ```
223
224use std::fmt;
225use std::marker::PhantomData;
226
227use http::{HeaderValue, StatusCode};
228
229use crate::body::{BodyReader, BodyWriter};
230use crate::util::ArrayVec;
231use crate::CloseReason;
232
233use amended::AmendedRequest;
234
235mod amended;
236
237// mod holder;
238
239#[cfg(test)]
240mod test;
241
242/// Max number of headers to parse from an HTTP response
243pub const MAX_RESPONSE_HEADERS: usize = 128;
244
245/// State types for the Call state machine.
246///
247/// These types are used as type parameters to `Call<B, State>` to represent
248/// the current state of the HTTP request/response state machine.
249pub mod state {
250    pub(crate) trait Named {
251        fn name() -> &'static str;
252    }
253
254    macro_rules! call_state {
255        ($n:tt) => {
256            #[doc(hidden)]
257            pub struct $n(());
258            impl Named for $n {
259                fn name() -> &'static str {
260                    stringify!($n)
261                }
262            }
263        };
264    }
265
266    call_state!(Prepare);
267    call_state!(SendRequest);
268    call_state!(Await100);
269    call_state!(SendBody);
270    call_state!(RecvResponse);
271    call_state!(RecvBody);
272    call_state!(Redirect);
273    call_state!(Cleanup);
274}
275use self::state::*;
276
277/// A state machine for an HTTP request/response cycle.
278///
279/// This type represents a state machine that transitions through various
280/// states during the lifecycle of an HTTP request/response.
281///
282/// The type parameters are:
283/// - `State`: The current state of the state machine (e.g., `Prepare`, `SendRequest`, etc.)
284///
285/// See the [state graph][crate::client] in the client module documentation for a
286/// visual representation of the state transitions.
287pub struct Call<State> {
288    inner: Inner,
289    _ph: PhantomData<State>,
290}
291
292/// Internal state of a Call.
293///
294/// This struct contains the actual state data for a Call, independent of the
295/// state type parameter. It's exposed as pub(crate) to allow tests to inspect
296/// the state.
297#[derive(Debug)]
298pub(crate) struct Inner {
299    pub request: AmendedRequest,
300    pub analyzed: bool,
301    pub state: BodyState,
302    pub close_reason: ArrayVec<CloseReason, 4>,
303    pub force_recv_body: bool,
304    pub force_send_body: bool,
305    pub await_100_continue: bool,
306    pub status: Option<StatusCode>,
307    pub location: Option<HeaderValue>,
308}
309
310impl Inner {
311    fn is_redirect(&self) -> bool {
312        match self.status {
313            // 304 is a redirect code, but it has no location header and
314            // thus we don't consider it a redirection.
315            Some(v) => v.is_redirection() && v != StatusCode::NOT_MODIFIED,
316            None => false,
317        }
318    }
319}
320
321/// State of the request/response body.
322///
323/// This struct tracks the current phase of the request/response cycle
324/// and manages the body writers and readers.
325#[derive(Debug, Default)]
326pub(crate) struct BodyState {
327    phase: RequestPhase,
328    writer: BodyWriter,
329    reader: Option<BodyReader>,
330    allow_non_standard_methods: bool,
331    stop_on_chunk_boundary: bool,
332}
333
334impl BodyState {
335    fn need_response_body(&self) -> bool {
336        !matches!(
337            self.reader,
338            Some(BodyReader::NoBody) | Some(BodyReader::LengthDelimited(0))
339        )
340    }
341}
342
343/// Phases of sending an HTTP request.
344///
345/// This enum represents the different phases of sending an HTTP request:
346/// - `SendLine`: Sending the request line (method, path, version)
347/// - `SendHeaders`: Sending the request headers
348/// - `SendBody`: Sending the request body
349#[derive(Clone, Copy, PartialEq, Eq, Default)]
350enum RequestPhase {
351    #[default]
352    Line,
353    Headers(usize),
354    Body,
355}
356
357impl RequestPhase {
358    fn is_prelude(&self) -> bool {
359        matches!(self, RequestPhase::Line | RequestPhase::Headers(_))
360    }
361
362    fn is_body(&self) -> bool {
363        matches!(self, RequestPhase::Body)
364    }
365}
366
367impl<S> Call<S> {
368    fn wrap(inner: Inner) -> Call<S>
369    where
370        S: Named,
371    {
372        let wrapped = Call {
373            inner,
374            _ph: PhantomData,
375        };
376
377        debug!("{:?}", wrapped);
378
379        wrapped
380    }
381
382    #[cfg(test)]
383    pub(crate) fn inner(&self) -> &Inner {
384        &self.inner
385    }
386}
387
388// //////////////////////////////////////////////////////////////////////////////////////////// PREPARE
389
390mod prepare;
391
392// //////////////////////////////////////////////////////////////////////////////////////////// SEND REQUEST
393
394mod sendreq;
395
396/// Possible state transitions after sending a request.
397///
398/// After sending the request headers, the call can transition to one of three states:
399/// - `Await100`: If the request included an `Expect: 100-continue` header
400/// - `SendBody`: If the request has a body to send
401/// - `RecvResponse`: If the request has no body (e.g., GET, HEAD)
402///
403/// See the [state graph][crate::client] for a visual representation.
404pub enum SendRequestResult {
405    /// Expect-100/Continue mechanic.
406    Await100(Call<Await100>),
407
408    /// Send the request body.
409    SendBody(Call<SendBody>),
410
411    /// Receive the response.
412    RecvResponse(Call<RecvResponse>),
413}
414
415// //////////////////////////////////////////////////////////////////////////////////////////// AWAIT 100
416
417mod await100;
418
419/// Possible state transitions after awaiting a 100 Continue response.
420///
421/// After awaiting a 100 Continue response, the call can transition to one of two states:
422/// - `SendBody`: If the server sent a 100 Continue response or the timeout expired
423/// - `RecvResponse`: If the server sent a different response
424///
425/// See the [state graph][crate::client] for a visual representation.
426pub enum Await100Result {
427    /// Send the request body.
428    SendBody(Call<SendBody>),
429
430    /// Receive server response.
431    RecvResponse(Call<RecvResponse>),
432}
433
434// //////////////////////////////////////////////////////////////////////////////////////////// SEND BODY
435
436mod sendbody;
437
438// //////////////////////////////////////////////////////////////////////////////////////////// RECV RESPONSE
439
440mod recvresp;
441
442/// Possible state transitions after receiving a response.
443///
444/// After receiving a response (status and headers), the call can transition to one of three states:
445/// - `RecvBody`: If the response has a body to receive
446/// - `Redirect`: If the response is a redirect
447/// - `Cleanup`: If the response has no body and is not a redirect
448///
449/// See the [state graph][crate::client] for a visual representation.
450pub enum RecvResponseResult {
451    /// Receive a response body.
452    RecvBody(Call<RecvBody>),
453
454    /// Follow a redirect.
455    Redirect(Call<Redirect>),
456
457    /// Run cleanup.
458    Cleanup(Call<Cleanup>),
459}
460
461// //////////////////////////////////////////////////////////////////////////////////////////// RECV BODY
462
463mod recvbody;
464
465/// Possible state transitions after receiving a response body.
466///
467/// After receiving a response body, the call can transition to one of two states:
468/// - `Redirect`: If the response is a redirect
469/// - `Cleanup`: If the response is not a redirect
470///
471/// See the [state graph][crate::client] for a visual representation.
472pub enum RecvBodyResult {
473    /// Follow a redirect
474    Redirect(Call<Redirect>),
475
476    /// Go to cleanup
477    Cleanup(Call<Cleanup>),
478}
479
480// //////////////////////////////////////////////////////////////////////////////////////////// REDIRECT
481
482mod redirect;
483
484/// Strategy for preserving authorization headers during redirects.
485///
486/// This enum defines how authorization headers should be handled when following
487/// redirects:
488///
489/// * `Never`: Never preserve the `authorization` header in redirects. This is the default.
490/// * `SameHost`: Preserve the `authorization` header when the redirect is to the same host
491///   and uses the same scheme (or switches to a more secure one, i.e., from HTTP to HTTPS,
492///   but not the reverse).
493#[derive(Debug, Clone, Copy, PartialEq, Eq)]
494#[non_exhaustive]
495pub enum RedirectAuthHeaders {
496    /// Never preserve the `authorization` header on redirect. This is the default.
497    Never,
498    /// Preserve the `authorization` header when the redirect is to the same host. Both hosts must use
499    /// the same scheme (or switch to a more secure one, i.e we can redirect from `http` to `https`,
500    /// but not the reverse).
501    SameHost,
502}
503
504// //////////////////////////////////////////////////////////////////////////////////////////// CLEANUP
505
506impl Call<Cleanup> {
507    /// Tell if we must close the connection.
508    pub fn must_close_connection(&self) -> bool {
509        self.close_reason().is_some()
510    }
511
512    /// If we are closing the connection, give a reason.
513    pub fn close_reason(&self) -> Option<&'static str> {
514        self.inner.close_reason.first().map(|s| s.explain())
515    }
516}
517
518// ////////////////////////////////////////////////////////////////////////////////////////////
519
520impl<State: Named> fmt::Debug for Call<State> {
521    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
522        write!(f, "Call<{}>", State::name())
523    }
524}
525
526impl fmt::Debug for RequestPhase {
527    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
528        match self {
529            Self::Line => write!(f, "SendLine"),
530            Self::Headers(_) => write!(f, "SendHeaders"),
531            Self::Body => write!(f, "SendBody"),
532        }
533    }
534}
535
536#[cfg(test)]
537mod tests {
538    use super::*;
539    use crate::client::amended::AmendedRequest;
540    use crate::client::state::SendRequest;
541    use crate::client::Inner;
542    use crate::Error;
543    use http::{header, Method, Request, Version};
544    use std::str;
545
546    #[test]
547    fn get_simple() {
548        let req = Request::get("http://foo.test/page")
549            .header("content-length", "0")
550            .body(())
551            .unwrap();
552
553        let call = Call::new(req).unwrap();
554
555        let mut call = call.proceed();
556
557        let mut output = vec![0; 1024];
558        let n = call.write(&mut output).unwrap();
559        let s = str::from_utf8(&output[..n]).unwrap();
560
561        assert_eq!(
562            s,
563            "GET /page HTTP/1.1\r\n\
564            host: foo.test\r\n\
565            content-length: 0\r\n\
566            \r\n"
567        );
568
569        // Since GET has no body (explicitly defined by content-length too) we should go straight to RecvResponse
570        let Ok(Some(SendRequestResult::RecvResponse(_call))) = call.proceed() else {
571            panic!("Exepcted `RecvResponse`")
572        };
573    }
574
575    #[test]
576    fn head_simple() {
577        let req = Request::head("http://foo.test/page").body(()).unwrap();
578        let call = Call::new(req).unwrap();
579
580        let mut call = call.proceed();
581
582        let mut output = vec![0; 1024];
583        let n = call.write(&mut output).unwrap();
584        let s = str::from_utf8(&output[..n]).unwrap();
585
586        assert_eq!(s, "HEAD /page HTTP/1.1\r\nhost: foo.test\r\n\r\n");
587    }
588
589    #[test]
590    fn head_without_body() {
591        let req = Request::head("http://foo.test/page").body(()).unwrap();
592        let call = Call::new(req).unwrap();
593
594        let mut call = call.proceed();
595
596        let mut output = vec![0; 1024];
597        call.write(&mut output).unwrap();
598
599        // Check if we can proceed
600        assert!(call.can_proceed());
601
602        // Proceed to the next state
603        let call = call.proceed().unwrap().unwrap();
604
605        // For a HEAD request, we should get a RecvResponse result
606        let SendRequestResult::RecvResponse(_) = call else {
607            panic!("Expected RecvResponse")
608        };
609    }
610
611    #[test]
612    fn head_with_body_despite_method() {
613        let req = Request::head("http://foo.fest/page")
614            .header(header::TRANSFER_ENCODING, "chunked")
615            .body(())
616            .unwrap();
617
618        let mut call = Call::new(req).unwrap();
619
620        // Force sending a body despite the method
621        call.force_send_body();
622
623        let mut call = call.proceed();
624
625        let mut output = vec![0; 1024];
626        call.write(&mut output).unwrap();
627
628        // Check if we can proceed
629        assert!(call.can_proceed());
630
631        // Proceed to the next state
632        let call = call.proceed().unwrap().unwrap();
633        let SendRequestResult::SendBody(mut call) = call else {
634            panic!("Expected SendBody")
635        };
636
637        // Write an empty body
638        let (i, n) = call.write(&[], &mut output).unwrap();
639        assert_eq!(i, 0);
640        assert_eq!(n, 5); // "0\r\n\r\n" for chunked encoding
641
642        // Check if we can proceed (body is fully sent)
643        assert!(call.can_proceed());
644    }
645
646    #[test]
647    fn post_simple() {
648        let req = Request::post("http://f.test/page")
649            .header("content-length", 5)
650            .body(())
651            .unwrap();
652        let call = Call::new(req).unwrap();
653
654        // Proceed to SendRequest
655        let mut call = call.proceed();
656
657        // Write the request headers
658        let mut output = vec![0; 1024];
659        let n1 = call.write(&mut output).unwrap();
660
661        // Check if we can proceed
662        assert!(call.can_proceed());
663
664        // Proceed to the next state
665        let call = call.proceed().unwrap().unwrap();
666        let SendRequestResult::SendBody(mut call) = call else {
667            panic!("Expected SendBody")
668        };
669
670        // Write the body
671        let (i1, n2) = call.write(b"hallo", &mut output[n1..]).unwrap();
672        assert_eq!(i1, 5);
673
674        let s = str::from_utf8(&output[..n1 + n2]).unwrap();
675        assert_eq!(
676            s,
677            "POST /page HTTP/1.1\r\nhost: f.test\r\ncontent-length: 5\r\n\r\nhallo"
678        );
679    }
680
681    #[test]
682    fn post_small_output() {
683        let req = Request::post("http://f.test/page")
684            .header("content-length", 5)
685            .body(())
686            .unwrap();
687        let call = Call::new(req).unwrap();
688
689        // Proceed to SendRequest
690        let mut call = call.proceed();
691
692        let mut output = vec![0; 1024];
693        let body = b"hallo";
694
695        // Write the request headers in multiple steps with small output buffers
696        {
697            let n = call.write(&mut output[..25]).unwrap();
698            let s = str::from_utf8(&output[..n]).unwrap();
699            assert_eq!(s, "POST /page HTTP/1.1\r\n");
700            assert!(!call.can_proceed());
701        }
702
703        {
704            let n = call.write(&mut output[..20]).unwrap();
705            let s = str::from_utf8(&output[..n]).unwrap();
706            assert_eq!(s, "host: f.test\r\n");
707            assert!(!call.can_proceed());
708        }
709
710        {
711            let n = call.write(&mut output[..21]).unwrap();
712            let s = str::from_utf8(&output[..n]).unwrap();
713            assert_eq!(s, "content-length: 5\r\n\r\n");
714            assert!(call.can_proceed());
715        }
716
717        // Proceed to SendBody
718        let call = call.proceed().unwrap().unwrap();
719        let SendRequestResult::SendBody(mut call) = call else {
720            panic!("Expected SendBody")
721        };
722
723        // Write the body
724        {
725            let (i, n) = call.write(body, &mut output[..25]).unwrap();
726            assert_eq!(n, 5);
727            assert_eq!(i, 5);
728            let s = str::from_utf8(&output[..n]).unwrap();
729            assert_eq!(s, "hallo");
730
731            // Check if we can proceed (body is fully sent)
732            assert!(call.can_proceed());
733        }
734    }
735
736    #[test]
737    fn post_with_short_content_length() {
738        let req = Request::post("http://f.test/page")
739            .header("content-length", 2)
740            .body(())
741            .unwrap();
742        let call = Call::new(req).unwrap();
743
744        // Proceed to SendRequest
745        let mut call = call.proceed();
746
747        // Write the request headers
748        let mut output = vec![0; 1024];
749        let n1 = call.write(&mut output).unwrap();
750
751        // Check if we can proceed
752        assert!(call.can_proceed());
753
754        // Proceed to SendBody
755        let call = call.proceed().unwrap().unwrap();
756        let SendRequestResult::SendBody(mut call) = call else {
757            panic!("Expected SendBody")
758        };
759
760        // Write the body (first write should fail because it's larger than content-length)
761        let body = b"hallo";
762        let r = call.write(body, &mut output[n1..]);
763        assert_eq!(r.unwrap_err(), Error::BodyLargerThanContentLength);
764
765        // Write a smaller body that fits within content-length
766        let body = b"ha";
767        let r = call.write(body, &mut output[n1..]);
768        assert!(r.is_ok());
769
770        // Check if we can proceed (body is fully sent)
771        assert!(call.can_proceed());
772    }
773
774    #[test]
775    fn post_with_short_body_input() {
776        let req = Request::post("http://f.test/page")
777            .header("content-length", 5)
778            .body(())
779            .unwrap();
780        let call = Call::new(req).unwrap();
781
782        // Proceed to SendRequest
783        let mut call = call.proceed();
784
785        // Write the request headers
786        let mut output = vec![0; 1024];
787        let n1 = call.write(&mut output).unwrap();
788
789        // Check if we can proceed
790        assert!(call.can_proceed());
791
792        // Proceed to SendBody
793        let call = call.proceed().unwrap().unwrap();
794        let SendRequestResult::SendBody(mut call) = call else {
795            panic!("Expected SendBody")
796        };
797
798        // Write the first part of the body
799        let (i1, n2) = call.write(b"ha", &mut output[n1..]).unwrap();
800        assert_eq!(i1, 2);
801
802        // Write the second part of the body
803        let (i2, n3) = call.write(b"ha", &mut output[n1 + n2..]).unwrap();
804        assert_eq!(i2, 2);
805
806        let s = str::from_utf8(&output[..n1 + n2 + n3]).unwrap();
807        assert_eq!(
808            s,
809            "POST /page HTTP/1.1\r\nhost: f.test\r\ncontent-length: 5\r\n\r\nhaha"
810        );
811
812        // Check if we can proceed (body is not fully sent yet)
813        assert!(!call.can_proceed());
814
815        // Write the third part of the body (should fail because it's larger than remaining content length)
816        let err = call.write(b"llo", &mut output[n1 + n2 + n3..]).unwrap_err();
817        assert_eq!(err, Error::BodyLargerThanContentLength);
818
819        // Write the last byte to complete the content length
820        let (i3, n4) = call.write(b"l", &mut output[n1 + n2 + n3..]).unwrap();
821        assert_eq!(i3, 1);
822
823        let s = str::from_utf8(&output[..n1 + n2 + n3 + n4]).unwrap();
824        assert_eq!(
825            s,
826            "POST /page HTTP/1.1\r\nhost: f.test\r\ncontent-length: 5\r\n\r\nhahal"
827        );
828
829        // Check if we can proceed (body is fully sent)
830        assert!(call.can_proceed());
831    }
832
833    #[test]
834    fn post_with_chunked() {
835        let req = Request::post("http://f.test/page")
836            .header("transfer-encoding", "chunked")
837            .body(())
838            .unwrap();
839        let call = Call::new(req).unwrap();
840
841        // Proceed to SendRequest
842        let mut call = call.proceed();
843
844        // Write the request headers
845        let mut output = vec![0; 1024];
846        let n1 = call.write(&mut output).unwrap();
847
848        // Check if we can proceed
849        assert!(call.can_proceed());
850
851        // Proceed to SendBody
852        let call = call.proceed().unwrap().unwrap();
853        let SendRequestResult::SendBody(mut call) = call else {
854            panic!("Expected SendBody")
855        };
856
857        let body = b"hallo";
858
859        // Write the first chunk of the body
860        let (i1, n2) = call.write(body, &mut output[n1..]).unwrap();
861        assert_eq!(i1, 5);
862
863        // Write the second chunk of the body
864        let (i2, n3) = call.write(body, &mut output[n1 + n2..]).unwrap();
865        assert_eq!(i2, 5);
866
867        // Indicate the end of the body
868        let (i3, n4) = call.write(&[], &mut output[n1 + n2 + n3..]).unwrap();
869        assert_eq!(i3, 0);
870
871        let s = str::from_utf8(&output[..n1 + n2 + n3 + n4]).unwrap();
872        assert_eq!(
873            s,
874            "POST /page HTTP/1.1\r\nhost: f.test\r\ntransfer-encoding: chunked\r\n\r\n5\r\nhallo\r\n5\r\nhallo\r\n0\r\n\r\n"
875        );
876
877        // Check if we can proceed (body is fully sent)
878        assert!(call.can_proceed());
879    }
880
881    #[test]
882    fn post_without_body() {
883        let req = Request::post("http://foo.test/page").body(()).unwrap();
884        let call = Call::new(req).unwrap();
885
886        // Proceed to SendRequest
887        let mut call = call.proceed();
888
889        // Write the request headers
890        let mut output = vec![0; 1024];
891        call.write(&mut output).unwrap();
892
893        // Check if we can proceed
894        assert!(call.can_proceed());
895
896        // Proceed to the next state
897        let call = call.proceed().unwrap().unwrap();
898
899        // For a POST request, we should get a SendBody result
900        let SendRequestResult::SendBody(mut call) = call else {
901            panic!("Expected SendBody");
902        };
903
904        // Check that we can't proceed without writing a body
905        assert!(!call.can_proceed());
906
907        // Write an empty body
908        let (i, n) = call.write(&[], &mut output).unwrap();
909        assert_eq!(i, 0);
910        assert_eq!(n, 5); // "0\r\n\r\n" for chunked encoding
911
912        // Check if we can proceed (body is fully sent)
913        assert!(call.can_proceed());
914    }
915
916    #[test]
917    fn post_streaming() {
918        let req = Request::post("http://f.test/page").body(()).unwrap();
919        let call = Call::new(req).unwrap();
920
921        // Proceed to SendRequest
922        let mut call = call.proceed();
923
924        // Write the request headers
925        let mut output = vec![0; 1024];
926        let n1 = call.write(&mut output).unwrap();
927
928        // Check if we can proceed
929        assert!(call.can_proceed());
930
931        // Proceed to SendBody
932        let call = call.proceed().unwrap().unwrap();
933        let SendRequestResult::SendBody(mut call) = call else {
934            panic!("Expected SendBody");
935        };
936
937        // Write the first chunk of the body (using i2, n2 to match original test)
938        let (i2, n2) = call.write(b"hallo", &mut output[n1..]).unwrap();
939
940        // Send end (using i3, n3 to match original test)
941        let (i3, n3) = call.write(&[], &mut output[n1 + n2..]).unwrap();
942
943        // Use i1 = 0 to match original test (in Call API, i1 is not used for headers)
944        let i1 = 0;
945
946        // Verify the results with the same assertions as the original test
947        assert_eq!(i1, 0);
948        assert_eq!(i2, 5);
949        assert_eq!(n1, 65);
950        assert_eq!(n2, 10);
951        assert_eq!(i3, 0);
952        assert_eq!(n3, 5);
953
954        let s = str::from_utf8(&output[..(n1 + n2 + n3)]).unwrap();
955
956        assert_eq!(
957            s,
958            "POST /page HTTP/1.1\r\nhost: f.test\r\ntransfer-encoding: chunked\r\n\r\n5\r\nhallo\r\n0\r\n\r\n"
959        );
960    }
961
962    #[test]
963    fn post_streaming_with_size() {
964        let req = Request::post("http://f.test/page")
965            .header("content-length", "5")
966            .body(())
967            .unwrap();
968        let call = Call::new(req).unwrap();
969
970        // Proceed to SendRequest
971        let mut call = call.proceed();
972
973        // Write the request headers
974        let mut output = vec![0; 1024];
975        let headers_n = call.write(&mut output).unwrap();
976
977        // Check if we can proceed
978        assert!(call.can_proceed());
979
980        // Proceed to SendBody
981        let call = call.proceed().unwrap().unwrap();
982        let SendRequestResult::SendBody(mut call) = call else {
983            panic!("Expected SendBody");
984        };
985
986        // Write the body (first call)
987        let (i1, n1) = call.write(b"hallo", &mut output[headers_n..]).unwrap();
988
989        // Verify the results
990        assert_eq!(i1, 5); // In Call API, i1 is the number of bytes consumed from the input
991        assert_eq!(n1, 5); // In Call API, n1 is the number of bytes written to the output
992
993        // Check if we can proceed (body is fully sent)
994        assert!(call.can_proceed());
995
996        // Try to write more data after the body is fully sent (should fail)
997        let err = call
998            .write(b"hallo", &mut output[headers_n + n1..])
999            .unwrap_err();
1000        assert_eq!(err, Error::BodyContentAfterFinish);
1001
1002        let s = str::from_utf8(&output[..headers_n + n1]).unwrap();
1003
1004        assert_eq!(
1005            s,
1006            "POST /page HTTP/1.1\r\nhost: f.test\r\ncontent-length: 5\r\n\r\nhallo"
1007        );
1008    }
1009
1010    #[test]
1011    fn post_streaming_after_end() {
1012        let req = Request::post("http://f.test/page").body(()).unwrap();
1013        let call = Call::new(req).unwrap();
1014
1015        // Proceed to SendRequest
1016        let mut call = call.proceed();
1017
1018        // Write the request headers
1019        let mut output = vec![0; 1024];
1020        let headers_n = call.write(&mut output).unwrap();
1021
1022        // Check if we can proceed
1023        assert!(call.can_proceed());
1024
1025        // Proceed to SendBody
1026        let call = call.proceed().unwrap().unwrap();
1027        let SendRequestResult::SendBody(mut call) = call else {
1028            panic!("Expected SendBody");
1029        };
1030
1031        // Write the body
1032        let (_, n1) = call.write(b"hallo", &mut output[headers_n..]).unwrap();
1033
1034        // Send end
1035        let (_, n2) = call.write(&[], &mut output[headers_n + n1..]).unwrap();
1036
1037        // Try to write after end
1038        let err = call.write(b"after end", &mut output[headers_n + n1 + n2..]);
1039
1040        assert_eq!(err, Err(Error::BodyContentAfterFinish));
1041    }
1042
1043    #[test]
1044    fn post_streaming_too_much() {
1045        let req = Request::post("http://f.test/page")
1046            .header("content-length", "5")
1047            .body(())
1048            .unwrap();
1049        let call = Call::new(req).unwrap();
1050
1051        // Proceed to SendRequest
1052        let mut call = call.proceed();
1053
1054        // Write the request headers
1055        let mut output = vec![0; 1024];
1056        let headers_n = call.write(&mut output).unwrap();
1057
1058        // Check if we can proceed
1059        assert!(call.can_proceed());
1060
1061        // Proceed to SendBody
1062        let call = call.proceed().unwrap().unwrap();
1063        let SendRequestResult::SendBody(mut call) = call else {
1064            panic!("Expected SendBody");
1065        };
1066
1067        // Write the body (first call)
1068        let (i1, n1) = call.write(b"hallo", &mut output[headers_n..]).unwrap();
1069
1070        // Verify the results
1071        assert_eq!(i1, 5); // In Call API, i1 is the number of bytes consumed from the input
1072        assert_eq!(n1, 5); // In Call API, n1 is the number of bytes written to the output
1073
1074        // Check if we can proceed (body is fully sent)
1075        assert!(call.can_proceed());
1076
1077        // Try to write more data after the body is fully sent (should fail with BodyContentAfterFinish)
1078        let err = call
1079            .write(b"hallo", &mut output[headers_n + n1..])
1080            .unwrap_err();
1081        assert_eq!(err, Error::BodyContentAfterFinish);
1082
1083        let s = str::from_utf8(&output[..headers_n + n1]).unwrap();
1084
1085        assert_eq!(
1086            s,
1087            "POST /page HTTP/1.1\r\nhost: f.test\r\ncontent-length: 5\r\n\r\nhallo"
1088        );
1089    }
1090
1091    #[test]
1092    fn username_password_uri() {
1093        let req = Request::get("http://martin:secret@f.test/page")
1094            .body(())
1095            .unwrap();
1096        let call = Call::new(req).unwrap();
1097
1098        // Proceed to SendRequest
1099        let mut call = call.proceed();
1100
1101        // Write the request headers
1102        let mut output = vec![0; 1024];
1103        let n = call.write(&mut output).unwrap();
1104
1105        let s = str::from_utf8(&output[..n]).unwrap();
1106
1107        assert_eq!(
1108            s,
1109            "GET /page HTTP/1.1\r\nhost: f.test\r\n\
1110            authorization: Basic bWFydGluOnNlY3JldA==\r\n\r\n"
1111        );
1112    }
1113
1114    #[test]
1115    fn username_uri() {
1116        let req = Request::get("http://martin@f.test/page").body(()).unwrap();
1117        let call = Call::new(req).unwrap();
1118
1119        // Proceed to SendRequest
1120        let mut call = call.proceed();
1121
1122        // Write the request headers
1123        let mut output = vec![0; 1024];
1124        let n = call.write(&mut output).unwrap();
1125
1126        let s = str::from_utf8(&output[..n]).unwrap();
1127
1128        assert_eq!(
1129            s,
1130            "GET /page HTTP/1.1\r\nhost: f.test\r\n\
1131            authorization: Basic bWFydGluOg==\r\n\r\n"
1132        );
1133    }
1134
1135    #[test]
1136    fn password_uri() {
1137        let req = Request::get("http://:secret@f.test/page").body(()).unwrap();
1138        let call = Call::new(req).unwrap();
1139
1140        // Proceed to SendRequest
1141        let mut call = call.proceed();
1142
1143        // Write the request headers
1144        let mut output = vec![0; 1024];
1145        let n = call.write(&mut output).unwrap();
1146
1147        let s = str::from_utf8(&output[..n]).unwrap();
1148
1149        assert_eq!(
1150            s,
1151            "GET /page HTTP/1.1\r\nhost: f.test\r\n\
1152            authorization: Basic OnNlY3JldA==\r\n\r\n"
1153        );
1154    }
1155
1156    #[test]
1157    fn override_auth_header() {
1158        let req = Request::get("http://martin:secret@f.test/page")
1159            // This should override the auth from the URI
1160            .header("authorization", "meh meh meh")
1161            .body(())
1162            .unwrap();
1163        let call = Call::new(req).unwrap();
1164
1165        // Proceed to SendRequest
1166        let mut call = call.proceed();
1167
1168        // Write the request headers
1169        let mut output = vec![0; 1024];
1170        let n = call.write(&mut output).unwrap();
1171
1172        let s = str::from_utf8(&output[..n]).unwrap();
1173
1174        assert_eq!(
1175            s,
1176            "GET /page HTTP/1.1\r\nhost: f.test\r\n\
1177            authorization: meh meh meh\r\n\r\n"
1178        );
1179    }
1180
1181    #[test]
1182    fn non_standard_port() {
1183        let req = Request::get("http://f.test:8080/page").body(()).unwrap();
1184        let call = Call::new(req).unwrap();
1185
1186        // Proceed to SendRequest
1187        let mut call = call.proceed();
1188
1189        // Write the request headers
1190        let mut output = vec![0; 1024];
1191        let n = call.write(&mut output).unwrap();
1192
1193        let s = str::from_utf8(&output[..n]).unwrap();
1194
1195        assert_eq!(s, "GET /page HTTP/1.1\r\nhost: f.test:8080\r\n\r\n");
1196    }
1197
1198    #[test]
1199    fn non_standard_method_not_allowed() {
1200        let m = Method::from_bytes(b"FNORD").unwrap();
1201
1202        let req = Request::builder()
1203            .method(m.clone())
1204            .uri("http://f.test:8080/page")
1205            .body(())
1206            .unwrap();
1207
1208        let call = Call::new(req).unwrap();
1209
1210        // Proceed to SendRequest
1211        let mut call = call.proceed();
1212
1213        // Try to write the request headers
1214        let mut output = vec![0; 1024];
1215        let err = call.write(&mut output).unwrap_err();
1216
1217        assert_eq!(err, Error::MethodVersionMismatch(m, Version::HTTP_11));
1218    }
1219
1220    #[test]
1221    fn non_standard_method_when_allowed() {
1222        let m = Method::from_bytes(b"FNORD").unwrap();
1223
1224        let req = Request::builder()
1225            .method(m.clone())
1226            .uri("http://f.test:8080/page")
1227            .body(())
1228            .unwrap();
1229
1230        let mut call = Call::new(req).unwrap();
1231
1232        // Allow non-standard methods
1233        call.allow_non_standard_methods(true);
1234
1235        // Proceed to SendRequest
1236        let mut call = call.proceed();
1237
1238        // Write the request headers
1239        let mut output = vec![0; 1024];
1240        let n = call.write(&mut output).unwrap();
1241
1242        let s = str::from_utf8(&output[..n]).unwrap();
1243
1244        assert_eq!(s, "FNORD /page HTTP/1.1\r\nhost: f.test:8080\r\n\r\n");
1245    }
1246
1247    #[test]
1248    fn ensure_reasonable_stack_sizes() {
1249        macro_rules! ensure {
1250            ($type:ty, $size:tt) => {
1251                let sz = std::mem::size_of::<$type>();
1252                assert!(
1253                    sz <= $size,
1254                    "Stack size of {} is too big {} > {}",
1255                    stringify!($type),
1256                    sz,
1257                    $size
1258                );
1259            };
1260        }
1261
1262        ensure!(http::Request<()>, 300); // 224
1263        ensure!(AmendedRequest, 400); // 368
1264        ensure!(Inner, 600); // 512
1265        ensure!(Call<SendRequest>, 600); // 512
1266    }
1267
1268    #[test]
1269    fn connect() {
1270        let req = Request::builder()
1271            .method(Method::CONNECT)
1272            .uri("http://example.com:80")
1273            .body(())
1274            .unwrap();
1275
1276        let call = Call::new(req).unwrap();
1277        let mut call = call.proceed();
1278
1279        let mut output = vec![0; 1024];
1280        let n = call.write(&mut output).unwrap();
1281
1282        // CONNECT request should have request target in authority form
1283        let s = str::from_utf8(&output[..n]).unwrap();
1284        assert_eq!(
1285            s,
1286            "CONNECT example.com:80 HTTP/1.1\r\nhost: example.com:80\r\n\r\n"
1287        );
1288
1289        // Should go to RecvResponse state (content-length/transfer-encoding is ignored with CONNECT)
1290        let SendRequestResult::RecvResponse(mut call) = call.proceed().unwrap().unwrap() else {
1291            panic!("Expected RecvResponse state")
1292        };
1293
1294        let input = b"HTTP/1.1 200 OK\r\n\r\n";
1295        let (n, res) = call.try_response(input, false).unwrap();
1296        assert_eq!(n, 19);
1297
1298        let Some(res) = res else {
1299            panic!("`try_response()` should return a response");
1300        };
1301
1302        assert!(res.headers().is_empty());
1303
1304        // should go to Cleanup state (content-length/transfer-encoding is ignored with CONNECT)
1305        let RecvResponseResult::Cleanup(_call) = call.proceed().unwrap() else {
1306            panic!("Expected Cleanup state")
1307        };
1308    }
1309
1310    #[test]
1311    fn connect_read_body() {
1312        let req = Request::builder()
1313            .method(Method::CONNECT)
1314            .uri("http://example.com:80")
1315            .body(())
1316            .unwrap();
1317
1318        let call = Call::new(req).unwrap();
1319        let mut call = call.proceed();
1320
1321        let mut output = vec![0; 1024];
1322        let n = call.write(&mut output).unwrap();
1323
1324        // CONNECT request should have request target in authority form
1325        let s = str::from_utf8(&output[..n]).unwrap();
1326        assert_eq!(
1327            s,
1328            "CONNECT example.com:80 HTTP/1.1\r\nhost: example.com:80\r\n\r\n"
1329        );
1330
1331        // Should go to RecvResponse state (content-length/transfer-encoding is ignored with CONNECT)
1332        let SendRequestResult::RecvResponse(mut call) = call.proceed().unwrap().unwrap() else {
1333            panic!("Expected RecvResponse state")
1334        };
1335
1336        call.force_recv_body();
1337
1338        let input = b"HTTP/1.1 200 OK\r\ncontent-length: 1024\r\n\r\n";
1339        let (_n, res) = call.try_response(input, false).unwrap();
1340
1341        let Some(_res) = res else {
1342            panic!("`try_response()` should return a response");
1343        };
1344
1345        let RecvResponseResult::RecvBody(_call) = call.proceed().unwrap() else {
1346            panic!("Expect RecvBody state");
1347        };
1348    }
1349
1350    #[test]
1351    fn connect_send_body_fails() {
1352        let req = Request::builder()
1353            .method(Method::CONNECT)
1354            .uri("http://example.com:80")
1355            .header("content-length", 1024)
1356            .body(())
1357            .unwrap();
1358
1359        let call = Call::new(req).unwrap();
1360        let mut call = call.proceed();
1361
1362        let mut output = vec![0; 1024];
1363        call.write(&mut output)
1364            .expect_err("no body allowed on CONNECT request");
1365    }
1366
1367    #[test]
1368    fn connect_send_body_with_footgun() {
1369        let req = Request::builder()
1370            .method(Method::CONNECT)
1371            .uri("http://example.com:80")
1372            .header("content-length", 1024)
1373            .body(())
1374            .unwrap();
1375
1376        let mut call = Call::new(req).unwrap();
1377
1378        call.force_send_body();
1379        let mut call = call.proceed();
1380
1381        let mut output = vec![0; 1024];
1382        let n = call.write(&mut output).unwrap();
1383
1384        // CONNECT request should have request target in authority form
1385        let s = str::from_utf8(&output[..n]).unwrap();
1386        assert_eq!(
1387            s,
1388            "CONNECT example.com:80 HTTP/1.1\r\nhost: example.com:80\r\ncontent-length: 1024\r\n\r\n"
1389        );
1390
1391        let SendRequestResult::SendBody(_call) = call.proceed().unwrap().unwrap() else {
1392            panic!("Expected SendBody state")
1393        };
1394    }
1395
1396    #[test]
1397    fn query_no_slash() {
1398        let req = Request::get("http://foo.test?query=foo").body(()).unwrap();
1399
1400        let call = Call::new(req).unwrap();
1401
1402        let mut call = call.proceed();
1403
1404        let mut output = vec![0; 1024];
1405        let n = call.write(&mut output).unwrap();
1406        let s = str::from_utf8(&output[..n]).unwrap();
1407
1408        // The added / here is to be compliant with RFC 9112 §3.2.1
1409        assert_eq!(
1410            s,
1411            "GET /?query=foo HTTP/1.1\r\n\
1412            host: foo.test\r\n\
1413            \r\n"
1414        );
1415    }
1416}