Backend API. Server-side code: this runs in a Codename One backend, not in the app on the device.
public static final class HttpServer.Request
- Object
- HttpServer.Request
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 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 Handler.handle 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 give Strings that are safe to keep.
Methods
public boolean pathIs(byte[] path) | True when the request PATH is exactly these bytes. |
public boolean pathStartsWith(byte[] prefix) | As pathIs, for a route that continues into a path variable. |
public String pathFrom(int from) | The path from from onwards, as text. |
public int pathByteLength() | The path’s length in bytes – the target up to ? – without building it. |
public String queryParam(String name) | A query parameter’s decoded value, or null when the request did not send it. |
public HttpServer.Response respond(int status, String contentType, byte[] body) | A Response for this request WITHOUT allocating one. |
public HttpServer.Response respondJson(int status, Object value) | A JSON response on the connection’s pooled Response, serialised straight from the value. |
public HttpServer.Response presetResponse() | DIAGNOSTIC: the connection’s Response exactly as the last request left it, or null the first time. |
public String getPeerAddress() | The address of the other end of the connection this request arrived on: the client’s when it connected directly, a load balancer’s when it did not. |
public String getRemoteAddress() | The address of the client this request is from. |
public boolean isSecure() | Whether the client reached this request’s server over TLS: this server terminated it, or a trusted proxy did and says so in X-Forwarded-Proto. |
public HttpSession getSession() | The session of this request, creating one if it has none. |
public HttpSession getSession(boolean create) | The session this request belongs to: the one its cookie names, or, with create set and no valid cookie, a new one the response will send a cookie for. |
public String getCookie(String name) | The value of one cookie the client sent, or null. |
public String getVersion() | “HTTP/1.1” or “HTTP/1.0”. |
public String getMethod() | |
public String getTarget() | Path plus query string, exactly as it arrived. |
public Map getHeaders() | The headers as a Map, lower-cased names to values. |
public String getHeader(String name) | One header by name, matched case-insensitively. |
public String getBody() | The body as text, or null when the request has none. |
public byte[] getBodyBytes() | The body as bytes, or null when the request has none: a binary body as it arrived – decompressed when the client sent it gzip encoded – and a text body as its UTF-8. |
public List getParts() | The parts of a multipart/form-data body, in order; an empty list when the body is not one. |
public HttpServer.Part getPart(String name) | The first part named name, or null. |
public String param(String name) | A request parameter, as @RequestParam reads it and as a servlet container does: the query string first, then the fields of a form body – application/x-www-form-urlencoded, or the parts of a multipart/form-data one that are not files. |
Inherited methods
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 /healthz and 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)pathIs, for a route that continues into a path variable.pathFrom
public String pathFrom(int from)The path from from onwards, 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()? – without building it.queryParam
public String queryParam(String name)?flag= is present with an empty value, which is not the
same as absent, and callers that offer a default depend on the difference.respond
public HttpServer.Response respond(int status, String contentType, byte[] body)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 Handler.handle 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
public HttpServer.Response respondJson(int status, Object value)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
public HttpServer.Response presetResponse()getPeerAddress
public String getPeerAddress()getRemoteAddress for the one to act on.Returns
getRemoteAddress
public String getRemoteAddress()The address of the client this request is from.
That is the other end of the connection, unless this server was told
it sits behind proxies and the connection is one of theirs. Then it
is what X-Forwarded-For says, read from the right past every
trusted proxy. From any other connection the header is ignored: it
is the client’s own claim about itself.
| Property | Meaning |
|---|---|
cn1.server.forwardHeaders | true to read the forwarding headers at all; false unless set. |
cn1.server.trustedProxies | The proxies, as addresses and CIDR ranges separated by commas. Unless explicitly listed, no peer is trusted, including loopback and private addresses. |
IPv4 is dotted, IPv6 is eight groups of hexadecimal without ::, and
an IPv4 address carried in IPv6 is written as IPv4 – one spelling
per address, so the text can be a key.
Returns
isSecure
public boolean isSecure()X-Forwarded-Proto. The header is believed under the rule
getRemoteAddress believes X-Forwarded-For under.getSession
public HttpSession getSession()getSession
public HttpSession getSession(boolean create)create set and no valid cookie, a new one the response will
send a cookie for. Null when there is none and create is false.
See HttpSession for the cookie and where sessions are kept.getCookie
public String getCookie(String name)getVersion
public String getVersion()getMethod
public String getMethod()getTarget
public String getTarget()getHeaders
public Map getHeaders()The headers as a Map, lower-cased names to values.
Built on the first call and cached. Prefer getHeader: this
allocates a String per name and per value, which is the cost the slice
representation exists to avoid.
getHeader
public String getHeader(String name)getBody
public String getBody()The body as text, or null when the request has none.
A text body – any Content-Type that is not binary, or none – was
checked to be UTF-8 when it arrived, and a request whose body was not is
answered 400 before any handler runs. A binary body (an upload, a
multipart/form-data form) is meant for getBodyBytes. Asked for as
text anyway, it is decoded when its bytes are UTF-8 – as Spring’s string
converter reads any media type into a @RequestBody String – and throws
when they are not, rather than handing a handler characters the client
never sent.
getBodyBytes
public byte[] getBodyBytes()getParts
public List getParts()multipart/form-data body, in order; an empty list
when the body is not one. Throws IllegalArgumentException for a body
that says it is multipart and is not well formed, which a generated
router answers with a 400.getPart
public HttpServer.Part getPart(String name)name, or null.param
public String param(String name)@RequestParam reads it and as a servlet
container does: the query string first, then the fields of a form body
– application/x-www-form-urlencoded, or the parts of a
multipart/form-data one that are not files. Null when neither has it.Throws
IllegalArgumentException- when the body says it is multipart and
is not well formed, as
getPartsdoes; read as “no such field”, a route whose parameters are all optional ran on a body nothing parsed IllegalStateException- when the field is a multipart part that is
not UTF-8 text. A generated
@RequestParambinding answers either with 400