Skip to main content

ClientConn

Struct ClientConn 

Source
pub struct ClientConn { /* private fields */ }
Expand description

The client side of one HTTP/1.1 connection: a sans-IO state machine.

By default one request is outstanding at a time; with Limits::with_pipeline_depth, idempotent requests may be pipelined behind it (see can_send). send_request serializes a Request; feed and poll parse the response. When the response allows it, the connection returns to ClientEvent::Idle and can send the next request (keep-alive).

The response is framed with the request in mind: a HEAD request’s response has no body whatever its headers say, a CONNECT 2xx becomes a tunnel, and a response with neither Content-Length nor Transfer-Encoding runs until the server closes.

§Examples

use http_net::{ClientConn, ClientEvent, Request};

let mut conn = ClientConn::new();
let mut out = Vec::new();
conn.send_request(&Request::get("/a").header("host", "h"), &mut out).unwrap();
assert_eq!(out, b"GET /a HTTP/1.1\r\nhost: h\r\n\r\n");
let _ = conn.feed(b"HTTP/1.1 204 No Content\r\n\r\n");
assert!(matches!(conn.poll().unwrap(), ClientEvent::Response(_)));
assert!(matches!(conn.poll().unwrap(), ClientEvent::ResponseEnd));
assert!(matches!(conn.poll().unwrap(), ClientEvent::Idle));

Implementations§

Source§

impl ClientConn

Source

pub fn new() -> Self

A connection with the default Limits.

§Examples
let mut conn = http_net::ClientConn::new();
assert!(matches!(conn.poll(), Ok(http_net::ClientEvent::Idle)));
Source

pub fn with_limits(limits: Limits) -> Self

A connection enforcing limits on responses.

§Examples
let conn = http_net::ClientConn::with_limits(http_net::Limits::new().with_max_body(1 << 24));
assert_eq!(conn.limits().max_body(), 1 << 24);
Source

pub fn with_buffer(storage: Vec<u8>, limits: Limits) -> Self

A connection enforcing limits that reads into storage (its capacity is used up to Limits::buffer_capacity, its contents are ignored), for drivers that pool buffers. Give it back with into_buffer.

§Examples
let conn = http_net::ClientConn::with_buffer(Vec::with_capacity(4096), http_net::Limits::new());
assert!(conn.into_buffer().capacity() >= 4096);
Source

pub fn limits(&self) -> &Limits

The limits this connection enforces.

§Examples
assert_eq!(http_net::ClientConn::new().limits().max_headers(), 100);
Source

pub fn feed(&mut self, bytes: &[u8]) -> usize

Copies as much of bytes into the input buffer as fits; returns how many were taken (see ServerConn::feed).

§Examples
let mut conn = http_net::ClientConn::new();
assert_eq!(conn.feed(b"HTTP/1.1"), 8);
Source

pub fn read_buf(&mut self) -> &mut [u8] ⓘ

Spare space at the end of the input buffer; follow with commit.

§Examples
let mut conn = http_net::ClientConn::new();
assert!(!conn.read_buf().is_empty());
Source

pub fn commit(&mut self, n: usize)

Records that n bytes were written into read_buf. An n larger than the slice read_buf returned is clamped to its length: committing bytes that were not written would hand stale buffer contents to the parser.

§Examples
let mut conn = http_net::ClientConn::new();
conn.read_buf()[..4].copy_from_slice(b"HTTP");
conn.commit(4);
Source

pub fn feed_eof(&mut self)

Records that the server closed its sending side: ends a body that runs until close, or reports HttpError::UnexpectedEof mid-message.

§Examples
use http_net::{ClientConn, ClientEvent, Request};

let mut conn = ClientConn::new();
conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new()).unwrap();
let _ = conn.feed(b"HTTP/1.0 200 OK\r\n\r\nall of it");
assert!(matches!(conn.poll().unwrap(), ClientEvent::Response(_)));
assert!(matches!(conn.poll().unwrap(), ClientEvent::Body(b"all of it")));
assert!(matches!(conn.poll().unwrap(), ClientEvent::NeedInput));
conn.feed_eof();
assert!(matches!(conn.poll().unwrap(), ClientEvent::ResponseEnd));
assert!(matches!(conn.poll().unwrap(), ClientEvent::Closed));
Source

pub fn send_request( &mut self, request: &Request, out: &mut Vec<u8>, ) -> Result<(), HttpError>

