@Override public int hashCode() { int result = uuid != null ? uuid.hashCode() : 0; result = 31 * result + (type != null ? type.hashCode() : 0); result = 31 * result + (buffer != null ? ByteBufUtil.hashCode(buffer) : 0); return result; }
/** * Decodes the client connection preface string from the input buffer. * * @return {@code true} if processing of the client preface string is complete. Since client * preface strings can only be received by servers, returns true immediately for client * endpoints. */ private boolean readClientPrefaceString(ByteBuf in) throws Http2Exception { if (clientPrefaceString == null) { return true; } int prefaceRemaining = clientPrefaceString.readableBytes(); int bytesRead = min(in.readableBytes(), prefaceRemaining); // If the input so far doesn't match the preface, break the connection. if (bytesRead == 0 || !ByteBufUtil.equals( in, in.readerIndex(), clientPrefaceString, clientPrefaceString.readerIndex(), bytesRead)) { String receivedBytes = hexDump( in, in.readerIndex(), min(in.readableBytes(), clientPrefaceString.readableBytes())); throw connectionError( PROTOCOL_ERROR, "HTTP/2 client preface string missing or corrupt. " + "Hex dump for received bytes: %s", receivedBytes); } in.skipBytes(bytesRead); clientPrefaceString.skipBytes(bytesRead); if (!clientPrefaceString.isReadable()) { // Entire preface has been read. clientPrefaceString.release(); clientPrefaceString = null; return true; } return false; }
/** * Converts the specified Netty {@link ByteBuf} into an {@link HttpData}. Unlike {@link * #of(byte[])}, this method makes a copy of the {@link ByteBuf}. * * @return a new {@link HttpData}. {@link #EMPTY_DATA} if the readable bytes of {@code buf} is 0. */ static HttpData of(ByteBuf buf) { requireNonNull(buf, "buf"); if (!buf.isReadable()) { return EMPTY_DATA; } return of(ByteBufUtil.getBytes(buf)); }
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; QueueMessage that = (QueueMessage) o; if (buffer != null ? !ByteBufUtil.equals(buffer, that.buffer) : that.buffer != null) return false; if (type != that.type) return false; if (uuid != null ? !uuid.equals(that.uuid) : that.uuid != null) return false; return true; }
@Override protected void encodeInitialLine(final ByteBuf buf, final HttpMessage message) throws Exception { if (message instanceof HttpRequest) { HttpRequest request = (HttpRequest) message; ByteBufUtil.writeAscii(buf, request.method().asciiName()); buf.writeByte(SP); buf.writeBytes(request.uri().getBytes(CharsetUtil.UTF_8)); buf.writeByte(SP); ByteBufUtil.writeAscii(buf, request.protocolVersion().text()); buf.writeBytes(CRLF); } else if (message instanceof HttpResponse) { HttpResponse response = (HttpResponse) message; ByteBufUtil.writeAscii(buf, response.protocolVersion().text()); buf.writeByte(SP); buf.writeBytes(String.valueOf(response.status().code()).getBytes(CharsetUtil.US_ASCII)); buf.writeByte(SP); ByteBufUtil.writeAscii(buf, response.status().reasonPhrase()); buf.writeBytes(CRLF); } else { throw new UnsupportedMessageTypeException( "Unsupported type " + StringUtil.simpleClassName(message)); } }
private void handleWebsocket( final OutPacketMessage msg, ChannelHandlerContext ctx, ChannelPromise promise) throws IOException { while (true) { Queue<Packet> queue = msg.getClientHead().getPacketsQueue(msg.getTransport()); Packet packet = queue.poll(); if (packet == null) { promise.setSuccess(); break; } final ByteBuf out = encoder.allocateBuffer(ctx.alloc()); encoder.encodePacket(packet, out, ctx.alloc(), true); WebSocketFrame res = new TextWebSocketFrame(out); if (log.isTraceEnabled()) { log.trace( "Out message: {} sessionId: {}", out.toString(CharsetUtil.UTF_8), msg.getSessionId()); } if (out.isReadable()) { ctx.channel().writeAndFlush(res, promise); } else { promise.setSuccess(); out.release(); } for (ByteBuf buf : packet.getAttachments()) { ByteBuf outBuf = encoder.allocateBuffer(ctx.alloc()); outBuf.writeByte(4); outBuf.writeBytes(buf); if (log.isTraceEnabled()) { log.trace( "Out attachment: {} sessionId: {}", ByteBufUtil.hexDump(outBuf), msg.getSessionId()); } ctx.channel().writeAndFlush(new BinaryWebSocketFrame(outBuf)); } } }
@Override public String toString() { if (refCnt() == 0) { return "SctpFrame{" + "streamIdentifier=" + streamIdentifier + ", protocolIdentifier=" + protocolIdentifier + ", unordered=" + unordered + ", data=(FREED)}"; } return "SctpFrame{" + "streamIdentifier=" + streamIdentifier + ", protocolIdentifier=" + protocolIdentifier + ", unordered=" + unordered + ", data=" + ByteBufUtil.hexDump(content()) + '}'; }
/** 此方法会在接收到服务器数据后调用 */ public void channelRead0(ChannelHandlerContext ctx, ByteBuf in) { System.out.println("Client received: " + ByteBufUtil.hexDump(in.readBytes(in.readableBytes()))); }
@Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws SSLException { final int startOffset = in.readerIndex(); final int endOffset = in.writerIndex(); int offset = startOffset; int totalLength = 0; // If we calculated the length of the current SSL record before, use that information. if (packetLength > 0) { if (endOffset - startOffset < packetLength) { return; } else { offset += packetLength; totalLength = packetLength; packetLength = 0; } } boolean nonSslRecord = false; while (totalLength < OpenSslEngine.MAX_ENCRYPTED_PACKET_LENGTH) { final int readableBytes = endOffset - offset; if (readableBytes < 5) { break; } final int packetLength = getEncryptedPacketLength(in, offset); if (packetLength == -1) { nonSslRecord = true; break; } assert packetLength > 0; if (packetLength > readableBytes) { // wait until the whole packet can be read this.packetLength = packetLength; break; } int newTotalLength = totalLength + packetLength; if (newTotalLength > OpenSslEngine.MAX_ENCRYPTED_PACKET_LENGTH) { // Don't read too much. break; } // We have a whole packet. // Increment the offset to handle the next packet. offset += packetLength; totalLength = newTotalLength; } if (totalLength > 0) { boolean decoded = false; // The buffer contains one or more full SSL records. // Slice out the whole packet so unwrap will only be called with complete packets. // Also directly reset the packetLength. This is needed as unwrap(..) may trigger // decode(...) again via: // 1) unwrap(..) is called // 2) wrap(...) is called from within unwrap(...) // 3) wrap(...) calls unwrapLater(...) // 4) unwrapLater(...) calls decode(...) // // See https://github.com/netty/netty/issues/1534 in.skipBytes(totalLength); // If SSLEngine expects a heap buffer for unwrapping, do the conversion. if (in.isDirect() && wantsInboundHeapBuffer) { ByteBuf copy = ctx.alloc().heapBuffer(totalLength); try { copy.writeBytes(in, startOffset, totalLength); decoded = unwrap(ctx, copy, 0, totalLength); } finally { copy.release(); } } else { decoded = unwrap(ctx, in, startOffset, totalLength); } if (!firedChannelRead) { // Check first if firedChannelRead is not set yet as it may have been set in a // previous decode(...) call. firedChannelRead = decoded; } } if (nonSslRecord) { // Not an SSL/TLS packet NotSslRecordException e = new NotSslRecordException("not an SSL/TLS record: " + ByteBufUtil.hexDump(in)); in.skipBytes(in.readableBytes()); ctx.fireExceptionCaught(e); setHandshakeFailure(ctx, e); } }
@Override public void inboundBufferUpdated(final ChannelHandlerContext ctx) throws Exception { final ByteBuf in = ctx.inboundByteBuffer(); if (in.readableBytes() < 5) { return; } int packetLength = getEncryptedPacketLength(in); if (packetLength == -1) { // Bad data - discard the buffer and raise an exception. NotSslRecordException e = new NotSslRecordException("not an SSL/TLS record: " + ByteBufUtil.hexDump(in)); in.skipBytes(in.readableBytes()); ctx.fireExceptionCaught(e); setHandshakeFailure(e); return; } assert packetLength > 0; final ByteBuf out = ctx.nextInboundByteBuffer(); out.discardReadBytes(); boolean wrapLater = false; int bytesProduced = 0; try { loop: for (; ; ) { SSLEngineResult result = unwrap(engine, in, out); bytesProduced += result.bytesProduced(); switch (result.getStatus()) { case CLOSED: // notify about the CLOSED state of the SSLEngine. See #137 sslCloseFuture.setClosed(); break; case BUFFER_UNDERFLOW: break loop; } switch (result.getHandshakeStatus()) { case NEED_UNWRAP: break; case NEED_WRAP: wrapLater = true; break; case NEED_TASK: runDelegatedTasks(); break; case FINISHED: setHandshakeSuccess(); wrapLater = true; continue; case NOT_HANDSHAKING: break; default: throw new IllegalStateException( "Unknown handshake status: " + result.getHandshakeStatus()); } if (result.bytesConsumed() == 0 && result.bytesProduced() == 0) { break; } } if (wrapLater) { flush(ctx, ctx.newFuture()); } } catch (SSLException e) { setHandshakeFailure(e); throw e; } finally { if (bytesProduced > 0) { in.discardReadBytes(); ctx.fireInboundBufferUpdated(); } } }
@Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws SSLException { // Keeps the list of the length of every SSL record in the input buffer. int[] recordLengths = null; int nRecords = 0; final int startOffset = in.readerIndex(); final int endOffset = in.writerIndex(); int offset = startOffset; // If we calculated the length of the current SSL record before, use that information. if (packetLength > 0) { if (endOffset - startOffset < packetLength) { return; } else { recordLengths = new int[4]; recordLengths[0] = packetLength; nRecords = 1; offset += packetLength; packetLength = 0; } } boolean nonSslRecord = false; for (; ; ) { final int readableBytes = endOffset - offset; if (readableBytes < 5) { break; } final int packetLength = getEncryptedPacketLength(in, offset); if (packetLength == -1) { nonSslRecord = true; break; } assert packetLength > 0; if (packetLength > readableBytes) { // wait until the whole packet can be read this.packetLength = packetLength; break; } // We have a whole packet. // Remember the length of the current packet. if (recordLengths == null) { recordLengths = new int[4]; } if (nRecords == recordLengths.length) { recordLengths = Arrays.copyOf(recordLengths, recordLengths.length << 1); } recordLengths[nRecords++] = packetLength; // Increment the offset to handle the next packet. offset += packetLength; } final int totalLength = offset - startOffset; if (totalLength > 0) { // The buffer contains one or more full SSL records. // Slice out the whole packet so unwrap will only be called with complete packets. // Also directly reset the packetLength. This is needed as unwrap(..) may trigger // decode(...) again via: // 1) unwrap(..) is called // 2) wrap(...) is called from within unwrap(...) // 3) wrap(...) calls unwrapLater(...) // 4) unwrapLater(...) calls decode(...) // // See https://github.com/netty/netty/issues/1534 in.skipBytes(totalLength); ByteBuffer buffer = in.nioBuffer(startOffset, totalLength); unwrapMultiple(ctx, buffer, totalLength, recordLengths, nRecords, out); } if (nonSslRecord) { // Not an SSL/TLS packet NotSslRecordException e = new NotSslRecordException("not an SSL/TLS record: " + ByteBufUtil.hexDump(in)); in.skipBytes(in.readableBytes()); ctx.fireExceptionCaught(e); setHandshakeFailure(e); } }
@Test public void stressTest() throws Exception { final Http2Headers headers = dummyHeaders(); final String pingMsg = "12345678"; int length = 10; final ByteBuf data = randomBytes(length); final String dataAsHex = ByteBufUtil.hexDump(data); final ByteBuf pingData = Unpooled.copiedBuffer(pingMsg, UTF_8); final int numStreams = 2000; // Collect all the ping buffers as we receive them at the server. final String[] receivedPings = new String[numStreams]; doAnswer( new Answer<Void>() { int nextIndex; @Override public Void answer(InvocationOnMock in) throws Throwable { receivedPings[nextIndex++] = ((ByteBuf) in.getArguments()[1]).toString(UTF_8); return null; } }) .when(serverListener) .onPingRead(any(ChannelHandlerContext.class), any(ByteBuf.class)); // Collect all the data buffers as we receive them at the server. final StringBuilder[] receivedData = new StringBuilder[numStreams]; doAnswer( new Answer<Integer>() { @Override public Integer answer(InvocationOnMock in) throws Throwable { int streamId = (Integer) in.getArguments()[1]; ByteBuf buf = (ByteBuf) in.getArguments()[2]; int padding = (Integer) in.getArguments()[3]; int processedBytes = buf.readableBytes() + padding; int streamIndex = (streamId - 3) / 2; StringBuilder builder = receivedData[streamIndex]; if (builder == null) { builder = new StringBuilder(dataAsHex.length()); receivedData[streamIndex] = builder; } builder.append(ByteBufUtil.hexDump(buf)); return processedBytes; } }) .when(serverListener) .onDataRead( any(ChannelHandlerContext.class), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean()); try { bootstrapEnv(numStreams * length, 1, numStreams * 4, numStreams); runInChannel( clientChannel, new Http2Runnable() { @Override public void run() { int upperLimit = 3 + 2 * numStreams; for (int streamId = 3; streamId < upperLimit; streamId += 2) { // Send a bunch of data on each stream. http2Client .encoder() .writeHeaders( ctx(), streamId, headers, 0, (short) 16, false, 0, false, newPromise()); http2Client .encoder() .writePing(ctx(), false, pingData.slice().retain(), newPromise()); http2Client .encoder() .writeData(ctx(), streamId, data.slice().retain(), 0, false, newPromise()); // Write trailers. http2Client .encoder() .writeHeaders( ctx(), streamId, headers, 0, (short) 16, false, 0, true, newPromise()); } } }); // Wait for all frames to be received. assertTrue(serverSettingsAckLatch.await(60, SECONDS)); assertTrue(trailersLatch.await(60, SECONDS)); verify(serverListener, times(numStreams)) .onHeadersRead( any(ChannelHandlerContext.class), anyInt(), eq(headers), eq(0), eq((short) 16), eq(false), eq(0), eq(false)); verify(serverListener, times(numStreams)) .onHeadersRead( any(ChannelHandlerContext.class), anyInt(), eq(headers), eq(0), eq((short) 16), eq(false), eq(0), eq(true)); verify(serverListener, times(numStreams)) .onPingRead(any(ChannelHandlerContext.class), any(ByteBuf.class)); verify(serverListener, never()) .onDataRead( any(ChannelHandlerContext.class), anyInt(), any(ByteBuf.class), eq(0), eq(true)); for (StringBuilder builder : receivedData) { assertEquals(dataAsHex, builder.toString()); } for (String receivedPing : receivedPings) { assertEquals(pingMsg, receivedPing); } } finally { data.release(); pingData.release(); } }