Coverage Report

Created: 2026-09-04 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/ureq-proto-0.6.1/src/client/mod.rs
Line
Count
Source
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
224
use std::fmt;
225
use std::marker::PhantomData;
226
227
use http::{HeaderValue, StatusCode};
228
229
use crate::CloseReason;
230
use crate::body::{BodyReader, BodyWriter};
231
use crate::util::ArrayVec;
232
233
use amended::AmendedRequest;
234
235
mod amended;
236
237
// mod holder;
238
239
#[cfg(test)]
240
mod test;
241
242
/// Max number of headers to parse from an HTTP response
243
pub 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.
249
pub 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
0
                fn name() -> &'static str {
260
0
                    stringify!($n)
261
0
                }
Unexecuted instantiation: <ureq_proto::client::state::Prepare as ureq_proto::client::state::Named>::name
Unexecuted instantiation: <ureq_proto::client::state::Await100 as ureq_proto::client::state::Named>::name
Unexecuted instantiation: <ureq_proto::client::state::SendBody as ureq_proto::client::state::Named>::name
Unexecuted instantiation: <ureq_proto::client::state::RecvResponse as ureq_proto::client::state::Named>::name
Unexecuted instantiation: <ureq_proto::client::state::RecvBody as ureq_proto::client::state::Named>::name
Unexecuted instantiation: <ureq_proto::client::state::Redirect as ureq_proto::client::state::Named>::name
Unexecuted instantiation: <ureq_proto::client::state::Cleanup as ureq_proto::client::state::Named>::name
Unexecuted instantiation: <ureq_proto::client::state::SendRequest as ureq_proto::client::state::Named>::name
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
}
275
use 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.
287
pub 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)]
298
pub(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
310
impl Inner {
311
0
    fn is_redirect(&self) -> bool {
312
0
        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
0
            Some(v) => v.is_redirection() && v != StatusCode::NOT_MODIFIED,
316
0
            None => false,
317
        }
318
0
    }
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)]
326
pub(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
334
impl BodyState {
335
0
    fn need_response_body(&self) -> bool {
336
0
        !matches!(
337
0
            self.reader,
338
            Some(BodyReader::NoBody) | Some(BodyReader::LengthDelimited(0))
339
        )
340
0
    }
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)]
350
enum RequestPhase {
351
    #[default]
352
    Line,
353
    Headers(usize),
354
    Body,
355
}
356
357
impl RequestPhase {
358
0
    fn is_prelude(&self) -> bool {
359
0
        matches!(self, RequestPhase::Line | RequestPhase::Headers(_))
360
0
    }
361
362
0
    fn is_body(&self) -> bool {
363
0
        matches!(self, RequestPhase::Body)
364
0
    }
365
}
366
367
impl<S> Call<S> {
368
0
    fn wrap(inner: Inner) -> Call<S>
369
0
    where
370
0
        S: Named,
371
    {
372
0
        let wrapped = Call {
373
0
            inner,
374
0
            _ph: PhantomData,
375
0
        };
376
377
0
        debug!("{:?}", wrapped);
378
379
0
        wrapped
380
0
    }
Unexecuted instantiation: <ureq_proto::client::Call<ureq_proto::client::state::SendRequest>>::wrap
Unexecuted instantiation: <ureq_proto::client::Call<ureq_proto::client::state::RecvResponse>>::wrap
Unexecuted instantiation: <ureq_proto::client::Call<ureq_proto::client::state::Cleanup>>::wrap
Unexecuted instantiation: <ureq_proto::client::Call<ureq_proto::client::state::Prepare>>::wrap
Unexecuted instantiation: <ureq_proto::client::Call<ureq_proto::client::state::Await100>>::wrap
Unexecuted instantiation: <ureq_proto::client::Call<ureq_proto::client::state::RecvBody>>::wrap
Unexecuted instantiation: <ureq_proto::client::Call<ureq_proto::client::state::Redirect>>::wrap
Unexecuted instantiation: <ureq_proto::client::Call<ureq_proto::client::state::SendBody>>::wrap
381
382
    #[cfg(test)]
383
    pub(crate) fn inner(&self) -> &Inner {
384
        &self.inner
385
    }
386
}
387
388
// //////////////////////////////////////////////////////////////////////////////////////////// PREPARE
389
390
mod prepare;
391
392
// //////////////////////////////////////////////////////////////////////////////////////////// SEND REQUEST
393
394
mod 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.
404
pub 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
417
mod 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.
426
pub 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
436
mod sendbody;
437
438
// //////////////////////////////////////////////////////////////////////////////////////////// RECV RESPONSE
439
440
mod 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.
450
pub 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
463
mod 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.
472
pub enum RecvBodyResult {
473
    /// Follow a redirect
474
    Redirect(Call<Redirect>),
475
476
    /// Go to cleanup
477
    Cleanup(Call<Cleanup>),
478
}
479
480
// //////////////////////////////////////////////////////////////////////////////////////////// REDIRECT
481
482
mod 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]
495
pub 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
506
impl Call<Cleanup> {
507
    /// Tell if we must close the connection.
508
0
    pub fn must_close_connection(&self) -> bool {
509
0
        self.close_reason().is_some()
510
0
    }
511
512
    /// If we are closing the connection, give a reason.
513
0
    pub fn close_reason(&self) -> Option<&'static str> {
514
0
        self.inner.close_reason.first().map(|s| s.explain())
515
0
    }
516
}
517
518
// ////////////////////////////////////////////////////////////////////////////////////////////
519
520
impl<State: Named> fmt::Debug for Call<State> {
521
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
522
0
        write!(f, "Call<{}>", State::name())
523
0
    }
Unexecuted instantiation: <ureq_proto::client::Call<ureq_proto::client::state::SendRequest> as core::fmt::Debug>::fmt
Unexecuted instantiation: <ureq_proto::client::Call<ureq_proto::client::state::RecvResponse> as core::fmt::Debug>::fmt
Unexecuted instantiation: <ureq_proto::client::Call<ureq_proto::client::state::Cleanup> as core::fmt::Debug>::fmt
Unexecuted instantiation: <ureq_proto::client::Call<ureq_proto::client::state::Prepare> as core::fmt::Debug>::fmt
Unexecuted instantiation: <ureq_proto::client::Call<ureq_proto::client::state::Await100> as core::fmt::Debug>::fmt
Unexecuted instantiation: <ureq_proto::client::Call<ureq_proto::client::state::RecvBody> as core::fmt::Debug>::fmt
Unexecuted instantiation: <ureq_proto::client::Call<ureq_proto::client::state::Redirect> as core::fmt::Debug>::fmt
Unexecuted instantiation: <ureq_proto::client::Call<ureq_proto::client::state::SendBody> as core::fmt::Debug>::fmt
524
}
525
526
impl fmt::Debug for RequestPhase {
527
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
528
0
        match self {
529
0
            Self::Line => write!(f, "SendLine"),
530
0
            Self::Headers(_) => write!(f, "SendHeaders"),
531
0
            Self::Body => write!(f, "SendBody"),
532
        }
533
0
    }
534
}
535
536
#[cfg(test)]
537
mod tests {
538
    use super::*;
539
    use crate::Error;
540
    use crate::client::Inner;
541
    use crate::client::amended::AmendedRequest;
542
    use crate::client::state::SendRequest;
543
    use http::{Method, Request, Version, header};
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
}