Writes request into out and starts waiting for its response.

A full body is sent with content-length (omitted for an empty body on methods that do not expect one); a streamed body continues with send_body and end_body.

A request built with Request::expect_continue is sent head first: is_waiting_for_continue is true until the server answers 100 Continue (reported as ClientEvent::Continue: then send the body), a final status (then the body must not be sent and the connection closes after the response), or the caller sends the body anyway (RFC 9110 section 10.1.1 allows it after a wait of its choosing).

§Errors
  • the first error recorded by the Request builder;
  • HttpError::MissingHost when the request has no host header;
  • HttpError::InvalidState when a request is outstanding and this one cannot be pipelined (depth 1, depth full, or not idempotent with a full body; see can_send), when the connection is closed or upgraded, or when an expect_continue request has a full body (it needs content_length or chunked);
  • HttpError::UnexpectedData when response bytes nobody asked for are buffered.
§Examples
use http_net::{ClientConn, HttpError, Request};

let mut conn = ClientConn::new();
let mut out = Vec::new();
assert_eq!(conn.send_request(&Request::get("/"), &mut out), Err(HttpError::MissingHost));
conn.send_request(&Request::post("/f").header("host", "a").body(&b"xyz"[..]), &mut out).unwrap();
assert_eq!(out, b"POST /f HTTP/1.1\r\nhost: a\r\ncontent-length: 3\r\n\r\nxyz");
Source

pub fn outstanding(&self) -> usize

The number of requests sent whose response has not completed: 0 when idle, up to Limits::pipeline_depth when pipelining. After the connection closed it is the number of requests that never got a response (send them again on a new connection if they are safe to repeat).

§Examples
use http_net::{ClientConn, Limits, Request};

let mut conn = ClientConn::with_limits(Limits::new().with_pipeline_depth(4));
let get = |p: &str| Request::get(p).header("host", "a");
let mut out = Vec::new();
conn.send_request(&get("/1"), &mut out)?;
conn.send_request(&get("/2"), &mut out)?;
assert_eq!(conn.outstanding(), 2);
Source

pub fn can_send(&self) -> bool

Whether send_request would accept another request now. With no request outstanding: the connection is open and nothing unsolicited is buffered. With requests outstanding (pipelining): fewer than Limits::pipeline_depth are outstanding, all of them are idempotent with their bodies fully sent, none asked to close, upgrade, CONNECT, or expect 100 Continue, and the response being read, once its head has arrived, keeps the connection open (RFC 9112 section 9.6: after close, or a response that ends with the connection, the client sends nothing more). The new request must also be idempotent with a full body (RFC 9112 section 9.3.2: a client should not pipeline non-idempotent requests).

§Examples
use http_net::{ClientConn, Limits, Request};

let mut conn = ClientConn::new(); // depth 1: no pipelining
assert!(conn.can_send());
conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new())?;
assert!(!conn.can_send());
Source

pub fn reset(&mut self)

Returns the connection to its initial state for a new socket, keeping the buffer and field-index allocations and the limits and reporting settings. Requests still outstanding are forgotten.

§Examples
use http_net::{ClientConn, Progress, Request};

let mut conn = ClientConn::new();
conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new())?;
conn.reset();
assert_eq!(conn.progress(), Progress::Idle);
assert!(conn.can_send());
Source

pub fn into_buffer(self) -> Vec<u8> ⓘ

Gives the input buffer’s storage back, for a pool.

§Examples
assert_eq!(http_net::ClientConn::new().into_buffer().capacity(), 0);
Source

pub fn shrink_idle(&mut self) -> bool

Frees the input buffer and the field indexes while no request is outstanding and nothing is buffered. Returns whether memory was released.

§Examples
use http_net::{ClientConn, ClientEvent, Request};

let mut conn = ClientConn::new();
conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new())?;
let _ = conn.feed(b"HTTP/1.1 204 No Content\r\n\r\n");
while !matches!(conn.poll()?, ClientEvent::Idle) {}
assert!(conn.shrink_idle());
Source

pub fn send_body( &mut self, data: &[u8], out: &mut Vec<u8>, ) -> Result<(), HttpError>

Writes body bytes of a streamed request.

Sending body bytes while waiting for 100 Continue ends the wait.

§Errors

HttpError::BodyLengthMismatch past the declared length; HttpError::InvalidState when no streamed request is in progress, or when the server answered an expect_continue request with a final status (the body must not be sent then).

