Package dev.relism.flash
Class RequestParser
java.lang.Object
dev.relism.flash.RequestParser
One instance per connection. The buffer is allocated once and reused across
keep-alive requests. Grows on demand (doubling, up to
maxHeaderBufferSize).
Zero-allocation design
- No
Stringallocations during parsing: paths, headers and protocol are exposed asByteViewslices into the shared buffer. headerMapis reset in-place per request — single allocation for the lifetime of the connection.- Pipelining / keep-alive leftover bytes are tracked via
bufBaseandbufLen— no copy between requests on the common path.
State invariant
bufBase and bufLen always reflect unconsumed bytes that belong
to the next request. They are snapshotted at the top of parse(dev.relism.flash.transport.BufferedByteSource)
and reset to 0/0 before any work begins, so an exception thrown mid-parse
leaves the fields clean rather than pointing at stale data from a previous request.
Anything wrong with the request itself — smuggling-relevant ambiguity, an over-limit
header, a malformed byte where the grammar forbids one — is reported as a
MalformedRequestException carrying the exact status the caller must respond with.
This is distinct from IOException, which still means "the socket failed" (EOF,
reset, timeout). The caller (HttpServer.process) must always close the connection
after a MalformedRequestException, never keep it alive — RFC 9112 §6.1's rationale
for rejecting Content-Length + Transfer-Encoding outright is exactly that a
kept-alive connection after a disputed request boundary is what a smuggling attack needs.-
Constructor Summary
ConstructorsConstructorDescriptionRequestParser(int maxHeaderBufferSize) RequestParser(int maxHeaderBufferSize, InetSocketAddress remoteAddress) RequestParser(int maxHeaderBufferSize, InetSocketAddress remoteAddress, SSLSocket sslSocket) -
Method Summary
Modifier and TypeMethodDescriptionbooleanWhether bytes from a previousparse(dev.relism.flash.transport.BufferedByteSource)call are already buffered and ready to be consumed by the next call without reading anything further from the source — the HTTP pipelining case.Parses the next HTTP request fromin.
-
Constructor Details
-
RequestParser
public RequestParser() -
RequestParser
public RequestParser(int maxHeaderBufferSize) -
RequestParser
-
RequestParser
-
-
Method Details
-
hasBufferedBytes
public boolean hasBufferedBytes()Whether bytes from a previousparse(dev.relism.flash.transport.BufferedByteSource)call are already buffered and ready to be consumed by the next call without reading anything further from the source — the HTTP pipelining case. The caller (the connection loop) uses this to decide whether it is safe to skip waiting for "the next request has started arriving": if bytes are already buffered, the next request has, by definition, already started (and may even be complete), so an idle-timeout wait on the underlying source would wait for bytes that were never going to arrive there — they are already here. -
parse
Parses the next HTTP request fromin.Exception safety:
bufBaseandbufLenare reset to0before any parsing work begins. If an exception is thrown, the connection will be closed by the caller, so stale leftover state is never a problem — but the reset ensures correctness in test scenarios where the same parser instance is reused after an error.- Returns:
- the parsed
Request, ornullon clean EOF. - Throws:
MalformedRequestException- if the request violates the HTTP/1.1 grammar or a configured safety limit — carries the exact status to respond with.IOException- on genuine I/O failure (socket reset, timeout).
-