Example #1
0
 @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;
    }
Example #3
0
 /**
  * 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));
 }
Example #4
0
  @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;
  }
Example #5
0
  @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));
      }
    }
  }
Example #7
0
 @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())
       + '}';
 }
Example #8
0
 /** 此方法会在接收到服务器数据后调用 */
 public void channelRead0(ChannelHandlerContext ctx, ByteBuf in) {
   System.out.println("Client received: " + ByteBufUtil.hexDump(in.readBytes(in.readableBytes())));
 }
Example #9
0
  @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);
    }
  }
Example #10
0
  @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();
      }
    }
  }
Example #11
0
  @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();
    }
  }