§Examples
use http_net::{ClientConn, Request};

let mut conn = ClientConn::new();
let mut out = Vec::new();
conn.send_request(&Request::post("/up").header("host", "a").chunked(), &mut out).unwrap();
conn.send_body(b"part", &mut out).unwrap();
conn.end_body(&mut out).unwrap();
assert!(out.ends_with(b"4\r\npart\r\n0\r\n\r\n"));
Source

pub fn end_body(&mut self, out: &mut Vec<u8>) -> Result<(), HttpError>

Ends a streamed request body.

§Errors

HttpError::BodyLengthMismatch when fewer bytes than declared were sent; HttpError::InvalidState when no streamed request is in progress, or when the server answered an expect_continue request with a final status (the body must not be sent then).

§Examples
use http_net::{ClientConn, Request};

let mut conn = ClientConn::new();
let mut out = Vec::new();
conn.send_request(&Request::post("/").header("host", "a").content_length(2), &mut out).unwrap();
conn.send_body(b"ok", &mut out).unwrap();
conn.end_body(&mut out).unwrap();
assert!(out.ends_with(b"\r\n\r\nok"));
Source

pub fn end_body_with_trailers( &mut self, trailers: &[(&str, &[u8])], out: &mut Vec<u8>, ) -> Result<(), HttpError>

Ends a streamed chunked request body with a trailer section (see ServerConn::end_body_with_trailers for the fields that are refused).

§Errors

HttpError::TrailerNotAllowed for a forbidden field or a body that is not chunked; the field errors of Request::header; otherwise as end_body.

§Examples
use http_net::{ClientConn, Request};

let mut conn = ClientConn::new();
let mut out = Vec::new();
conn.send_request(&Request::put("/f").header("host", "a").chunked(), &mut out)?;
conn.send_body(b"data", &mut out)?;
conn.end_body_with_trailers(&[("content-digest", b"sha-256=:x:")], &mut out)?;
assert!(out.ends_with(b"0\r\ncontent-digest: sha-256=:x:\r\n\r\n"));
Source

pub fn poll(&mut self) -> Result<ClientEvent<'_>, HttpError>

Advances the response parser and reports what happened.

§Errors

Any HttpError parse error; the connection is then closed and must not be reused.

§Examples
use http_net::{ClientConn, HttpError, Request};

let mut conn = ClientConn::new();
conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new()).unwrap();
let _ = conn.feed(b"HTTP/1.1 200 OK\r\nContent-Length: 1\r\nContent-Length: 1\r\n\r\nx");
assert_eq!(conn.poll().unwrap_err(), HttpError::DuplicateContentLength);
Source

pub fn response(&self) -> Option<ResponseHead<'_>>

The current response’s head, from its arrival until the connection moves on from it: the next poll after ResponseEnd or the next send_request (still available after an upgrade).

§Examples
use http_net::{ClientConn, ClientEvent, Request};

let mut conn = ClientConn::new();
conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new()).unwrap();
let _ = conn.feed(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
while !matches!(conn.poll().unwrap(), ClientEvent::ResponseEnd) {}
assert_eq!(conn.response().unwrap().status(), 200);
Source

pub fn is_upgraded(&self) -> bool

Whether the server switched protocols and the connection now belongs to another protocol.

§Examples
assert!(!http_net::ClientConn::new().is_upgraded());
Source

pub fn remaining_input(&self) -> &[u8] ⓘ

Input that arrived after the last message parsed: after ClientEvent::Upgraded, the first bytes of the new protocol.

§Examples
use http_net::{ClientConn, ClientEvent, Request};

let mut conn = ClientConn::new();
conn.send_request(&Request::get("/").header("host", "a").upgrade("x"), &mut Vec::new()).unwrap();
let _ = conn.feed(b"HTTP/1.1 101 Switching Protocols\r\nConnection: upgrade\r\nUpgrade: x\r\n\r\nhi");
assert!(matches!(conn.poll().unwrap(), ClientEvent::Response(_)));
assert!(matches!(conn.poll().unwrap(), ClientEvent::Upgraded));
assert_eq!(conn.remaining_input(), b"hi");
Source

pub fn set_report_informational(&mut self, report: bool)

When true, each interim response other than 101 (for example 103 Early Hints) is reported as ClientEvent::Informational before the final response; when false (the default) they are skipped. Interim responses count against Limits::max_interim either way.

§Examples
use http_net::{ClientConn, ClientEvent, Request};

let mut conn = ClientConn::new();
conn.set_report_informational(true);
conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new())?;
let _ = conn.feed(b"HTTP/1.1 103 Early Hints\r\nLink: </a.css>; rel=preload\r\n\r\nHTTP/1.1 204 No Content\r\n\r\n");
let ClientEvent::Informational(hints) = conn.poll()? else { panic!() };
assert_eq!(hints.status(), 103);
assert_eq!(hints.header("link"), Some(&b"</a.css>; rel=preload"[..]));
assert!(matches!(conn.poll()?, ClientEvent::Response(r) if r.status() == 204));
Source

