Пример #1
0
  /**
   * Handles post-mortem of writes that have failed.
   *
   * @param cause the cause of the failure.
   * @param propagateErrorIfRequired if {@code true} and {@code cause} is not an instance of {@link
   *     Exception}, the error is propagated through the netty pipeline.
   */
  private void handleChannelWriteFailure(Throwable cause, boolean propagateErrorIfRequired) {
    long writeFailureProcessingStartTime = System.currentTimeMillis();
    try {
      nettyMetrics.channelWriteError.inc();
      Exception exception;
      if (!(cause instanceof Exception)) {
        logger.warn("Encountered a throwable on channel write failure", cause);
        exception = new IllegalStateException("Encountered a Throwable - " + cause.getMessage());
        if (propagateErrorIfRequired) {
          // we can't ignore throwables - so we let Netty deal with it.
          ctx.fireExceptionCaught(cause);
          nettyMetrics.throwableCount.inc();
        }
      } else {
        exception = (Exception) cause;
      }
      onResponseComplete(exception);

      logger.trace("Cleaning up remaining chunks on write failure");
      Chunk chunk = chunksAwaitingCallback.poll();
      while (chunk != null) {
        chunk.resolveChunk(exception);
        chunk = chunksAwaitingCallback.poll();
      }
      chunk = chunksToWrite.poll();
      while (chunk != null) {
        chunksToWriteCount.decrementAndGet();
        chunk.resolveChunk(exception);
        chunk = chunksToWrite.poll();
      }
    } finally {
      nettyMetrics.channelWriteFailureProcessingTimeInMs.update(
          System.currentTimeMillis() - writeFailureProcessingStartTime);
    }
  }
Пример #2
0
 /** Is called when a write timeout was detected */
 protected void writeTimedOut(ChannelHandlerContext ctx) throws Exception {
   if (!closed) {
     ctx.fireExceptionCaught(WriteTimeoutException.INSTANCE);
     ctx.close();
     closed = true;
   }
 }
 @Override
 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
   if (cause instanceof IOException) {
     if (LOGGER.isDebugEnabled()) {
       LOGGER.debug(
           logIdent(ctx, endpoint) + "Connection reset by peer: " + cause.getMessage(), cause);
     } else {
       LOGGER.info(logIdent(ctx, endpoint) + "Connection reset by peer: " + cause.getMessage());
     }
     handleOutstandingOperations(ctx);
   } else if (cause instanceof DecoderException
       && cause.getCause() instanceof SSLHandshakeException) {
     if (!connectFuture.isDone()) {
       connectFuture.setFailure(cause.getCause());
     } else {
       // This should not be possible, since handshake is done before connecting. But just in case,
       // we
       // can trap and log an error that might slip through for one reason or another.
       LOGGER.warn(
           logIdent(ctx, endpoint)
               + "Caught SSL exception after being connected: "
               + cause.getMessage(),
           cause);
     }
   } else {
     LOGGER.warn(
         logIdent(ctx, endpoint) + "Caught unknown exception: " + cause.getMessage(), cause);
     ctx.fireExceptionCaught(cause);
   }
 }
Пример #4
0
  @Override
  public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    if (cause instanceof SpdyProtocolException) {
      issueSessionError(ctx, SpdySessionStatus.PROTOCOL_ERROR);
    }

    ctx.fireExceptionCaught(cause);
  }
Пример #5
0
  @Override
  public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {

    if (heartBeat != null) {
      heartBeat.cancel(true);
      heartBeat = null;
    }
    ctx.fireExceptionCaught(cause);
  }
Пример #6
0
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
      super.channelInactive(ctx);

      if (failOnMissingResponse) {
        long missingResponses = requestResponseCounter.get();
        if (missingResponses > 0) {
          ctx.fireExceptionCaught(
              new PrematureChannelClosureException(
                  "channel gone inactive with " + missingResponses + " missing response(s)"));
        }
      }
    }
