package com.hubspot.smtp.messages; import java.io.InputStream; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.handler.stream.ChunkedStream; /** * A {@code ChunkedStream} implementation that wraps an {@code InputStream}, appending * CRLF only if the stream doesn't already end with that byte sequence. * */ class CrlfTerminatingChunkedStream extends ChunkedStream { private static final byte CR = '\r'; private static final byte LF = '\n'; private static final int DEFAULT_CHUNK_SIZE = 64 * 1024; private static final byte[] TRAILING_BYTES = { CR, LF }; CrlfTerminatingChunkedStream(InputStream in) { this(in, DEFAULT_CHUNK_SIZE); } CrlfTerminatingChunkedStream(InputStream in, int chunkSize) { super(in, chunkSize); } @Override public ByteBuf readChunk(ByteBufAllocator allocator) throws Exception { ByteBuf chunk = super.readChunk(allocator); if (!isEndOfInput()) { return chunk; } if (chunk == null || isTerminatedWithCrLf(chunk)) { return chunk; } return allocator.compositeBuffer(2).addComponents(true, chunk, allocator.buffer(2).writeBytes(TRAILING_BYTES)); } private boolean isTerminatedWithCrLf(ByteBuf chunk) { int length = chunk.readableBytes(); return length >= 2 && chunk.getByte(length - 2) == CR && chunk.getByte(length - 1) == LF; } }