Package dev.relism.flash.transport
Class BufferedByteSource
java.lang.Object
java.io.InputStream
dev.relism.flash.transport.BufferedByteSource
- All Implemented Interfaces:
Closeable,AutoCloseable
The single buffered view over one connection's inbound bytes, for the whole lifetime of the
gives Why this exists instead of
A generic buffered stream would already fix the per-byte-syscall problem, but it cannot
"un-consume" bytes without a fragile Deadline, not
ProtocolNegotiator a way to inspect the first bytes
of a plaintext connection (the h2c preface) without consuming them.
Why this exists instead of BufferedInputStream
A generic buffered stream would already fix the per-byte-syscall problem, but it cannot
"un-consume" bytes without a fragile mark()/reset() dance, and it has no way
to bound an individual read by an absolute wall-clock deadline (see below). This class is
purpose-built for exactly the two things this connection loop needs beyond plain buffering:
peek(byte[], int, int) (look-ahead without consuming — used once, at connection
start, for h2c prior-knowledge detection) and prependOnce(byte[], int, int)
(zero-allocation, zero-copy re-insertion of bytes the caller already read into its own
buffer — used by ChunkedInputStream to hand back the header-parser's read-ahead
bytes instead of the SequenceInputStream/ByteArrayInputStream wrapping this
replaces).
Deadline, not SO_TIMEOUT alone
Socket.setSoTimeout(int) bounds a single read() call, not a sequence of them —
a peer that trickles one byte every 9 seconds never trips a 10-second SO_TIMEOUT, since
each individual read succeeds within the window. setDeadline(long) instead records an
absolute System.nanoTime() deadline; every underlying socket read computes the
remaining budget and hands exactly that to setSoTimeout before reading, so a
SocketTimeoutException from an underlying read unambiguously means the deadline —
deadline, do not rely on setSoTimeout alone."
Thread-safety
Not thread-safe, by design — exactly one virtual thread ever owns a connection's inbound bytes at a time (the same invariantRequestParser and ChunkedInputStream
already assume).-
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final intDefault internal buffer size. -
Constructor Summary
ConstructorsConstructorDescriptionBufferedByteSource(InputStream in, Socket socket) BufferedByteSource(InputStream in, Socket socket, int bufferSize) -
Method Summary
Modifier and TypeMethodDescriptionintvoidRemoves the deadline and restores the socket to blocking indefinitely (SO_TIMEOUT = 0).voidclose()intpeek(byte[] dst, int off, int len) Ensures up tolenbytes are buffered and copies them intodstwithout advancing the read position — a subsequentread()still returns the same bytes.voidprependOnce(byte[] src, int off, int len) Queueslenbytes, starting atoffin the caller-owned arraysrc, to be served by the next reads before anything else — zero allocation and zero copy, sincesrcis referenced directly, not duplicated.intread()intread(byte[] dst, int off, int len) voidsetDeadline(long deadlineNanoTime) Every underlying socket read performed after this call is bounded so that it cannot still be blocking pastdeadlineNanoTime(an absolute value comparable toSystem.nanoTime()).longskip(long n) Methods inherited from class java.io.InputStream
mark, markSupported, nullInputStream, read, readAllBytes, readNBytes, readNBytes, reset, skipNBytes, transferTo
-
Field Details
-
DEFAULT_BUFFER_SIZE
public static final int DEFAULT_BUFFER_SIZEDefault internal buffer size. Matches the relay-buffer convention already used elsewhere in this codebase (the 8 KBSTREAM_RELAY_BUFFERinHttpServer) rather than introducing a new tuning constant nothing has calibrated yet.- See Also:
-
-
Constructor Details
-
BufferedByteSource
-
BufferedByteSource
-
-
Method Details
-
setDeadline
public void setDeadline(long deadlineNanoTime) Every underlying socket read performed after this call is bounded so that it cannot still be blocking pastdeadlineNanoTime(an absolute value comparable toSystem.nanoTime()). A read that would exceed the deadline throwsSocketTimeoutExceptioninstead of blocking further. Bytes already sitting in the internal buffer or the prepend window are served immediately regardless of the deadline — only reads that would otherwise block on the network are bounded. -
clearDeadline
Removes the deadline and restores the socket to blocking indefinitely (SO_TIMEOUT = 0). Must be called before any read the caller wants to be unbounded (e.g. handing the connection off to a long-lived WebSocket session loop). test in this codebase that constructs aBufferedByteSourcedirectly over aByteArrayInputStreampassesnull, since there is no real connection to bound) is treated as "no OS-level timeout to clear", not an error — only the deadline bookkeeping is reset. Production always supplies a real socket, so this changes no production behavior; without it, no test can exercise the deadline mechanism at all.- Throws:
IOException
-
read
- Specified by:
readin classInputStream- Throws:
IOException
-
read
- Overrides:
readin classInputStream- Throws:
IOException
-
skip
- Overrides:
skipin classInputStream- Throws:
IOException
-
available
public int available()- Overrides:
availablein classInputStream
-
close
- Specified by:
closein interfaceAutoCloseable- Specified by:
closein interfaceCloseable- Overrides:
closein classInputStream- Throws:
IOException
-
peek
Ensures up tolenbytes are buffered and copies them intodstwithout advancing the read position — a subsequentread()still returns the same bytes. Blocks (bounded by the active deadline, if any) untillenbytes are available or the underlying stream reaches EOF. Returns the number of bytes actually made available, which is less thanlenonly at EOF.Only valid before anything has been
prepended— in practice this means it is only ever called once, byProtocolNegotiator, at the very start of a connection before any other read.- Throws:
IllegalArgumentException- iflenexceeds the internal buffer's capacity — this class cannot peek further ahead than it buffers.IOException
-
prependOnce
public void prependOnce(byte[] src, int off, int len) Queueslenbytes, starting atoffin the caller-owned arraysrc, to be served by the next reads before anything else — zero allocation and zero copy, sincesrcis referenced directly, not duplicated. The caller must not mutatesrc[off..off+len)until the prefix is fully consumed.Exactly one prefix may be pending at a time. This is intentional: it exists solely to hand
RequestParser's header-buffer read-ahead bytes to a freshChunkedInputStreamat the start of a chunked body, a single well-defined moment per request — it is not a general-purpose pushback stack.- Throws:
IllegalStateException- if a prefix is already pending
-