Пример #7
0
  public boolean decode(
      ChannelHandlerContext ctx,
      final ByteBuf buf,
      InetSocketAddress recipient,
      final InetSocketAddress sender) {

    LOG.debug("Decoding of TomP2P starts now. Readable: {}.", buf.readableBytes());

    try {
      final int readerBefore = buf.readerIndex();
      // set the sender of this message for handling timeout
      final Attribute<InetSocketAddress> attributeInet = ctx.attr(INET_ADDRESS_KEY);
      attributeInet.set(sender);

      if (message == null && !headerDone) {
        headerDone = decodeHeader(buf, recipient, sender);
        if (headerDone) {
          // store the sender as an attribute
          final Attribute<PeerAddress> attributePeerAddress = ctx.attr(PEER_ADDRESS_KEY);
          attributePeerAddress.set(message.sender());
          message.udp(ctx.channel() instanceof DatagramChannel);
          if (message.isFireAndForget() && message.isUdp()) {
            TimeoutFactory.removeTimeout(ctx);
          }
        } else {
          return false;
        }
      }

      final boolean donePayload = decodePayload(buf);
      decodeSignature(buf, readerBefore, donePayload);

      if (donePayload) {
        boolean isRelay = message.sender().isRelayed();
        if (isRelay && !message.peerSocketAddresses().isEmpty()) {
          PeerAddress tmpSender =
              message.sender().changePeerSocketAddresses(message.peerSocketAddresses());
          message.sender(tmpSender);
        }
      }

      // see https://github.com/netty/netty/issues/1976
      buf.discardSomeReadBytes();
      return donePayload;

    } catch (Exception e) {
      ctx.fireExceptionCaught(e);
      e.printStackTrace();
      return true;
    }
  }
Пример #8
0
 @Override
 public void run() {
   // Was not written yet so issue a write timeout
   // The promise itself will be failed with a ClosedChannelException once the close() was issued
   // See https://github.com/netty/netty/issues/2159
   if (!promise.isDone()) {
     try {
       writeTimedOut(ctx);
     } catch (Throwable t) {
       ctx.fireExceptionCaught(t);
     }
   }
   removeWriteTimeoutTask(this);
 }
Пример #9
0
 @Override
 public void channelRead0(ChannelHandlerContext ctx, Iterable<OFMessage> msgList)
     throws Exception {
   for (OFMessage ofm : msgList) {
     try {
       // Do the actual packet processing
       state.processOFMessage(ofm);
     } catch (Exception ex) {
       // We are the last handler in the stream, so run the
       // exception through the channel again by passing in
       // ctx.getChannel().
       ctx.fireExceptionCaught(ex);
     }
   }
 }
  @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);
  }
Пример #11
0
  @Override
  public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    if (ignoreException(cause)) {
      // It is safe to ignore the 'connection reset by peer' or
      // 'broken pipe' error after sending close_notify.
      if (logger.isDebugEnabled()) {
        logger.debug(
            "Swallowing a harmless 'connection reset by peer / broken pipe' error that occurred "
                + "while writing close_notify in response to the peer's close_notify",
            cause);
      }

      // Close the connection explicitly just in case the transport
      // did not close the connection automatically.
      if (ctx.channel().isActive()) {
        ctx.close();
      }
    } else {
      ctx.fireExceptionCaught(cause);
    }
  }
Пример #12
0
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
      Preconditions.checkState(
          acceptMessage(msg),
          "Incorrect response type %s, %s.",
          msg.getClass().getCanonicalName(),
          msg);

      RPCProtoMessage response = (RPCProtoMessage) msg;
      Protocol.Status status = ((Protocol.Response) response.getMessage()).getStatus();
      if (!Status.isOk(status)) {
        ctx.fireExceptionCaught(
            new IOException(
                String.format(
                    "Failed to read block %d from %s with status %s.",
                    mId, mAddress, status.toString())));
      }
      mLock.lock();
      try {
        Preconditions.checkState(mPacketReaderException == null);
        DataBuffer dataBuffer = response.getPayloadDataBuffer();
        ByteBuf buf;
        if (dataBuffer == null) {
          buf = ctx.alloc().buffer(0, 0);
        } else {
          Preconditions.checkState(dataBuffer.getLength() > 0);
          assert dataBuffer.getNettyOutput() instanceof ByteBuf;
          buf = (ByteBuf) dataBuffer.getNettyOutput();
        }
        mPackets.offer(buf);
        mNotEmptyOrFailed.signal();

        if (tooManyPacketsPending()) {
          pause();
        }
      } finally {
        mLock.unlock();
      }
    }
