@Override public void channelActive(final ChannelHandlerContext ctx) throws Exception { log.info("ECHO active " + NioUdtProvider.socketUDT(ctx.channel()).toStringOptions()); final MessageBuf<Object> out = ctx.nextOutboundMessageBuffer(); out.add(message); ctx.flush(); }
@Override protected void messageReceived(final ChannelHandlerContext ctx, final UdtMessage message) throws Exception { final ByteBuf byteBuf = message.data(); meter.mark(byteBuf.readableBytes()); final MessageBuf<Object> out = ctx.nextOutboundMessageBuffer(); out.add(message); ctx.flush(); }
@Override public final void flushNow() { if (inFlushNow || flushTaskInProgress != null) { return; } inFlushNow = true; ChannelHandlerContext ctx = directOutboundContext(); Throwable cause = null; try { if (ctx.hasOutboundByteBuffer()) { ByteBuf out = ctx.outboundByteBuffer(); int oldSize = out.readableBytes(); try { doFlushByteBuffer(out); } catch (Throwable t) { cause = t; } finally { final int newSize = out.readableBytes(); final int writtenBytes = oldSize - newSize; if (writtenBytes > 0) { flushFutureNotifier.increaseWriteCounter(writtenBytes); if (newSize == 0) { out.discardReadBytes(); } } } } else { MessageBuf<Object> out = ctx.outboundMessageBuffer(); int oldSize = out.size(); try { doFlushMessageBuffer(out); } catch (Throwable t) { cause = t; } finally { flushFutureNotifier.increaseWriteCounter(oldSize - out.size()); } } if (cause == null) { flushFutureNotifier.notifyFlushFutures(); } else { flushFutureNotifier.notifyFlushFutures(cause); if (cause instanceof IOException) { close(voidFuture()); } } } finally { inFlushNow = false; } }
@Before public void initialize() { MockitoAnnotations.initMocks(this); when(context.nextInboundMessageBuffer()).thenReturn(buf); when(buf.add(anyObject())).thenReturn(true); when(context.nextOutboundMessageBuffer()).thenReturn(outboundBuf); }
@Override public void flush(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { ByteBuf in = ctx.outboundByteBuffer(); try { MessageBuf<Object> out = ctx.nextOutboundMessageBuffer(); ByteBuf payload = Unpooled.buffer(in.readableBytes()); payload.writeBytes(in); out.add(new SctpMessage(streamIdentifier, protocolIdentifier, payload)); in.discardReadBytes(); } catch (Throwable t) { ctx.fireExceptionCaught(new EncoderException(t)); } ctx.flush(promise); }
@Override protected int doReadMessages(MessageBuf<Object> buf) throws Exception { DatagramChannel ch = javaChannel(); ByteBuf buffer = alloc().directBuffer(config().getReceivePacketSize()); boolean free = true; try { ByteBuffer data = buffer.nioBuffer(buffer.writerIndex(), buffer.writableBytes()); InetSocketAddress remoteAddress = (InetSocketAddress) ch.receive(data); if (remoteAddress == null) { return 0; } buf.add( new DatagramPacket( buffer.writerIndex(buffer.writerIndex() + data.remaining()), remoteAddress)); free = false; return 1; } catch (Throwable cause) { if (cause instanceof Error) { throw (Error) cause; } if (cause instanceof RuntimeException) { throw (RuntimeException) cause; } if (cause instanceof Exception) { throw (Exception) cause; } throw new ChannelException(cause); } finally { if (free) { buffer.free(); } } }
@Override protected int doWriteMessages(MessageBuf<Object> buf, boolean lastSpin) throws Exception { DatagramPacket packet = (DatagramPacket) buf.peek(); ByteBuf data = packet.data(); int dataLen = data.readableBytes(); ByteBuffer nioData; if (data.nioBufferCount() == 1) { nioData = data.nioBuffer(); } else { nioData = ByteBuffer.allocate(dataLen); data.getBytes(data.readerIndex(), nioData); nioData.flip(); } final int writtenBytes = javaChannel().send(nioData, packet.remoteAddress()); final SelectionKey key = selectionKey(); final int interestOps = key.interestOps(); if (writtenBytes <= 0 && dataLen > 0) { // Did not write a packet. // 1) If 'lastSpin' is false, the caller will call this method again real soon. // - Do not update OP_WRITE. // 2) If 'lastSpin' is true, the caller will not retry. // - Set OP_WRITE so that the event loop calls flushForcibly() later. if (lastSpin) { if ((interestOps & SelectionKey.OP_WRITE) == 0) { key.interestOps(interestOps | SelectionKey.OP_WRITE); } } return 0; } // Wrote a packet. buf.remove(); // packet was written free up buffer packet.free(); if (buf.isEmpty()) { // Wrote the outbound buffer completely - clear OP_WRITE. if ((interestOps & SelectionKey.OP_WRITE) != 0) { key.interestOps(interestOps & ~SelectionKey.OP_WRITE); } } return 1; }
@Override protected int doReadMessages(MessageBuf<Object> buf) throws Exception { SctpChannel ch = javaChannel().accept(); if (ch == null) { return 0; } buf.add(new NioSctpChannel(this, null, ch)); return 1; }
@Override protected void doWriteMessages(MessageBuf<Object> buf) throws Exception { DatagramPacket p = (DatagramPacket) buf.poll(); ByteBuf data = p.data(); int length = data.readableBytes(); InetSocketAddress remote = p.remoteAddress(); if (remote != null) { tmpPacket.setSocketAddress(remote); } if (data.hasArray()) { tmpPacket.setData(data.array(), data.arrayOffset() + data.readerIndex(), length); } else { byte[] tmp = new byte[length]; data.getBytes(data.readerIndex(), tmp); tmpPacket.setData(tmp); } socket.send(tmpPacket); }
@Override protected int doReadMessages(MessageBuf<Object> buf) throws Exception { if (readSuspended) { try { Thread.sleep(SO_TIMEOUT); } catch (InterruptedException e) { // ignore; } return 0; } int packetSize = config().getReceivePacketSize(); byte[] data = new byte[packetSize]; tmpPacket.setData(data); try { socket.receive(tmpPacket); InetSocketAddress remoteAddr = (InetSocketAddress) tmpPacket.getSocketAddress(); if (remoteAddr == null) { remoteAddr = remoteAddress(); } buf.add( new DatagramPacket( Unpooled.wrappedBuffer(data, tmpPacket.getOffset(), tmpPacket.getLength()), remoteAddr)); if (readSuspended) { return 0; } else { return 1; } } catch (SocketTimeoutException e) { // Expected return 0; } catch (SocketException e) { if (!e.getMessage().toLowerCase(Locale.US).contains("socket closed")) { throw e; } return -1; } }
@Override protected void decode( ChannelHandlerContext ctx, SpdyDataOrControlFrame msg, MessageBuf<Object> out) throws Exception { if (msg instanceof SpdySynStreamFrame) { // HTTP requests/responses are mapped one-to-one to SPDY streams. SpdySynStreamFrame spdySynStreamFrame = (SpdySynStreamFrame) msg; int streamId = spdySynStreamFrame.getStreamId(); if (SpdyCodecUtil.isServerId(streamId)) { // SYN_STREAM frames initiated by the server are pushed resources int associatedToStreamId = spdySynStreamFrame.getAssociatedToStreamId(); // If a client receives a SYN_STREAM with an Associated-To-Stream-ID of 0 // it must reply with a RST_STREAM with error code INVALID_STREAM if (associatedToStreamId == 0) { SpdyRstStreamFrame spdyRstStreamFrame = new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.INVALID_STREAM); ctx.write(spdyRstStreamFrame); } String URL = SpdyHeaders.getUrl(spdyVersion, spdySynStreamFrame); // If a client receives a SYN_STREAM without a 'url' header // it must reply with a RST_STREAM with error code PROTOCOL_ERROR if (URL == null) { SpdyRstStreamFrame spdyRstStreamFrame = new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.PROTOCOL_ERROR); ctx.write(spdyRstStreamFrame); } try { FullHttpResponse httpResponseWithEntity = createHttpResponse(spdyVersion, spdySynStreamFrame); // Set the Stream-ID, Associated-To-Stream-ID, Priority, and URL as headers SpdyHttpHeaders.setStreamId(httpResponseWithEntity, streamId); SpdyHttpHeaders.setAssociatedToStreamId(httpResponseWithEntity, associatedToStreamId); SpdyHttpHeaders.setPriority(httpResponseWithEntity, spdySynStreamFrame.getPriority()); SpdyHttpHeaders.setUrl(httpResponseWithEntity, URL); if (spdySynStreamFrame.isLast()) { HttpHeaders.setContentLength(httpResponseWithEntity, 0); out.add(httpResponseWithEntity); } else { // Response body will follow in a series of Data Frames putMessage(streamId, httpResponseWithEntity); } } catch (Exception e) { SpdyRstStreamFrame spdyRstStreamFrame = new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.PROTOCOL_ERROR); ctx.write(spdyRstStreamFrame); } } else { // SYN_STREAM frames initiated by the client are HTTP requests try { FullHttpRequest httpRequestWithEntity = createHttpRequest(spdyVersion, spdySynStreamFrame); // Set the Stream-ID as a header SpdyHttpHeaders.setStreamId(httpRequestWithEntity, streamId); if (spdySynStreamFrame.isLast()) { out.add(httpRequestWithEntity); } else { // Request body will follow in a series of Data Frames putMessage(streamId, httpRequestWithEntity); } } catch (Exception e) { // If a client sends a SYN_STREAM without all of the getMethod, url (host and path), // scheme, and version headers the server must reply with a HTTP 400 BAD REQUEST reply. // Also sends HTTP 400 BAD REQUEST reply if header name/value pairs are invalid SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamId); spdySynReplyFrame.setLast(true); SpdyHeaders.setStatus(spdyVersion, spdySynReplyFrame, HttpResponseStatus.BAD_REQUEST); SpdyHeaders.setVersion(spdyVersion, spdySynReplyFrame, HttpVersion.HTTP_1_0); ctx.write(spdySynReplyFrame); } } } else if (msg instanceof SpdySynReplyFrame) { SpdySynReplyFrame spdySynReplyFrame = (SpdySynReplyFrame) msg; int streamId = spdySynReplyFrame.getStreamId(); try { FullHttpResponse httpResponseWithEntity = createHttpResponse(spdyVersion, spdySynReplyFrame); // Set the Stream-ID as a header SpdyHttpHeaders.setStreamId(httpResponseWithEntity, streamId); if (spdySynReplyFrame.isLast()) { HttpHeaders.setContentLength(httpResponseWithEntity, 0); out.add(httpResponseWithEntity); } else { // Response body will follow in a series of Data Frames putMessage(streamId, httpResponseWithEntity); } } catch (Exception e) { // If a client receives a SYN_REPLY without valid getStatus and version headers // the client must reply with a RST_STREAM frame indicating a PROTOCOL_ERROR SpdyRstStreamFrame spdyRstStreamFrame = new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.PROTOCOL_ERROR); ctx.write(spdyRstStreamFrame); } } else if (msg instanceof SpdyHeadersFrame) { SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg; int streamId = spdyHeadersFrame.getStreamId(); FullHttpMessage fullHttpMessage = getMessage(streamId); // If message is not in map discard HEADERS frame. if (fullHttpMessage == null) { return; } for (Map.Entry<String, String> e : spdyHeadersFrame.headers().entries()) { fullHttpMessage.headers().add(e.getKey(), e.getValue()); } if (spdyHeadersFrame.isLast()) { HttpHeaders.setContentLength(fullHttpMessage, fullHttpMessage.content().readableBytes()); removeMessage(streamId); out.add(fullHttpMessage); } } else if (msg instanceof SpdyDataFrame) { SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg; int streamId = spdyDataFrame.getStreamId(); FullHttpMessage fullHttpMessage = getMessage(streamId); // If message is not in map discard Data Frame. if (fullHttpMessage == null) { return; } ByteBuf content = fullHttpMessage.content(); if (content.readableBytes() > maxContentLength - spdyDataFrame.content().readableBytes()) { removeMessage(streamId); throw new TooLongFrameException( "HTTP content length exceeded " + maxContentLength + " bytes."); } ByteBuf spdyDataFrameData = spdyDataFrame.content(); int spdyDataFrameDataLen = spdyDataFrameData.readableBytes(); content.writeBytes(spdyDataFrameData, spdyDataFrameData.readerIndex(), spdyDataFrameDataLen); if (spdyDataFrame.isLast()) { HttpHeaders.setContentLength(fullHttpMessage, content.readableBytes()); removeMessage(streamId); out.add(fullHttpMessage); } } else if (msg instanceof SpdyRstStreamFrame) { SpdyRstStreamFrame spdyRstStreamFrame = (SpdyRstStreamFrame) msg; int streamId = spdyRstStreamFrame.getStreamId(); removeMessage(streamId); } }
@Override protected void decode(ChannelHandlerContext ctx, HttpObject msg, MessageBuf<Object> out) throws Exception { FullHttpMessage currentMessage = this.currentMessage; if (msg instanceof HttpMessage) { assert currentMessage == null; HttpMessage m = (HttpMessage) msg; // Handle the 'Expect: 100-continue' header if necessary. // TODO: Respond with 413 Request Entity Too Large // and discard the traffic or close the connection. // No need to notify the upstream handlers - just log. // If decoding a response, just throw an exception. if (is100ContinueExpected(m)) { ctx.write(CONTINUE.duplicate()); } if (!m.getDecoderResult().isSuccess()) { removeTransferEncodingChunked(m); this.currentMessage = null; out.add(BufUtil.retain(m)); return; } if (msg instanceof HttpRequest) { HttpRequest header = (HttpRequest) msg; this.currentMessage = currentMessage = new DefaultFullHttpRequest( header.getProtocolVersion(), header.getMethod(), header.getUri(), Unpooled.compositeBuffer(maxCumulationBufferComponents)); } else if (msg instanceof HttpResponse) { HttpResponse header = (HttpResponse) msg; this.currentMessage = currentMessage = new DefaultFullHttpResponse( header.getProtocolVersion(), header.getStatus(), Unpooled.compositeBuffer(maxCumulationBufferComponents)); } else { throw new Error(); } currentMessage.headers().set(m.headers()); // A streamed message - initialize the cumulative buffer, and wait for incoming chunks. removeTransferEncodingChunked(currentMessage); } else if (msg instanceof HttpContent) { assert currentMessage != null; // Merge the received chunk into the content of the current message. HttpContent chunk = (HttpContent) msg; CompositeByteBuf content = (CompositeByteBuf) currentMessage.content(); if (content.readableBytes() > maxContentLength - chunk.content().readableBytes()) { // TODO: Respond with 413 Request Entity Too Large // and discard the traffic or close the connection. // No need to notify the upstream handlers - just log. // If decoding a response, just throw an exception. throw new TooLongFrameException( "HTTP content length exceeded " + maxContentLength + " bytes."); } // Append the content of the chunk if (chunk.content().isReadable()) { chunk.retain(); content.addComponent(chunk.content()); content.writerIndex(content.writerIndex() + chunk.content().readableBytes()); } final boolean last; if (!chunk.getDecoderResult().isSuccess()) { currentMessage.setDecoderResult(DecoderResult.failure(chunk.getDecoderResult().cause())); last = true; } else { last = chunk instanceof LastHttpContent; } if (last) { this.currentMessage = null; // Merge trailing headers into the message. if (chunk instanceof LastHttpContent) { LastHttpContent trailer = (LastHttpContent) chunk; currentMessage.headers().add(trailer.trailingHeaders()); } // Set the 'Content-Length' header. currentMessage .headers() .set(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(content.readableBytes())); // All done out.add(currentMessage); } } else { throw new Error(); } }