Responses¶
The base response and every response class shipped with the framework.
Response
¶
Base HTTP response.
Usage::
from veloce import Response
async def handler(request):
return Response(body=b"hello", content_type="text/plain")
is_json
property
¶
True when Content-Type is JSON.
Matches application/json and any application/*+json
structured suffix (RFC 6839 Sec. 3.1).
mimetype
property
writable
¶
The bare media type - Content-Type without parameters.
text/html; charset=utf-8 -> text/html. Lower-cased and
stripped per RFC 9110 Sec. 8.3 (media types are case-insensitive).
status
property
writable
¶
Full HTTP status line, e.g. "200 OK".
Assignable: accepts an int (200), a bare numeric string
("200"), or a full status line ("200 OK" / "404 Not
Found"). The leading integer is parsed into status_code.
content_length
property
¶
Length of the response body in bytes.
Always derived from len(body). Streaming responses (which
don't materialise the body) return 0 here; see is_streamed.
charset
property
writable
¶
Response charset from Content-Type.
Falls back to "utf-8" when no charset parameter is present.
Assignable: setting it rewrites the charset= parameter on the
existing Content-Type (the bare media type is preserved).
mimetype_params
property
¶
Parameters of the Content-Type header.
Everything after the bare media type, as a dict of lower-cased
parameter names to their (unquoted) values. For
text/html; charset=utf-8 this is {"charset": "utf-8"}.
Returns an empty dict when no parameters are present.
last_modified
property
writable
¶
Parsed Last-Modified header -> UTC datetime or None.
Accepts the three RFC 9110 Sec. 5.6.7 HTTP-date
forms. Returns None on missing/unparseable.
expires
property
writable
¶
Parsed Expires header -> UTC datetime or None (RFC 9111 Sec. 5.3).
cookies
property
¶
Parsed cookie jar from this response's Set-Cookie header(s).
Walks every Set-Cookie entry (Q44 separator \r\nSet-Cookie:
respected) and returns {name: value}. Values are percent-decoded,
so a cookie reads back as the string set_cookie() was given rather
than its wire form. Multiple cookies with the same name resolve to
the last set - matches the wire behaviour where the client also keeps
the most-recent value. Caller introspection only; mutation goes
through set_cookie().
headerlist
property
¶
Headers flattened to the (name, value) tuple list the wire emit sends.
Each Set-Cookie (Q44 multi-cookie join) expands to its own tuple, so
the caller gets the per-cookie view ASGI requires. Two spellings of one
field name collapse to a single entry carrying the last name and value
seen, at the position of the first (RFC 9110 Sec. 5.1 makes field names
case-insensitive), and a CR/LF/NUL anywhere in a name or value raises
ValueError rather than being handed back - both matching the emit
paths. Response.headers remains the raw, unfolded view.
data
property
writable
¶
Body bytes alias for Response.body.
Read returns the current body; writing through the setter
replaces the body, invalidates any cached HTTP/1.1 encoded
bytes (_encoded), and updates Content-Length on the
headers if it was previously set.
vary
property
writable
¶
The Vary header as a HeaderSet.
Returns a fresh HeaderSet parsed from the current header.
Assign a HeaderSet, iterable of strings, or a comma-separated
string to replace it. Mutating the returned object does not
write back - call add_vary(...) or reassign for that.
allow
property
writable
¶
The Allow header as a HeaderSet.
Lists the HTTP methods the resource supports (RFC 9110 Sec. 10.2.1).
Assign a HeaderSet, iterable, or comma-separated string.
www_authenticate
property
writable
¶
The WWW-Authenticate challenge header - RFC 9110 Sec. 11.6.1.
Sent on 401 Unauthorized to tell the client which auth
scheme(s) to use. None when unset.
content_encoding
property
writable
¶
The Content-Encoding header - RFC 9110 Sec. 8.4. None when unset.
content_language
property
writable
¶
The Content-Language header - RFC 9110 Sec. 8.5. None when unset.
accept_ranges
property
writable
¶
The Accept-Ranges header - RFC 9110 Sec. 14.3.
Typically bytes (range requests supported) or none
(explicitly unsupported). None when the header is unset.
content_range
property
¶
The raw Content-Range header - RFC 9110 Sec. 14.4. None if unset.
date
property
writable
¶
The Date header as a tz-aware UTC datetime - RFC 9110 Sec. 6.6.1.
Returns None when unset or unparseable. Assign a datetime
or POSIX timestamp to set it; assign None to remove it.
location
property
writable
¶
The Location header - RFC 9110 Sec. 10.2.2. None when unset.
content_location
property
writable
¶
The Content-Location header - RFC 9110 Sec. 8.7. None when unset.
retry_after
property
writable
¶
The Retry-After header - RFC 9110 Sec. 10.2.3.
Returns an int (delay in seconds) when the header is numeric,
a tz-aware datetime when it's an HTTP-date, or None when
unset. Assign an int / timedelta / datetime to set it;
assign None to remove it.
age
property
writable
¶
The Age header in seconds - RFC 9110 Sec. 5.1. None when unset.
cache_control
property
¶
Parsed Cache-Control header (read-only view).
For setting directives, prefer set_cache_control(...) which
writes the header directly. This property is convenient for
introspection: resp.cache_control.max_age,
resp.cache_control.no_store, etc.
get_json
¶
Parse the response body as JSON.
Returns None for an empty body. Useful in tests to inspect a
JSON response without re-decoding body by hand. Raises if the
body is non-empty and not valid JSON.
set_cookie
¶
set_cookie(key: str, value: str, max_age: Any = None, expires: Any = None, path: str = '/', domain: str | None = None, secure: bool = False, httponly: bool = False, samesite: str | None = 'Lax', partitioned: bool = False, prefix: Literal['host', 'secure'] | None = None) -> None
Build a Set-Cookie header per RFC 6265.
The cookie name must be a valid RFC 6265 token (no spaces, separators,
or control characters) and must not collide with a cookie-attribute
keyword (Path, Max-Age, ...); a violation raises ValueError.
samesite defaults to "Lax" - a CSRF-resistant default that
matches modern browser behaviour. Pass samesite="None" (with
secure=True) for a cookie that must travel on cross-site
requests, or samesite=None/"" to omit the attribute.
expires= accepts a datetime, a Unix timestamp int|float,
or an already-formatted IMF-fixdate str. When both max_age
and expires are set, both are emitted (RFC 6265 Sec. 5.2.2: clients
prefer Max-Age when supported, falling back to Expires on
legacy IE).
partitioned=True adds the CHIPS Partitioned attribute
(Cookies Having Independent Partitioned State) - a partitioned
cookie is keyed to the top-level site, so embedded third-party
contexts each get an isolated jar. Partitioned requires
Secure, so it is only emitted when secure=True.
prefix="host" / prefix="secure" add the RFC 6265bis Sec. 4.1.3
name prefix (__Host- / __Secure-) and enforce its invariants:
"secure" requires secure=True; "host" also requires path="/"
and no domain. A violation raises ValueError.
The cookie name and value are rejected if they contain CR, LF, or
NUL - untrusted data must not be able to inject additional cookies
or response headers. dump_cookie performs that CRLF check on all
five fields (name, value, domain, path, samesite), so set_cookie
does not repeat it.
calculate_content_length
¶
Set Content-Length from len(body) and return the value.
Useful when a caller mutates body directly and wants the
header to follow. The ASGI emit path computes Content-Length
from body on the fly anyway; this helper is for callers that
want it locked into self.headers ahead of time.
set_data
¶
Replace the response body.
Accepts bytes or str (UTF-8 encoded). Invalidates the cached
HTTP/1.1 encode so the new body wire-out on the next emit.
Refreshes Content-Length when previously set on the headers.
set_cache_control
¶
set_cache_control(max_age: int | None = None, public: bool = False, private: bool = False, no_cache: bool = False, no_store: bool = False, must_revalidate: bool = False, immutable: bool = False, s_maxage: int | None = None) -> str
Build and set the Cache-Control header - RFC 9111 Sec. 5.2.
Combines the standard directives in the order RFC 9111 Sec. 5.2
documents. Values that are False / None are omitted, so a plain
resp.set_cache_control(max_age=3600, public=True) produces
Cache-Control: public, max-age=3600. Returns the value set.
add_vary
¶
Append header names to the Vary response header - RFC 9110 Sec. 12.5.5.
Merges with any existing Vary value (de-duplicates,
case-insensitive). Returns the resulting header value.
Useful when middleware wants to communicate "this response
depends on the named request headers" without clobbering
existing entries.
set_basic_auth_challenge
¶
Write a Basic WWW-Authenticate challenge - RFC 7617.
Convenience for the common 401 case:
WWW-Authenticate: Basic realm="<realm>", charset="UTF-8".
Returns the header value written.
set_content_range
¶
set_content_range(start: int | None, stop: int | None, length: int | None, unit: str = HEADER_VALUE_BYTES) -> str
Write a Content-Range header - RFC 9110 Sec. 14.4.
set_content_range(0, 499, 1234)->bytes 0-499/1234.start/stopbothNone-> an unsatisfied-range response:bytes */1234(length required in that form).lengthNone-> unknown total:bytes 0-499/*.
Returns the header value written.
set_etag
¶
Set the ETag header from an explicit value.
Quotes the value if the caller passed it bare. Prepends W/
when weak=True. Use add_etag() for body-derived MD5
ETags; set_etag is for callers that already have an
authoritative tag (DB revision, commit hash, version
counter).
get_etag
¶
Return (etag, is_weak) parsed from the ETag header.
(None, False) when unset. Returned tag keeps its quotes so
it compares directly with If-None-Match values.
freeze
¶
Pre-compute the cached HTTP/1.1 encode.
For buffered responses, populates _encoded so subsequent
access pays no encode cost. For streaming responses, no-op.
Used by response caching layers that want immutable bytes.
iter_encoded
¶
Yield the response body.
Return type is mode-dependent and the two modes are NOT interchangeable:
- Buffered response (
is_streamed is False) -> returns a synchronous iterator yieldingbytes. Drain withfor. - Streaming response (
is_streamed is True) -> returns the underlying async iterator (AsyncIterator[bytes]). Drain withasync for.
Callers must branch on response.is_streamed (or use
inspect.isasyncgen / hasattr(it, "__aiter__")) to pick
the right loop, e.g.:
it = response.iter_encoded()
if response.is_streamed:
async for chunk in it:
...
else:
for chunk in it:
...
The return shape is mode-dependent: a buffered response yields a
synchronous iterator of bytes, a streaming response yields the
underlying AsyncIterator[bytes]. Branch on response.is_streamed
to drain with the right loop.
iter_chunked
¶
Yield the response body in fixed-size chunks.
Return type is mode-dependent and the two modes are NOT interchangeable:
- Buffered response (
is_streamed is False) -> returns a synchronous generator yieldingbytesslices of lengthsize(the final slice may be shorter). Drain withfor. - Streaming response (
is_streamed is True) -> returns the underlying async iterator unchanged (AsyncIterator[bytes]);sizeis ignored because chunk boundaries are controlled by the source generator, not the caller. Drain withasync for.
Pick the loop based on response.is_streamed:
it = response.iter_chunked(4096)
if response.is_streamed:
async for chunk in it:
...
else:
for chunk in it:
...
size must be positive. The return shape is mode-dependent: branch
on response.is_streamed to drain with the right loop.
add_etag
¶
Compute and attach an ETag derived from the body.
Uses MD5 of the response body, opaque-quoted per RFC 9110 Sec. 8.8.3.
weak=True prepends W/ so the validator is treated as a
weak match (matching content but possibly different
byte-for-byte). Sets ETag even if one was already set; pass
the explicit ETag in __init__(headers=...) to skip this.
Returns the value set.
make_conditional
¶
make_conditional(request: Any) -> Response
Downgrade this response to 304 when the request's preconditions match the response's ETag / Last-Modified.
Checks If-None-Match first (per RFC 9110 Sec. 13.2 precedence),
then If-Modified-Since. On a match, mutates self to status
304 with no body. Returns self so callers can use it inline:
return resp.make_conditional(request).
Handles If-None-Match: * (matches any current representation
of the resource) and the weak/strong ETag comparison rules.
check_preconditions
¶
check_preconditions(request: Any) -> Response
Enforce the write-side If-Match precondition (RFC 9110 Sec. 13.1.1).
Raises PreconditionFailed (412) when the request carries an
If-Match header that the response's current ETag does not satisfy
under the strong comparison (Sec. 8.8.3.1) - the lost-update guard.
If-Match: * is satisfied whenever a current representation exists,
approximated here by the presence of an ETag header. With no
If-Match header the response is returned unchanged. Returns self
so it can be chained: return resp.check_preconditions(request).
Invoke this inside a handler (where HTTPException is converted to a
response); it raises rather than mutating the status.
set_content_disposition
¶
set_content_disposition(disposition: str = HEADER_VALUE_ATTACHMENT, filename: str | None = None) -> str
Write a Content-Disposition header - RFC 6266.
disposition is "attachment" (force download) or "inline"
(render in-browser). When filename is given, an ASCII quotable
name uses filename="..." (spaces and punctuation preserved, only
\ and " escaped); a non-ASCII or non-quotable name uses only
the RFC 5987 filename*=UTF-8''... form, with no lossy legacy slot.
Returns the header value written.
delete_cookie
¶
delete_cookie(key: str, path: str = '/', domain: str | None = None, secure: bool = False, httponly: bool = False, samesite: str | None = None, partitioned: bool = False, prefix: Literal['host', 'secure'] | None = None) -> None
Delete a cookie by overwriting it with an empty value + Max-Age=0.
The browser only treats the new cookie as a replacement for the
existing one if Path, Domain, and the Secure / SameSite
/ Partitioned attributes match - otherwise it stores both. So a
session cookie originally set with Secure; SameSite=None (or with
Partitioned) will not be deleted by a plain delete_cookie(key)
call. Pass the same flags here. prefix deletes the cookie under
its true __Host-/__Secure- wire name and enforces the same
invariants on the deletion's attributes.
JSONResponse
¶
Bases: Response
JSON response using orjson for speed.
Usage::
from veloce import JSONResponse
async def handler(request):
return JSONResponse({"ok": True}, status_code=200)
from_bytes
classmethod
¶
from_bytes(body: bytes, *, status_code: int = HTTP_200_OK, headers: dict[str, str] | None = None) -> JSONResponse
Build a JSONResponse from already-encoded JSON bytes.
Skips __init__'s orjson re-encode - use this when the caller
has produced the JSON body itself (e.g. with custom orjson
options or via a JSONProvider.dumps). The body is sent
verbatim with Content-Type taken from cls.default_media_type
(so a subclass like class ProblemJSON(JSONResponse):
default_media_type = "application/problem+json" gets its
declared type without overriding this method).
The caller is responsible for ensuring body is valid UTF-8
JSON; no parsing or validation is performed. Passing non-JSON
bytes will produce a response whose body does not match its
declared content type.
body must be bytes or bytearray. A str raises
TypeError rather than being silently encoded, so callers do
not produce a response with a mismatched charset by accident.
Header precedence: when headers includes a Content-Type
entry, the caller-supplied value wins and the class default is
not emitted. This matches Response's general rule that user
headers override framework defaults and lets callers send
application/problem+json or another JSON suffix type without
subclassing.
ORJSONResponse
¶
Bases: JSONResponse
Explicit orjson-backed JSON response.
JSONResponse already uses orjson for encoding, so this class is a
semantic alias - useful when route declarations want to communicate
the encoder choice via response_class=ORJSONResponse.
UJSONResponse
¶
Bases: Response
JSON response encoded with ujson.
Lazily imports ujson at construction. Raises ImportError with a
clear message when the package is missing rather than at module load,
so apps that don't use this class don't need ujson installed.
HTMLResponse
¶
Bases: _TextResponse
HTML response.
PlainTextResponse
¶
Bases: _TextResponse
Plain text response.
StreamingResponse
¶
Bases: Response
Streaming response for large payloads.
content may be an async iterator/iterable or a plain sync
iterable (e.g. a generator). A sync iterable is wrapped so the
response always exposes an async stream; both forms are accepted.
Usage::
from veloce import StreamingResponse
async def handler(request):
def chunks():
yield b"part-1"
yield b"part-2"
return StreamingResponse(chunks(), content_type="text/plain")
stream_to
async
¶
Stream chunks to transport.
When drain is supplied (the raw serving protocol passes its write-side
flow-control awaitable) it is awaited after each chunk, so a producer
outrunning a slow client is throttled instead of growing the transport
write buffer without bound. drain is a no-op until the buffer crosses
the high-water mark, so the fast path pays one already-set check.
FileResponse
¶
Bases: Response
Serve a file from disk - small files inline, large files via executor.
from_path
async
classmethod
¶
from_path(path: str, filename: str | None = None, content_type: str | None = None, headers: dict[str, str] | None = None, content_disposition_type: str = HEADER_VALUE_ATTACHMENT) -> FileResponse
Async factory - reads small files inline, large files in the executor.
Stats the path on the loop (one fast syscall) to size the file. A file at
or below _INLINE_READ_MAX is read inline, skipping the thread-pool hop
that otherwise dominates serving a small static asset; a larger file is
read in the executor so a big read never stalls the loop.