Пример #13
0
  private void handleOutboundMessage(ChannelHandlerContext ctx, Object msg, ChannelPromise promise)
      throws Exception {
    if (msg instanceof SpdyDataFrame) {

      SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg;
      int streamId = spdyDataFrame.streamId();

      // Frames must not be sent on half-closed streams
      if (spdySession.isLocalSideClosed(streamId)) {
        spdyDataFrame.release();
        promise.setFailure(PROTOCOL_EXCEPTION);
        return;
      }

      /*
       * SPDY Data frame flow control processing requirements:
       *
       * Sender must not send a data frame with data length greater
       * than the transfer window size.
       *
       * After sending each data frame, the sender decrements its
       * transfer window size by the amount of data transmitted.
       *
       * When the window size becomes less than or equal to 0, the
       * sender must pause transmitting data frames.
       */

      int dataLength = spdyDataFrame.content().readableBytes();
      int sendWindowSize = spdySession.getSendWindowSize(streamId);
      int sessionSendWindowSize = spdySession.getSendWindowSize(SPDY_SESSION_STREAM_ID);
      sendWindowSize = Math.min(sendWindowSize, sessionSendWindowSize);

      if (sendWindowSize <= 0) {
        // Stream is stalled -- enqueue Data frame and return
        spdySession.putPendingWrite(streamId, new SpdySession.PendingWrite(spdyDataFrame, promise));
        return;
      } else if (sendWindowSize < dataLength) {
        // Stream is not stalled but we cannot send the entire frame
        spdySession.updateSendWindowSize(streamId, -1 * sendWindowSize);
        spdySession.updateSendWindowSize(SPDY_SESSION_STREAM_ID, -1 * sendWindowSize);

        // Create a partial data frame whose length is the current window size
        SpdyDataFrame partialDataFrame =
            new DefaultSpdyDataFrame(
                streamId, spdyDataFrame.content().readSlice(sendWindowSize).retain());

        // Enqueue the remaining data (will be the first frame queued)
        spdySession.putPendingWrite(streamId, new SpdySession.PendingWrite(spdyDataFrame, promise));

        // The transfer window size is pre-decremented when sending a data frame downstream.
        // Close the session on write failures that leave the transfer window in a corrupt state.
        final ChannelHandlerContext context = ctx;
        ctx.write(partialDataFrame)
            .addListener(
                new ChannelFutureListener() {
                  @Override
                  public void operationComplete(ChannelFuture future) throws Exception {
                    if (!future.isSuccess()) {
                      issueSessionError(context, SpdySessionStatus.INTERNAL_ERROR);
                    }
                  }
                });
        return;
      } else {
        // Window size is large enough to send entire data frame
        spdySession.updateSendWindowSize(streamId, -1 * dataLength);
        spdySession.updateSendWindowSize(SPDY_SESSION_STREAM_ID, -1 * dataLength);

        // The transfer window size is pre-decremented when sending a data frame downstream.
        // Close the session on write failures that leave the transfer window in a corrupt state.
        final ChannelHandlerContext context = ctx;
        promise.addListener(
            new ChannelFutureListener() {
              @Override
              public void operationComplete(ChannelFuture future) throws Exception {
                if (!future.isSuccess()) {
                  issueSessionError(context, SpdySessionStatus.INTERNAL_ERROR);
                }
              }
            });
      }

      // Close the local side of the stream if this is the last frame
      if (spdyDataFrame.isLast()) {
        halfCloseStream(streamId, false, promise);
      }

    } else if (msg instanceof SpdySynStreamFrame) {

      SpdySynStreamFrame spdySynStreamFrame = (SpdySynStreamFrame) msg;
      int streamId = spdySynStreamFrame.streamId();

      if (isRemoteInitiatedId(streamId)) {
        promise.setFailure(PROTOCOL_EXCEPTION);
        return;
      }

      byte priority = spdySynStreamFrame.priority();
      boolean remoteSideClosed = spdySynStreamFrame.isUnidirectional();
      boolean localSideClosed = spdySynStreamFrame.isLast();
      if (!acceptStream(streamId, priority, remoteSideClosed, localSideClosed)) {
        promise.setFailure(PROTOCOL_EXCEPTION);
        return;
      }

    } else if (msg instanceof SpdySynReplyFrame) {

      SpdySynReplyFrame spdySynReplyFrame = (SpdySynReplyFrame) msg;
      int streamId = spdySynReplyFrame.streamId();

      // Frames must not be sent on half-closed streams
      if (!isRemoteInitiatedId(streamId) || spdySession.isLocalSideClosed(streamId)) {
        promise.setFailure(PROTOCOL_EXCEPTION);
        return;
      }

      // Close the local side of the stream if this is the last frame
      if (spdySynReplyFrame.isLast()) {
        halfCloseStream(streamId, false, promise);
      }

    } else if (msg instanceof SpdyRstStreamFrame) {

      SpdyRstStreamFrame spdyRstStreamFrame = (SpdyRstStreamFrame) msg;
      removeStream(spdyRstStreamFrame.streamId(), promise);

    } else if (msg instanceof SpdySettingsFrame) {

      SpdySettingsFrame spdySettingsFrame = (SpdySettingsFrame) msg;

      int settingsMinorVersion =
          spdySettingsFrame.getValue(SpdySettingsFrame.SETTINGS_MINOR_VERSION);
      if (settingsMinorVersion >= 0 && settingsMinorVersion != minorVersion) {
        // Settings frame had the wrong minor version
        promise.setFailure(PROTOCOL_EXCEPTION);
        return;
      }

      int newConcurrentStreams =
          spdySettingsFrame.getValue(SpdySettingsFrame.SETTINGS_MAX_CONCURRENT_STREAMS);
      if (newConcurrentStreams >= 0) {
        localConcurrentStreams = newConcurrentStreams;
      }

      // Persistence flag are inconsistent with the use of SETTINGS to communicate
      // the initial window size. Remove flags from the sender requesting that the
      // value be persisted. Remove values that the sender indicates are persisted.
      if (spdySettingsFrame.isPersisted(SpdySettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE)) {
        spdySettingsFrame.removeValue(SpdySettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE);
      }
      spdySettingsFrame.setPersistValue(SpdySettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE, false);

      int newInitialWindowSize =
          spdySettingsFrame.getValue(SpdySettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE);
      if (newInitialWindowSize >= 0) {
        updateInitialReceiveWindowSize(newInitialWindowSize);
      }

    } else if (msg instanceof SpdyPingFrame) {

      SpdyPingFrame spdyPingFrame = (SpdyPingFrame) msg;
      if (isRemoteInitiatedId(spdyPingFrame.id())) {
        ctx.fireExceptionCaught(
            new IllegalArgumentException("invalid PING ID: " + spdyPingFrame.id()));
        return;
      }
      pings.getAndIncrement();

    } else if (msg instanceof SpdyGoAwayFrame) {

      // Why is this being sent? Intercept it and fail the write.
      // Should have sent a CLOSE ChannelStateEvent
      promise.setFailure(PROTOCOL_EXCEPTION);
      return;

    } else if (msg instanceof SpdyHeadersFrame) {

      SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg;
      int streamId = spdyHeadersFrame.streamId();

      // Frames must not be sent on half-closed streams
      if (spdySession.isLocalSideClosed(streamId)) {
        promise.setFailure(PROTOCOL_EXCEPTION);
        return;
      }

      // Close the local side of the stream if this is the last frame
      if (spdyHeadersFrame.isLast()) {
        halfCloseStream(streamId, false, promise);
      }

    } else if (msg instanceof SpdyWindowUpdateFrame) {

      // Why is this being sent? Intercept it and fail the write.
      promise.setFailure(PROTOCOL_EXCEPTION);
      return;
    }

    ctx.write(msg, promise);
  }
