protected void messageReceived( byte[] data, String action, LocalTransport sourceTransport, Version version, @Nullable final Long sendRequestId) { Transports.assertTransportThread(); try { transportServiceAdapter.received(data.length); StreamInput stream = StreamInput.wrap(data); stream.setVersion(version); long requestId = stream.readLong(); byte status = stream.readByte(); boolean isRequest = TransportStatus.isRequest(status); if (isRequest) { ThreadContext threadContext = threadPool.getThreadContext(); threadContext.readHeaders(stream); handleRequest(stream, requestId, data.length, sourceTransport, version); } else { final TransportResponseHandler handler = transportServiceAdapter.onResponseReceived(requestId); // ignore if its null, the adapter logs it if (handler != null) { if (TransportStatus.isError(status)) { handleResponseError(stream, handler); } else { handleResponse(stream, sourceTransport, handler); } } } } catch (Throwable e) { if (sendRequestId != null) { TransportResponseHandler handler = sourceTransport.transportServiceAdapter.onResponseReceived(sendRequestId); if (handler != null) { RemoteTransportException error = new RemoteTransportException(nodeName(), localAddress, action, e); sourceTransport .workers() .execute( () -> { ThreadContext threadContext = sourceTransport.threadPool.getThreadContext(); try (ThreadContext.StoredContext ignore = threadContext.stashContext()) { sourceTransport.handleException(handler, error); } }); } } else { logger.warn("Failed to receive message for action [{}]", e, action); } } }
protected String handleRequest( Channel channel, StreamInput buffer, long requestId, Version version) throws IOException { final String action = buffer.readString(); final NettyTransportChannel transportChannel = new NettyTransportChannel(transport, action, channel, requestId, version); try { final TransportRequestHandler handler = transportServiceAdapter.handler(action); if (handler == null) { throw new ActionNotFoundTransportException(action); } final TransportRequest request = handler.newInstance(); request.remoteAddress( new InetSocketTransportAddress((InetSocketAddress) channel.getRemoteAddress())); request.readFrom(buffer); if (handler.executor() == ThreadPool.Names.SAME) { //noinspection unchecked handler.messageReceived(request, transportChannel); } else { threadPool .executor(handler.executor()) .execute(new RequestHandler(handler, request, transportChannel, action)); } } catch (Throwable e) { try { transportChannel.sendResponse(e); } catch (IOException e1) { logger.warn("Failed to send error message back to client for action [" + action + "]", e); logger.warn("Actual Exception", e1); } } return action; }
@Override public void disconnectFromNode(DiscoveryNode node) { synchronized (this) { LocalTransport removed = connectedNodes.remove(node); if (removed != null) { transportServiceAdapter.raiseNodeDisconnected(node); } } }
@Override public void sendRequest( final DiscoveryNode node, final long requestId, final String action, final TransportRequest request, TransportRequestOptions options) throws IOException, TransportException { final Version version = Version.smallest(node.getVersion(), this.version); try (BytesStreamOutput stream = new BytesStreamOutput()) { stream.setVersion(version); stream.writeLong(requestId); byte status = 0; status = TransportStatus.setRequest(status); stream.writeByte(status); // 0 for request, 1 for response. threadPool.getThreadContext().writeTo(stream); stream.writeString(action); request.writeTo(stream); stream.close(); final LocalTransport targetTransport = connectedNodes.get(node); if (targetTransport == null) { throw new NodeNotConnectedException(node, "Node not connected"); } final byte[] data = stream.bytes().toBytes(); transportServiceAdapter.sent(data.length); transportServiceAdapter.onRequestSent(node, requestId, action, request, options); targetTransport .workers() .execute( () -> { ThreadContext threadContext = targetTransport.threadPool.getThreadContext(); try (ThreadContext.StoredContext context = threadContext.stashContext()) { targetTransport.messageReceived( data, action, LocalTransport.this, version, requestId); } }); } }
@Override public void connectToNode(DiscoveryNode node) throws ConnectTransportException { synchronized (this) { if (connectedNodes.containsKey(node)) { return; } final LocalTransport targetTransport = transports.get(node.getAddress()); if (targetTransport == null) { throw new ConnectTransportException(node, "Failed to connect"); } connectedNodes.put(node, targetTransport); transportServiceAdapter.raiseNodeConnected(node); } }
@Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { Object m = e.getMessage(); if (!(m instanceof ChannelBuffer)) { ctx.sendUpstream(e); return; } ChannelBuffer buffer = (ChannelBuffer) m; int size = buffer.getInt(buffer.readerIndex() - 4); transportServiceAdapter.received(size + 6); // we have additional bytes to read, outside of the header boolean hasMessageBytesToRead = (size - (NettyHeader.HEADER_SIZE - 6)) != 0; int markedReaderIndex = buffer.readerIndex(); int expectedIndexReader = markedReaderIndex + size; // netty always copies a buffer, either in NioWorker in its read handler, where it copies to a // fresh // buffer, or in the cumlation buffer, which is cleaned each time StreamInput streamIn = ChannelBufferStreamInputFactory.create(buffer, size); long requestId = buffer.readLong(); byte status = buffer.readByte(); Version version = Version.fromId(buffer.readInt()); StreamInput wrappedStream; if (TransportStatus.isCompress(status) && hasMessageBytesToRead && buffer.readable()) { Compressor compressor = CompressorFactory.compressor(buffer); if (compressor == null) { int maxToRead = Math.min(buffer.readableBytes(), 10); int offset = buffer.readerIndex(); 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 ["); for (int i = 0; i < maxToRead; i++) { sb.append(buffer.getByte(offset + i)).append(","); } sb.append("]"); throw new ElasticsearchIllegalStateException(sb.toString()); } wrappedStream = CachedStreamInput.cachedHandlesCompressed(compressor, streamIn); } else { wrappedStream = CachedStreamInput.cachedHandles(streamIn); } wrappedStream.setVersion(version); if (TransportStatus.isRequest(status)) { String action = handleRequest(ctx.getChannel(), wrappedStream, requestId, version); if (buffer.readerIndex() != expectedIndexReader) { if (buffer.readerIndex() < expectedIndexReader) { logger.warn( "Message not fully read (request) for [{}] and action [{}], resetting", requestId, action); } else { logger.warn( "Message read past expected size (request) for [{}] and action [{}], resetting", requestId, action); } buffer.readerIndex(expectedIndexReader); } } else { TransportResponseHandler handler = transportServiceAdapter.remove(requestId); // ignore if its null, the adapter logs it if (handler != null) { if (TransportStatus.isError(status)) { handlerResponseError(wrappedStream, handler); } else { handleResponse(ctx.getChannel(), wrappedStream, handler); } } else { // if its null, skip those bytes buffer.readerIndex(markedReaderIndex + size); } if (buffer.readerIndex() != expectedIndexReader) { if (buffer.readerIndex() < expectedIndexReader) { logger.warn( "Message not fully read (response) for [{}] handler {}, error [{}], resetting", requestId, handler, TransportStatus.isError(status)); } else { logger.warn( "Message read past expected size (response) for [{}] handler {}, error [{}], resetting", requestId, handler, TransportStatus.isError(status)); } buffer.readerIndex(expectedIndexReader); } } wrappedStream.close(); }
@Override public void writeComplete(ChannelHandlerContext ctx, WriteCompletionEvent e) throws Exception { transportServiceAdapter.sent(e.getWrittenAmount()); super.writeComplete(ctx, e); }
/** * simulate an error for the given requestId, unlike {@link #handleLocalError(long, Throwable)} * and {@link #handleRemoteError(long, Throwable)}, the provided exception will not be wrapped but * will be delivered to the transport layer as is * * @param requestId the id corresponding to the captured send request * @param e the failure */ public void handleError(final long requestId, final TransportException e) { adapter.onResponseReceived(requestId).handleException(e); }
/** simulate a response for the given requestId */ public void handleResponse(final long requestId, final TransportResponse response) { adapter.onResponseReceived(requestId).handleResponse(response); }
private void handleRequest( StreamInput stream, long requestId, int messageLengthBytes, LocalTransport sourceTransport, Version version) throws Exception { stream = new NamedWriteableAwareStreamInput(stream, namedWriteableRegistry); final String action = stream.readString(); transportServiceAdapter.onRequestReceived(requestId, action); inFlightRequestsBreaker() .addEstimateBytesAndMaybeBreak(messageLengthBytes, "<transport_request>"); final LocalTransportChannel transportChannel = new LocalTransportChannel( this, transportServiceAdapter, sourceTransport, action, requestId, version, messageLengthBytes); try { final RequestHandlerRegistry reg = transportServiceAdapter.getRequestHandler(action); if (reg == null) { throw new ActionNotFoundTransportException("Action [" + action + "] not found"); } final TransportRequest request = reg.newRequest(); request.remoteAddress(sourceTransport.boundAddress.publishAddress()); request.readFrom(stream); if (ThreadPool.Names.SAME.equals(reg.getExecutor())) { //noinspection unchecked reg.processMessageReceived(request, transportChannel); } else { threadPool .executor(reg.getExecutor()) .execute( new AbstractRunnable() { @Override protected void doRun() throws Exception { //noinspection unchecked reg.processMessageReceived(request, transportChannel); } @Override public boolean isForceExecution() { return reg.isForceExecution(); } @Override public void onFailure(Throwable e) { if (lifecycleState() == Lifecycle.State.STARTED) { // we can only send a response transport is started.... try { transportChannel.sendResponse(e); } catch (Throwable e1) { logger.warn( "Failed to send error message back to client for action [{}]", e1, action); logger.warn("Actual Exception", e); } } } }); } } catch (Throwable e) { try { transportChannel.sendResponse(e); } catch (Throwable e1) { logger.warn("Failed to send error message back to client for action [{}]", e, action); logger.warn("Actual Exception", e1); } } }