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}