Пример #14
0
 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
   cause.printStackTrace();
   nodeCheck.remove(ctx.channel().remoteAddress().toString()); // 删除缓存
   ctx.close();
   ctx.fireExceptionCaught(cause);
 }
 @Override
 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
   ctx.fireExceptionCaught(cause);
 }
Пример #16
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();
      }
    }
  }
Пример #17
0
  @Override
  public void flush(final ChannelHandlerContext ctx, ChannelFuture future) throws Exception {
    final ByteBuf in = ctx.outboundByteBuffer();
    final ByteBuf out = ctx.nextOutboundByteBuffer();

    out.unsafe().discardSomeReadBytes();

    // Do not encrypt the first write request if this handler is
    // created with startTLS flag turned on.
    if (startTls && !sentFirstMessage) {
      sentFirstMessage = true;
      out.writeBytes(in);
      ctx.flush(future);
      return;
    }

    if (ctx.executor() == ctx.channel().eventLoop()) {
      flushFutureNotifier.addFlushFuture(future, in.readableBytes());
    } else {
      synchronized (flushFutureNotifier) {
        flushFutureNotifier.addFlushFuture(future, in.readableBytes());
      }
    }

    boolean unwrapLater = false;
    int bytesConsumed = 0;
    try {
      for (; ; ) {
        SSLEngineResult result = wrap(engine, in, out);
        bytesConsumed += result.bytesConsumed();
        if (result.getStatus() == Status.CLOSED) {
          // SSLEngine has been closed already.
          // Any further write attempts should be denied.
          if (in.readable()) {
            in.clear();
            SSLException e = new SSLException("SSLEngine already closed");
            future.setFailure(e);
            ctx.fireExceptionCaught(e);
            flush0(ctx, bytesConsumed, e);
            bytesConsumed = 0;
          }
          break;
        } else {
          switch (result.getHandshakeStatus()) {
            case NEED_WRAP:
              ctx.flush();
              continue;
            case NEED_UNWRAP:
              if (ctx.inboundByteBuffer().readable()) {
                unwrapLater = true;
              }
              break;
            case NEED_TASK:
              runDelegatedTasks();
              continue;
            case FINISHED:
              setHandshakeSuccess();
              continue;
            case NOT_HANDSHAKING:
              break;
            default:
              throw new IllegalStateException(
                  "Unknown handshake status: " + result.getHandshakeStatus());
          }

          if (result.bytesConsumed() == 0 && result.bytesProduced() == 0) {
            break;
          }
        }
      }

      if (unwrapLater) {
        inboundBufferUpdated(ctx);
      }
    } catch (SSLException e) {
      setHandshakeFailure(e);
      throw e;
    } finally {
      in.unsafe().discardSomeReadBytes();
      flush0(ctx, bytesConsumed);
    }
  }
