Class HttpServer.Request
- Enclosing class:
HttpServer
What a handler receives. Header names are matched case-insensitively.
The headers are NOT copied out of the request. They stay as offsets into
the buffer the kernel filled, and getHeader(String) compares against those
bytes -- so a handler that reads two headers allocates nothing, where
building a map of Strings cost about 2.6KB per request and made char[] the
single largest allocation in the server. getHeaders() still returns
a Map, built on first call, for callers that want one.
A Request is valid for the duration of HttpServer.Handler.handle(HttpServer.Request) and NOT
beyond it. Both the byte array it was parsed from and the slice table that
indexes it are reused -- the array by the reading thread, the table by the
connection -- so a Request held past the handler describes whatever arrived
next, not what it was built from.
This corrects a claim that used to stand here, that the slices "stay valid
for as long as the Request is held". That was never true: the table is
conn.slices, reused by the very next request on the same connection. The
zero-copy read added a second way for it to be false, which is what prompted
reading the sentence carefully enough to notice it had always been wrong.
The synchronous handler signature already makes the call the natural
lifetime, so this documents the contract rather than narrowing one -- but
anything that needs to outlive the handler must copy what it needs, and
getHeaders() or getHeader(String) give Strings that are safe to keep.
-
Method Summary
Modifier and TypeMethodDescriptiongetBody()One header by name, matched case-insensitively.The headers as a Map, lower-cased names to values.Path plus query string, exactly as it arrived."HTTP/1.1" or "HTTP/1.0".intThe path's length in bytes -- the target up to?-- without building it.pathFrom(int from) The path fromfromonwards, as text.booleanpathIs(byte[] path) True when the request PATH is exactly these bytes.booleanpathStartsWith(byte[] prefix) AspathIs(byte[]), for a route that continues into a path variable.DIAGNOSTIC: the connection's Response exactly as the last request left it, or null the first time.queryParam(String name) A query parameter's decoded value, or null when the request did not send it.A Response for this request WITHOUT allocating one.respondJson(int status, Object value) A JSON response on the connection's pooled Response, serialised straight from the value.
-
Method Details
-
pathIs
public boolean pathIs(byte[] path) True when the request PATH is exactly these bytes.
The path, not the target: everything from
?onwards is the query string and is not part of the route. A router that compared the whole target would match/healthzand miss/healthz?probe=1, which is the same request.For the generated router, which holds each route as a byte[] constant. No String is built and nothing is hashed: it is a length test and a compare against the buffer the request was parsed from. Falls back to comparing the target String when the slice is not available, which is the HTTP/2 path -- there the target came from HPACK rather than from a byte range.
-
pathStartsWith
public boolean pathStartsWith(byte[] prefix) AspathIs(byte[]), for a route that continues into a path variable. -
pathFrom
The path from
fromonwards, as text. Allocates, so a matched route only.Percent escapes are left alone. The router decodes the segments it binds, because decoding first would let an encoded
/invent a segment boundary that the client never sent. -
pathByteLength
public int pathByteLength()The path's length in bytes -- the target up to?-- without building it. -
queryParam
-
respond
A Response for this request WITHOUT allocating one.
Returns the connection's single Response, re-pointed to these values. It is valid for the duration of
HttpServer.Handler.handle(HttpServer.Request)and not beyond it -- the same contract this Request already carries, and for the same reason: the next request on this connection reuses it.Why it exists: on a route that allocates nothing else, the Response was the last per-request allocation, and allocation is what drives both the collector's frequency and its footprint. Removing it measured a 15x better p99 and an 8x smaller resident set at the same throughput.
new Response(...)still works and still allocates; a handler that needs its Response to outlive the call must use it. -
respondJson
A JSON response on the connection's pooled Response, serialised straight from the value.
The deferred-JSON path already avoided every copy on the body side -- Json.write goes into the connection's reusable ByteSink, so nothing materialises a byte[] or a String -- but Response.jsonValue is a static that allocates a fresh Response per call, and that was the ONLY thing the route allocated. Profiled over 10.8M requests: 88.1 bytes each, all of it one HttpServer.Response, count 10789601 against 10789541 requests. The plaintext route had already been pooled and sat at 0.1 bytes per request.
That is worth removing because of what allocation costs HERE rather than what it costs to allocate: the collector shares the server's cores, so a route that allocates pays for cycles in its tail. fasthttp on the same body allocates about 16 bytes per request and collects three times a second; this route was collecting thirteen to eighteen times a second.
reset() clears deferredJson and hasDeferredJson, so a pooled Response reused for a plain body cannot carry a stale value into the next response -- which is the failure this would otherwise invite.
-
presetResponse
DIAGNOSTIC: the connection's Response exactly as the last request left it, or null the first time. Separates the allocation pooling saves from the field writes it adds -- see the bench demo's RESPONSE_MODE. -
getVersion
"HTTP/1.1" or "HTTP/1.0". The two differ on whether keep-alive is the default. -
getMethod
-
getTarget
Path plus query string, exactly as it arrived. -
getHeaders
The headers as a Map, lower-cased names to values.
Built on the first call and cached. Prefer
getHeader(String): this allocates a String per name and per value, which is the cost the slice representation exists to avoid. -
getHeader
-
getBody
-