Class HttpServer.Request

java.lang.Object
com.codename1.backend.HttpServer.Request
Enclosing class:
HttpServer

public static final class HttpServer.Request extends Object

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 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)
      As pathIs(byte[]), 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()
      The path's length in bytes -- the target up to ? -- without building it.
    • queryParam

      public String queryParam(String name)
      A query parameter's decoded value, or null when the request did not send it. An empty ?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 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

      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()
      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

      public String getVersion()
      "HTTP/1.1" or "HTTP/1.0". The two differ on whether keep-alive is the default.
    • getMethod

      public String getMethod()
    • getTarget

      public String getTarget()
      Path plus query string, exactly as it arrived.
    • getHeaders

      public Map 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

      public String getHeader(String name)
      One header by name, matched case-insensitively. Allocates only the value.
    • getBody

      public String getBody()