Пример #18
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);
    }
  }
Пример #19
0
 private static void fireProtocolException(ChannelHandlerContext ctx, String message) {
   ctx.fireExceptionCaught(new SpdyProtocolException(message));
 }
Пример #20
0
 private static void fireInvalidFrameException(ChannelHandlerContext ctx) {
   ctx.fireExceptionCaught(INVALID_FRAME);
 }
Пример #21
0
  @Override
  protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out)
      throws Exception {
    switch (state) {
      case READ_COMMON_HEADER:
        state = readCommonHeader(buffer);
        if (state == State.FRAME_ERROR) {
          if (version != spdyVersion) {
            fireProtocolException(ctx, "Unsupported version: " + version);
          } else {
            fireInvalidFrameException(ctx);
          }
        }

        // FrameDecoders must consume data when producing frames
        // All length 0 frames must be generated now
        if (length == 0) {
          if (state == State.READ_DATA_FRAME) {
            SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(streamId);
            spdyDataFrame.setLast((flags & SPDY_DATA_FLAG_FIN) != 0);
            state = State.READ_COMMON_HEADER;
            out.add(spdyDataFrame);
            return;
          }
          // There are no length 0 control frames
          state = State.READ_COMMON_HEADER;
        }

        return;

      case READ_CONTROL_FRAME:
        try {
          Object frame = readControlFrame(buffer);
          if (frame != null) {
            state = State.READ_COMMON_HEADER;
            out.add(frame);
          }
          return;
        } catch (IllegalArgumentException e) {
          state = State.FRAME_ERROR;
          fireInvalidFrameException(ctx);
        }
        return;

      case READ_SETTINGS_FRAME:
        if (spdySettingsFrame == null) {
          // Validate frame length against number of entries
          if (buffer.readableBytes() < 4) {
            return;
          }
          int numEntries = getUnsignedInt(buffer, buffer.readerIndex());
          buffer.skipBytes(4);
          length -= 4;

          // Each ID/Value entry is 8 bytes
          if ((length & 0x07) != 0 || length >> 3 != numEntries) {
            state = State.FRAME_ERROR;
            fireInvalidFrameException(ctx);
            return;
          }

          spdySettingsFrame = new DefaultSpdySettingsFrame();

          boolean clear = (flags & SPDY_SETTINGS_CLEAR) != 0;
          spdySettingsFrame.setClearPreviouslyPersistedSettings(clear);
        }

        int readableEntries = Math.min(buffer.readableBytes() >> 3, length >> 3);
        for (int i = 0; i < readableEntries; i++) {
          byte ID_flags = buffer.getByte(buffer.readerIndex());
          int ID = getUnsignedMedium(buffer, buffer.readerIndex() + 1);
          int value = getSignedInt(buffer, buffer.readerIndex() + 4);
          buffer.skipBytes(8);

          if (!spdySettingsFrame.isSet(ID)) {
            boolean persistVal = (ID_flags & SPDY_SETTINGS_PERSIST_VALUE) != 0;
            boolean persisted = (ID_flags & SPDY_SETTINGS_PERSISTED) != 0;
            spdySettingsFrame.setValue(ID, value, persistVal, persisted);
          }
        }

        length -= 8 * readableEntries;
        if (length == 0) {
          state = State.READ_COMMON_HEADER;
          Object frame = spdySettingsFrame;
          spdySettingsFrame = null;
          out.add(frame);
          return;
        }
        return;

      case READ_HEADER_BLOCK_FRAME:
        try {
          spdyHeadersFrame = readHeaderBlockFrame(buffer);
          if (spdyHeadersFrame != null) {
            if (length == 0) {
              state = State.READ_COMMON_HEADER;
              Object frame = spdyHeadersFrame;
              spdyHeadersFrame = null;
              out.add(frame);
              return;
            }
            state = State.READ_HEADER_BLOCK;
          }
          return;
        } catch (IllegalArgumentException e) {
          state = State.FRAME_ERROR;
          fireInvalidFrameException(ctx);
          return;
        }

      case READ_HEADER_BLOCK:
        int compressedBytes = Math.min(buffer.readableBytes(), length);
        ByteBuf compressed = buffer.slice(buffer.readerIndex(), compressedBytes);

        try {
          headerBlockDecoder.decode(compressed, spdyHeadersFrame);
        } catch (Exception e) {
          state = State.FRAME_ERROR;
          spdyHeadersFrame = null;
          ctx.fireExceptionCaught(e);
          return;
        }

        int readBytes = compressedBytes - compressed.readableBytes();
        buffer.skipBytes(readBytes);
        length -= readBytes;

        if (spdyHeadersFrame != null
            && (spdyHeadersFrame.isInvalid() || spdyHeadersFrame.isTruncated())) {

          Object frame = spdyHeadersFrame;
          spdyHeadersFrame = null;
          if (length == 0) {
            headerBlockDecoder.reset();
            state = State.READ_COMMON_HEADER;
          }
          out.add(frame);
          return;
        }

        if (length == 0) {
          Object frame = spdyHeadersFrame;
          spdyHeadersFrame = null;
          headerBlockDecoder.reset();
          state = State.READ_COMMON_HEADER;
          if (frame != null) {
            out.add(frame);
          }
        }
        return;

      case READ_DATA_FRAME:
        if (streamId == 0) {
          state = State.FRAME_ERROR;
          fireProtocolException(ctx, "Received invalid data frame");
          return;
        }

        // Generate data frames that do not exceed maxChunkSize
        int dataLength = Math.min(maxChunkSize, length);

        // Wait until entire frame is readable
        if (buffer.readableBytes() < dataLength) {
          return;
        }

        ByteBuf data = ctx.alloc().buffer(dataLength);
        data.writeBytes(buffer, dataLength);
        SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(streamId, data);
        length -= dataLength;

        if (length == 0) {
          spdyDataFrame.setLast((flags & SPDY_DATA_FLAG_FIN) != 0);
          state = State.READ_COMMON_HEADER;
        }
        out.add(spdyDataFrame);
        return;

      case DISCARD_FRAME:
        int numBytes = Math.min(buffer.readableBytes(), length);
        buffer.skipBytes(numBytes);
        length -= numBytes;
        if (length == 0) {
          state = State.READ_COMMON_HEADER;
        }
        return;

      case FRAME_ERROR:
        buffer.skipBytes(buffer.readableBytes());
        return;

      default:
        throw new Error("Shouldn't reach here.");
    }
  }
Пример #22
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);
    }
  }