/**
   * Used to add additional HTTP message chunk content to {@link #inputContentBuffer}.
   *
   * @param requestedLen how much content should attempt to be read, <code>-1</code> means read till
   *     the end of the message.
   * @return the number of bytes actually read
   * @throws IOException if an I/O error occurs while reading content
   */
  private int fill(final int requestedLen) throws IOException {

    int read = 0;
    while ((requestedLen == -1 || read < requestedLen) && httpHeader.isExpectContent()) {

      final HttpContent c = blockingRead();

      final boolean isLast = c.isLast();
      // Check if HttpContent is chunked message trailer w/ headers
      checkHttpTrailer(c);

      final Buffer b;
      try {
        b = c.getContent();
      } catch (HttpBrokenContentException e) {
        final Throwable cause = e.getCause();
        throw Exceptions.makeIOException(cause != null ? cause : e);
      }

      read += b.remaining();
      updateInputContentBuffer(b);
      c.recycle();

      if (isLast) {
        finished();
        break;
      }
    }

    if (read > 0 || requestedLen == 0) {
      return read;
    }

    return -1;
  }
  /**
   * Appends the specified {@link Buffer} to the internal composite {@link Buffer}.
   *
   * @param httpContent the {@link HttpContent} to append
   * @return <code>true</code> if {@link ReadHandler} callback was invoked, otherwise returns <code>
   *     false</code>.
   * @throws IOException if an error occurs appending the {@link Buffer}
   */
  public boolean append(final HttpContent httpContent) throws IOException {

    // Stop waiting for data asynchronously and enable it again
    // only if we have a handler registered, which requirement
    // (expected size) is not met.
    isWaitingDataAsynchronously = false;

    // check if it's broken HTTP content message or not
    if (!HttpContent.isBroken(httpContent)) {
      final Buffer buffer = httpContent.getContent();

      if (closed) {
        buffer.dispose();
        return false;
      }

      final ReadHandler localHandler = handler;

      final boolean isLast = httpContent.isLast();

      // if we have a handler registered - switch the flag to true
      boolean askForMoreDataInThisThread = !isLast && localHandler != null;
      boolean invokeDataAvailable = false;

      if (buffer.hasRemaining()) {
        updateInputContentBuffer(buffer);
        if (localHandler != null) {
          final int available = readyData();
          if (available >= requestedSize) {
            invokeDataAvailable = true;
            askForMoreDataInThisThread = false;
          }
        }
      }

      if (askForMoreDataInThisThread) {
        // There is a ReadHandler registered, but it requested more
        // data to be available before we can notify it - so wait for
        // more data to come
        isWaitingDataAsynchronously = true;
        return true;
      }

      handler = null;

      if (isLast) {
        checkHttpTrailer(httpContent);
      }

      invokeHandlerOnProperThread(localHandler, invokeDataAvailable, isLast);

    } else { // broken content
      final ReadHandler localHandler = handler;
      handler = null;
      invokeErrorHandlerOnProperThread(
          localHandler, ((HttpBrokenContent) httpContent).getException());
    }

    return false;
  }
示例#3
0
    @Override
    public NextAction handleRead(FilterChainContext ctx) throws IOException {
      // Get the parsed HttpContent (we assume prev. filter was HTTP)
      HttpContent message = ctx.getMessage();
      Socket tunnelSocket = tunnelSockets.get(ctx.getConnection());

      if (tunnelSocket == null) {
        // handle connection procedure
        return GrizzlyModProxy.this.handleConnect(ctx, message);
      }

      if (message.getContent().hasRemaining()) {
        // relay the content to the tunnel connection

        Buffer buffer = message.getContent();
        message.recycle();

        tunnelSocket
            .getOutputStream()
            .write(buffer.array(), buffer.arrayOffset(), buffer.remaining());
      }

      return ctx.getStopAction();
    }
      @Override
      public NextAction handleRead(FilterChainContext ctx) throws IOException {

        HttpContent c = (HttpContent) ctx.getMessage();
        Buffer b = c.getContent();
        if (b.hasRemaining()) {
          sb.append(b.toStringContent());
        }

        // Last content from the server, set the future result so
        // the client can display the result and gracefully exit.
        if (c.isLast()) {
          future.result(sb.toString());
        }
        return ctx.getStopAction(); // discontinue filter chain execution
      }
  /**
   * Per-request initialization required for the InputBuffer to function properly.
   *
   * @param httpHeader the header from which input will be obtained.
   * @param ctx the FilterChainContext for the chain processing this request
   */
  public void initialize(final HttpHeader httpHeader, final FilterChainContext ctx) {

    if (ctx == null) {
      throw new IllegalArgumentException("ctx cannot be null.");
    }
    this.httpHeader = httpHeader;

    this.ctx = ctx;
    connection = ctx.getConnection();
    final Object message = ctx.getMessage();
    if (message instanceof HttpContent) {
      final HttpContent content = (HttpContent) message;

      // Check if HttpContent is chunked message trailer w/ headers
      checkHttpTrailer(content);
      updateInputContentBuffer(content.getContent());
      contentRead = content.isLast();
      content.recycle();

      if (LOGGER.isLoggable(LOGGER_LEVEL)) {
        log("InputBuffer %s initialize with ready content: %s", this, inputContentBuffer);
      }
    }
  }
  /**
   * Used to convert bytes to chars.
   *
   * @param requestedLen how much content should attempt to be read
   * @return the number of chars actually read
   * @throws IOException if an I/O error occurs while reading content
   */
  private int fillChars(final int requestedLen, final CharBuffer dst) throws IOException {

    int read = 0;

    // 1) Check pre-decoded singleCharBuf
    if (dst != singleCharBuf && singleCharBuf.hasRemaining()) {
      dst.put(singleCharBuf.get());
      read = 1;
    }

    // 2) Decode available byte[] -> char[]
    if (inputContentBuffer.hasRemaining()) {
      read += fillAvailableChars(requestedLen - read, dst);
    }

    if (read >= requestedLen) {
      dst.flip();
      return read;
    }

    // 3) If we don't expect more data - return what we've read so far
    if (!httpHeader.isExpectContent()) {
      dst.flip();
      return read > 0 ? read : -1;
    }

    // 4) Try to read more data (we may block)
    CharsetDecoder decoderLocal = getDecoder();

    boolean isNeedMoreInput =
        false; // true, if content in composite buffer is not enough to produce even 1 char
    boolean last = false;

    while (read < requestedLen && httpHeader.isExpectContent()) {

      if (isNeedMoreInput || !inputContentBuffer.hasRemaining()) {
        final HttpContent c = blockingRead();
        updateInputContentBuffer(c.getContent());
        last = c.isLast();

        c.recycle();
        isNeedMoreInput = false;
      }

      final ByteBuffer bytes = inputContentBuffer.toByteBuffer();

      final int bytesPos = bytes.position();
      final int dstPos = dst.position();

      final CoderResult result = decoderLocal.decode(bytes, dst, false);

      final int producedChars = dst.position() - dstPos;
      final int consumedBytes = bytes.position() - bytesPos;

      read += producedChars;

      if (consumedBytes > 0) {
        bytes.position(bytesPos);
        inputContentBuffer.position(inputContentBuffer.position() + consumedBytes);
        if (readAheadLimit == -1) {
          inputContentBuffer.shrink();
        }
      } else {
        isNeedMoreInput = true;
      }

      if (last || result == CoderResult.OVERFLOW) {
        break;
      }
    }

    dst.flip();

    if (last && read == 0) {
      read = -1;
    }
    return read;
  }