InputStream.readNBytes(n) returns at most n bytes; an early end of stream can make the returned array shorter.
Java readNBytes: reject a truncated payload after the header
Validate the declared length
A wire header is untrusted input. Check its sign and a protocol maximum before asking readNBytes to allocate. Then compare the returned array length with the declared size. End of stream after the header is a truncated frame, not an empty field or a successful partial message.
This example uses a one-byte length prefix to isolate the rule. Larger frames should use a bounded header reader and a fixed-size chunk loop when allocation pressure matters.
Decide who owns the bytes
The returned array belongs to the caller. Do not expose it as mutable shared application state without a copy or a clear ownership transfer. A readFully call provides a different API contract but still requires a maximum before buffer allocation.
Working program
import java.io.ByteArrayInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
public class ShipmentFrameReader {
static String readShipment(InputStream wire) throws IOException {
int declared = wire.read();
if (declared < 0) throw new EOFException("missing length");
if (declared > 64) throw new IOException("frame too large");
byte[] payload = wire.readNBytes(declared);
if (payload.length != declared) throw new EOFException("truncated payload");
return new String(payload, StandardCharsets.US_ASCII);
}
public static void main(String[] args) throws IOException {
System.out.println(readShipment(new ByteArrayInputStream(new byte[] {4, 'A', 'B', '4', '7'})));
try { readShipment(new ByteArrayInputStream(new byte[] {5, 'A', 'B'})); }
catch (EOFException truncated) { System.out.println(truncated.getMessage()); }
}
}Output
AB47
truncated payloadCost and ownership
readNBytes allocates up to the requested payload size, so the cap is a memory policy, not an optional validation. Time is O(n) for n bytes received. This example targets Java 11 or later; a streaming decoder can avoid retaining the whole frame.
Common Mistakes
- Do not infer that readNBytes always returns the requested count.
- Do not let a header dictate an unbounded allocation.
- Do not treat an incomplete payload as a valid message.
Read next
Java InputStream short reads: assemble a complete record header, datainputstream readfully eof, Java byte streams: partial reads and bounded copying, Java UTF-8 decoding: reject malformed bytes before parsing.