pub fn set_report_trailers(&mut self, report: bool)

When true, a response’s trailer fields are kept (bounded by the trailer budgets of Limits) and reported as ClientEvent::Trailers just before ClientEvent::ResponseEnd; when false (the default) they are validated and discarded.

§Examples
use http_net::{ClientConn, ClientEvent, Request};

let mut conn = ClientConn::new();
conn.set_report_trailers(true);
conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new())?;
let _ = conn.feed(b"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n2\r\nok\r\n0\r\nServer-Timing: db;dur=5\r\n\r\n");
let mut saw = false;
loop {
    match conn.poll()? {
        ClientEvent::Trailers(t) => saw = t.len() == 1,
        ClientEvent::ResponseEnd => break,
        _ => {}
    }
}
assert!(saw);
Source

pub fn set_leniency(&mut self, leniency: ClientLeniency)

Sets the opt-in tolerances for responses (see ClientLeniency); the default is strict. Takes effect from the next response head.

§Examples
use http_net::{ClientConn, ClientLeniency, HttpError, Request};

let folded = b"HTTP/1.1 200 OK\r\nX: a\r\n b\r\nContent-Length: 0\r\n\r\n";
let mut strict = ClientConn::new();
strict.send_request(&Request::get("/").header("host", "a"), &mut Vec::new())?;
let _ = strict.feed(folded);
assert_eq!(strict.poll().unwrap_err(), HttpError::ObsFold);

let mut lenient = ClientConn::new();
lenient.set_leniency(ClientLeniency::new().with_obs_fold_in_responses(true));
lenient.send_request(&Request::get("/").header("host", "a"), &mut Vec::new())?;
let _ = lenient.feed(folded);
assert!(lenient.poll().is_ok());
Source

pub fn is_waiting_for_continue(&self) -> bool

Whether a request built with Request::expect_continue is still waiting: no 100 Continue, no final response, and no body bytes sent yet. A client may stop waiting on its own timer and send the body anyway (RFC 9110 section 10.1.1).

§Examples
use http_net::{ClientConn, ClientEvent, Request};

let mut conn = ClientConn::new();
let mut out = Vec::new();
let upload = Request::put("/big").header("host", "a").content_length(4).expect_continue();
conn.send_request(&upload, &mut out)?;
assert!(conn.is_waiting_for_continue());
let _ = conn.feed(b"HTTP/1.1 100 Continue\r\n\r\n");
assert!(matches!(conn.poll()?, ClientEvent::Continue));
assert!(!conn.is_waiting_for_continue());
conn.send_body(b"data", &mut out)?;
conn.end_body(&mut out)?;
Source

pub fn is_reusable(&self) -> bool

Whether the connection can carry another request now: no request is outstanding (or the last response completed) and both sides allow keep-alive. With pipelining, can_send says whether another request can be sent while some are outstanding.

§Examples
use http_net::{ClientConn, ClientEvent, Request};

let mut conn = ClientConn::new();
assert!(conn.is_reusable());
conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new())?;
assert!(!conn.is_reusable());
let _ = conn.feed(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
while !matches!(conn.poll()?, ClientEvent::ResponseEnd) {}
assert!(conn.is_reusable());
Source

pub fn progress(&self) -> Progress

The connection’s phase, for response deadlines: Progress::Idle with no request outstanding, Progress::WritingBody while a request body is streamed, Progress::AwaitingResponse until the first byte of the response, then Progress::Head and Progress::Body.

§Examples
use http_net::{ClientConn, Progress, Request};

let mut conn = ClientConn::new();
assert_eq!(conn.progress(), Progress::Idle);
conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new())?;
assert_eq!(conn.progress(), Progress::AwaitingResponse);

Trait Implementations§

Source§

impl Debug for ClientConn

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for ClientConn

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.