Merge pull request #11768 from jpountz/fix/stream_eos

Make sure messages are fully read even in case of EOS markers.
This commit is contained in:
Adrien Grand 2015-06-30 09:00:59 +02:00
commit ed561cd434
1 changed files with 76 additions and 47 deletions

View File

@ -19,6 +19,7 @@
package org.elasticsearch.transport.netty; package org.elasticsearch.transport.netty;
import org.apache.lucene.util.IOUtils;
import org.elasticsearch.Version; import org.elasticsearch.Version;
import org.elasticsearch.common.component.Lifecycle; import org.elasticsearch.common.component.Lifecycle;
import org.elasticsearch.common.compress.Compressor; import org.elasticsearch.common.compress.Compressor;
@ -90,62 +91,90 @@ public class MessageChannelHandler extends SimpleChannelUpstreamHandler {
byte status = buffer.readByte(); byte status = buffer.readByte();
Version version = Version.fromId(buffer.readInt()); Version version = Version.fromId(buffer.readInt());
StreamInput wrappedStream; StreamInput wrappedStream = null;
if (TransportStatus.isCompress(status) && hasMessageBytesToRead && buffer.readable()) { try {
Compressor compressor; if (TransportStatus.isCompress(status) && hasMessageBytesToRead && buffer.readable()) {
try { Compressor compressor;
compressor = CompressorFactory.compressor(buffer); try {
} catch (NotCompressedException ex) { compressor = CompressorFactory.compressor(buffer);
int maxToRead = Math.min(buffer.readableBytes(), 10); } catch (NotCompressedException ex) {
int offset = buffer.readerIndex(); int maxToRead = Math.min(buffer.readableBytes(), 10);
StringBuilder sb = new StringBuilder("stream marked as compressed, but no compressor found, first [").append(maxToRead).append("] content bytes out of [").append(buffer.readableBytes()).append("] readable bytes with message size [").append(size).append("] ").append("] are ["); int offset = buffer.readerIndex();
for (int i = 0; i < maxToRead; i++) { StringBuilder sb = new StringBuilder("stream marked as compressed, but no compressor found, first [").append(maxToRead).append("] content bytes out of [").append(buffer.readableBytes()).append("] readable bytes with message size [").append(size).append("] ").append("] are [");
sb.append(buffer.getByte(offset + i)).append(","); for (int i = 0; i < maxToRead; i++) {
sb.append(buffer.getByte(offset + i)).append(",");
}
sb.append("]");
throw new IllegalStateException(sb.toString());
} }
sb.append("]"); wrappedStream = compressor.streamInput(streamIn);
throw new IllegalStateException(sb.toString()); } else {
wrappedStream = streamIn;
} }
wrappedStream = compressor.streamInput(streamIn); wrappedStream.setVersion(version);
} else {
wrappedStream = streamIn;
}
wrappedStream.setVersion(version);
if (TransportStatus.isRequest(status)) { if (TransportStatus.isRequest(status)) {
String action = handleRequest(ctx.getChannel(), wrappedStream, requestId, version); String action = handleRequest(ctx.getChannel(), wrappedStream, requestId, version);
if (buffer.readerIndex() != expectedIndexReader) { boolean success = false;
if (buffer.readerIndex() < expectedIndexReader) { try {
logger.warn("Message not fully read (request) for requestId [{}], action [{}], readerIndex [{}] vs expected [{}]; resetting", final int nextByte = wrappedStream.read();
requestId, action, buffer.readerIndex(), expectedIndexReader); // calling read() is useful to make sure the message is fully read, even if there is an EOS marker
} else { if (nextByte != -1) {
logger.warn("Message read past expected size (request) for requestId=[{}], action [{}], readerIndex [{}] vs expected [{}]; resetting", throw new IllegalStateException("Message not fully read (request) for requestId [" + requestId + "], action ["
requestId, action, buffer.readerIndex(), expectedIndexReader); + action + "], readerIndex [" + buffer.readerIndex() + "] vs expected [" + expectedIndexReader + "]; resetting");
} }
buffer.readerIndex(expectedIndexReader); if (buffer.readerIndex() < expectedIndexReader) {
} throw new IllegalStateException("Message is fully read (request), yet there are " + (expectedIndexReader - buffer.readerIndex()) + " remaining bytes; resetting");
} else { }
TransportResponseHandler handler = transportServiceAdapter.onResponseReceived(requestId); if (buffer.readerIndex() > expectedIndexReader) {
// ignore if its null, the adapter logs it throw new IllegalStateException("Message read past expected size (request) for requestId [" + requestId + "], action ["
if (handler != null) { + action + "], readerIndex [" + buffer.readerIndex() + "] vs expected [" + expectedIndexReader + "]; resetting");
if (TransportStatus.isError(status)) { }
handlerResponseError(wrappedStream, handler); success = true;
} else { } finally {
handleResponse(ctx.getChannel(), wrappedStream, handler); if (!success) {
buffer.readerIndex(expectedIndexReader);
}
} }
} else { } else {
// if its null, skip those bytes TransportResponseHandler handler = transportServiceAdapter.onResponseReceived(requestId);
buffer.readerIndex(markedReaderIndex + size); // ignore if its null, the adapter logs it
} if (handler != null) {
if (buffer.readerIndex() != expectedIndexReader) { if (TransportStatus.isError(status)) {
if (buffer.readerIndex() < expectedIndexReader) { handlerResponseError(wrappedStream, handler);
logger.warn("Message not fully read (response) for [{}] handler {}, error [{}], resetting", requestId, handler, TransportStatus.isError(status)); } else {
handleResponse(ctx.getChannel(), wrappedStream, handler);
}
} else { } else {
logger.warn("Message read past expected size (response) for [{}] handler {}, error [{}], resetting", requestId, handler, TransportStatus.isError(status)); // if its null, skip those bytes
buffer.readerIndex(markedReaderIndex + size);
}
boolean success = false;
try {
final int nextByte = wrappedStream.read();
// calling read() is useful to make sure the message is fully read, even if there is an EOS marker
if (nextByte != -1) {
throw new IllegalStateException("Message not fully read (response) for requestId [" + requestId + "], handler ["
+ handler + "], error [" + TransportStatus.isError(status) + "]; resetting");
}
if (buffer.readerIndex() < expectedIndexReader) {
throw new IllegalStateException("Message is fully read (response), yet there are " + (expectedIndexReader - buffer.readerIndex()) + " remaining bytes; resetting");
}
if (buffer.readerIndex() > expectedIndexReader) {
throw new IllegalStateException("Message read past expected size (response) for requestId [" + requestId + "], handler ["
+ handler + "], error [" + TransportStatus.isError(status) + "]; resetting");
}
success = true;
} finally {
if (!success) {
buffer.readerIndex(expectedIndexReader);
}
} }
buffer.readerIndex(expectedIndexReader);
} }
} finally {
IOUtils.close(wrappedStream);
} }
wrappedStream.close();
} }
protected void handleResponse(Channel channel, StreamInput buffer, final TransportResponseHandler handler) { protected void handleResponse(Channel channel, StreamInput buffer, final TransportResponseHandler handler) {