#[non_exhaustive]pub struct HttpResponse {
pub status: u16,
pub headers: Headers,
/* private fields */
}Expand description
An HTTP response: status and headers, then a streamed body.
Fields (Non-exhaustive)§
This struct is marked as non-exhaustive
Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.status: u16Status code.
headers: HeadersHeaders.
Implementations§
Source§impl HttpResponse
impl HttpResponse
Sourcepub fn from_bytes(status: u16, headers: Headers, body: impl Into<Bytes>) -> Self
pub fn from_bytes(status: u16, headers: Headers, body: impl Into<Bytes>) -> Self
A response with the whole body at once.
Sourcepub fn is_success(&self) -> bool
pub fn is_success(&self) -> bool
Returns true for 2xx statuses.
Sourcepub async fn bytes(self) -> Result<Bytes, TransportError>
pub async fn bytes(self) -> Result<Bytes, TransportError>
Read the whole body, however large.
For a response from a server you do not control, read with
bytes_limited instead: a broken or hostile
server can send a body that does not fit in memory.
§Errors
The transport failed while the body was read.
Sourcepub async fn text(self) -> Result<String, TransportError>
pub async fn text(self) -> Result<String, TransportError>
Read the whole body as text (invalid UTF-8 is replaced), however
large. See bytes; prefer
text_limited.
§Errors
The transport failed while the body was read.
Sourcepub async fn bytes_limited(self, limit: usize) -> Result<Bytes, TransportError>
pub async fn bytes_limited(self, limit: usize) -> Result<Bytes, TransportError>
Read the whole body, failing as soon as it passes limit bytes. The
bundled providers read success bodies with it, under the builder’s
max_response_size (default DEFAULT_MAX_RESPONSE_SIZE).
The buffer never grows past limit: the chunk that would take it
past is refused before it is copied, and the rest of the body is
never read.
use allama_http::{Headers, HttpResponse, TransportErrorKind};
let response = HttpResponse::from_bytes(200, Headers::new(), "0123456789");
let err = response.bytes_limited(4).await.unwrap_err();
assert_eq!(err.kind(), TransportErrorKind::TooLarge);
assert!(matches!(err.into_error(), allama::Error::UnexpectedResponse { .. }));§Errors
- A
TransportErrorKind::TooLargeerror once the body passeslimit, with an authored message. It maps toallama::Error::UnexpectedResponse, which is not retryable: asking again returns the same body. - The transport’s error when reading the body failed.
Sourcepub async fn text_limited(self, limit: usize) -> Result<String, TransportError>
pub async fn text_limited(self, limit: usize) -> Result<String, TransportError>
Read the whole body as text (invalid UTF-8 is replaced), failing as
soon as it passes limit bytes, as
bytes_limited does.
§Errors
See bytes_limited.
Sourcepub async fn map_error(
self,
map: impl FnOnce(u16, &Headers, &str) -> Error,
) -> Error
pub async fn map_error( self, map: impl FnOnce(u16, &Headers, &str) -> Error, ) -> Error
The error for an error response (one that is not a success): reads
the start of its body for diagnostics and maps the status, headers
and body text with map, the adapter’s own classification.
Every bundled HTTP provider maps its error responses here, so these rules hold on all of them:
- The body read is bounded. At most
MAX_ERROR_BODY_SIZEbytes are read, withinERROR_BODY_TIMEOUT; the rest of the body is never read. An error keeps only the firstErrorContext::MAX_BODY_BYTESof it anyway, and a streaming request has no total deadline, so an unbounded read of a body that never ends would hang the call and grow without limit. - A failed read is recorded, not hidden.
mapgets the text read before the failure, and the failure (a transport error, or the time bound) is added to the error’s upstream message. - A redirect is never followed. A
3xxis theredirect_errorfor the response, andmapis not called: an LLM API never answers a request with a redirect, and following one would send the request, credentials included, somewhere the application never configured.
use allama::Error;
use allama_http::{HttpResponse, Headers, error_context, status_error};
let map = |status: u16, headers: &Headers, body: &str| {
status_error(status, format!("HTTP {status}"))
.with_context(error_context(status, headers))
.authored()
.with_body(body.to_owned())
};
let response = HttpResponse::from_bytes(429, Headers::new(), "slow down");
let err = response.map_error(map).await;
assert!(matches!(err, Error::RateLimited { .. }));
assert_eq!(err.context().and_then(|c| c.capped_body()), Some("slow down"));
let headers = [("location", "https://elsewhere.example/v1/chat")].into_iter().collect();
let redirect = HttpResponse::from_bytes(307, headers, "");
let err = redirect.map_error(map).await;
assert!(matches!(err, Error::UnexpectedResponse { .. }));
assert!(err.to_string().contains("elsewhere.example"));