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
impl ClientConn
Sourcepub fn with_limits(limits: Limits) -> Self
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);Sourcepub fn with_buffer(storage: Vec<u8>, limits: Limits) -> Self
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);Sourcepub fn limits(&self) -> &Limits
pub fn limits(&self) -> &Limits
The limits this connection enforces.
§Examples
assert_eq!(http_net::ClientConn::new().limits().max_headers(), 100);Sourcepub fn feed(&mut self, bytes: &[u8]) -> usize
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);Sourcepub fn commit(&mut self, n: usize)
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);Sourcepub fn feed_eof(&mut self)
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));Sourcepub fn send_request(
&mut self,
request: &Request,
out: &mut Vec<u8>,
) -> Result<(), HttpError>
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
Requestbuilder; HttpError::MissingHostwhen the request has nohostheader;HttpError::InvalidStatewhen a request is outstanding and this one cannot be pipelined (depth 1, depth full, or not idempotent with a full body; seecan_send), when the connection is closed or upgraded, or when anexpect_continuerequest has a full body (it needscontent_lengthorchunked);HttpError::UnexpectedDatawhen 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");Sourcepub fn outstanding(&self) -> usize
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);Sourcepub fn can_send(&self) -> bool
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());Sourcepub fn reset(&mut self)
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());Sourcepub fn into_buffer(self) -> Vec<u8> ⓘ
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);Sourcepub fn shrink_idle(&mut self) -> bool
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());Sourcepub fn send_body(
&mut self,
data: &[u8],
out: &mut Vec<u8>,
) -> Result<(), HttpError>
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"));Sourcepub fn end_body(&mut self, out: &mut Vec<u8>) -> Result<(), HttpError>
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"));Sourcepub fn end_body_with_trailers(
&mut self,
trailers: &[(&str, &[u8])],
out: &mut Vec<u8>,
) -> Result<(), HttpError>
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"));Sourcepub fn poll(&mut self) -> Result<ClientEvent<'_>, HttpError>
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);Sourcepub fn response(&self) -> Option<ResponseHead<'_>>
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);Sourcepub fn is_upgraded(&self) -> bool
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());Sourcepub fn remaining_input(&self) -> &[u8] ⓘ
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");Sourcepub fn set_report_informational(&mut self, report: bool)
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));Sourcepub fn set_report_trailers(&mut self, report: bool)
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);Sourcepub fn set_leniency(&mut self, leniency: ClientLeniency)
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());Sourcepub fn is_waiting_for_continue(&self) -> bool
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)?;Sourcepub fn is_reusable(&self) -> bool
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());Sourcepub fn progress(&self) -> Progress
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);