Ejemplo n.º 1
0
 @Test
 @Ignore(
     "Very simple microbenchmark to compare different writeTo implementations. Only for development thus "
         + "ignored.")
 public void testWriteToMicrobenchmark() throws IOException {
   int capacity = 1024 * 128;
   int iterations = 100;
   int testRuns = 10;
   byte[] bytes = new byte[capacity];
   ThreadLocalRandom.current().nextBytes(bytes);
   ByteBuffer buffer = BufferUtil.allocate(capacity);
   BufferUtil.append(buffer, bytes, 0, capacity);
   long startTest = System.nanoTime();
   for (int i = 0; i < testRuns; i++) {
     long start = System.nanoTime();
     for (int j = 0; j < iterations; j++) {
       ByteArrayOutputStream out = new ByteArrayOutputStream();
       long startRun = System.nanoTime();
       BufferUtil.writeTo(buffer.asReadOnlyBuffer(), out);
       long elapsedRun = System.nanoTime() - startRun;
       //                LOG.warn("run elapsed={}ms", elapsedRun / 1000);
       assertThat(
           "Bytes in out equal bytes in buffer",
           Arrays.equals(bytes, out.toByteArray()),
           is(true));
     }
     long elapsed = System.nanoTime() - start;
     LOG.warn("elapsed={}ms average={}ms", elapsed / 1000, elapsed / iterations / 1000);
   }
   LOG.warn(
       "overall average: {}ms", (System.nanoTime() - startTest) / testRuns / iterations / 1000);
 }
Ejemplo n.º 2
0
  private void handle(SelectionKey key) throws IOException {
    // TODO Auto-generated method stub

    ServerSocketChannel server = null;
    SocketChannel client = null;
    String receiveText = null;
    int count = 0;

    if (key.isAcceptable()) {
      // client require accept events
      server = (ServerSocketChannel) key.channel();
      client = server.accept();
      client.configureBlocking(false);
      client.register(selector, SelectionKey.OP_READ);
    } else if (key.isReadable()) {
      // 如果是read事件,则直接读取
      client = (SocketChannel) key.channel();
      recBuffer.clear();
      count = client.read(recBuffer);
      if (count > 0) {
        recBuffer.flip();
        receiveText = decode.decode(recBuffer.asReadOnlyBuffer()).toString();
        System.out.println(client.toString() + ":" + receiveText);
        sendBuffer.clear();
        sendBuffer.put((sdf.format(new Date()) + "服务器收到你的消息").getBytes());
        sendBuffer.flip();
        client.write(sendBuffer);
        dispatch(client, receiveText);
        client = (SocketChannel) key.channel();
        client.register(selector, SelectionKey.OP_READ);
      }
    }
  }
Ejemplo n.º 3
0
  private static void printRecords(Direction direction, ByteBuffer buf) {
    while (buf.hasRemaining()) {
      byte type = buf.get();
      byte v1 = buf.get();
      byte v2 = buf.get();
      int len = buf.getShort();

      ByteBuffer data = buf.asReadOnlyBuffer();
      buf.position(buf.position() + len);
      data.limit(data.position() + len);

      System.out.println(
          direction + " Record type=" + type + " version=" + v1 + "." + v2 + " len=" + len);

      switch (type) {
        case CONTENTTYPE_HANDSHAKE:
          printHandshakeRecord(data, ((direction == Direction.IN) ? inEnc : outEnc));
          break;
        case CONTENTTYPE_ALERT:
          printAlertRecord(data);
          break;
        case CONTENTTYPE_CHANGECIPHERSPEC:
          System.out.println("  Change Cipher Spec");
          printRecordBytes(data);
          if (direction == Direction.OUT) outEnc = true;
          else inEnc = true;
          break;
        default:
          printRecordBytes(data);
          break;
      }
    }
  }
Ejemplo n.º 4
0
  static void printHandshakeRecord(ByteBuffer buf, boolean enc) {
    while (buf.hasRemaining()) {

      if (enc) {
        System.out.println("  Handshake Encrypted");
        printRecordBytes(buf);
        continue;
      }

      byte typeByte = buf.get();
      HandshakeType type = HandshakeType.getTypeByCode(typeByte);
      buf.get();
      short len = buf.getShort(); // uint24

      ByteBuffer data = buf.asReadOnlyBuffer();
      data.limit(len + data.position());
      buf.position(buf.position() + len);

      if (type != null) {
        type.parse(data);
      } else {
        System.out.println("  Handshake type=" + typeByte);
        printRecordBytes(data);
      }
    }
  }
Ejemplo n.º 5
0
 /**
  * Blocking send of content.
  *
  * @param content The content to send.
  * @throws IOException
  */
 public void sendContent(ByteBuffer content) throws IOException {
   final BlockingCallback callback = _channel.getWriteBlockingCallback();
   if (content.hasArray() && content.limit() < content.capacity())
     content = content.asReadOnlyBuffer();
   _channel.write(content, true, callback);
   callback.block();
 }
Ejemplo n.º 6
0
 /**
  * Constructs from a buffer of data and some metadata.
  *
  * @param data the binary data
  * @param metadata metadata, such as mime type, creation date, or the like
  */
 public RawData(ByteBuffer data, Map<String, List<String>> metadata) {
   this.data = data.asReadOnlyBuffer();
   Map<String, List<String>> tmpMetaData;
   if (metadata == null) {
     tmpMetaData = new HashMap<>();
   } else {
     tmpMetaData = metadata;
   }
   this.metadata = ImmutableMap.<String, List<String>>builder().putAll(tmpMetaData).build();
 }
Ejemplo n.º 7
0
 private void testWriteToWithBufferThatDoesNotExposeArray(int capacity) throws IOException {
   ByteArrayOutputStream out = new ByteArrayOutputStream();
   byte[] bytes = new byte[capacity];
   ThreadLocalRandom.current().nextBytes(bytes);
   ByteBuffer buffer = BufferUtil.allocate(capacity);
   BufferUtil.append(buffer, bytes, 0, capacity);
   BufferUtil.writeTo(buffer.asReadOnlyBuffer(), out);
   assertThat(
       "Bytes in out equal bytes in buffer", Arrays.equals(bytes, out.toByteArray()), is(true));
 }
Ejemplo n.º 8
0
  @Override
  protected boolean afterReceived(ByteBuffer buffer) {
    // TODO: 以后要考虑 Header 太长导致 Host 不在第一个 buffer 里的情况
    if (firstBufferReceived) {
      return false;
    }

    onParsedHost(HttpHeaderParser.parseHost(buffer.asReadOnlyBuffer()));

    firstBufferReceived = true;
    return false;
  }
Ejemplo n.º 9
0
 public static String getString(ByteBuffer buffer) {
   Charset charset = null;
   CharsetDecoder decoder = null;
   CharBuffer charBuffer = null;
   try {
     charset = Charset.forName("UTF-8");
     decoder = charset.newDecoder();
     // charBuffer = decoder.decode(buffer);//用这个的话,只能输出来一次结果,第二次显示为空
     charBuffer = decoder.decode(buffer.asReadOnlyBuffer());
     return charBuffer.toString();
   } catch (Exception ex) {
     ex.printStackTrace();
     return "";
   }
 }
Ejemplo n.º 10
0
  /**
   * ************************************************************************* See description of
   * superclass/interface.
   *
   * @see ch.skyguide.message.structure.AbstractGenericDataBlock#encode()
   */
  @Override
  public ByteBuffer encode() {
    ByteBuffer dataBlockBuffer = ByteBuffer.allocate(getFieldLengthIndicator());

    dataBlockBuffer.put((byte) getDataCategory());
    dataBlockBuffer.putShort((short) getFieldLengthIndicator());

    for (GenericRecord currentRecord : m_records) {
      dataBlockBuffer.put(currentRecord.encode());
    }

    dataBlockBuffer.flip();

    return dataBlockBuffer.asReadOnlyBuffer();
  }
    /* ------------------------------------------------------------ */
    @Override
    public ByteBuffer getDirectBuffer() {
      ByteBuffer buffer = _directBuffer.get();
      if (buffer == null) {
        ByteBuffer buffer2 = ResourceCache.this.getDirectBuffer(_resource);

        if (buffer2 == null) LOG.warn("Could not load " + this);
        else if (_directBuffer.compareAndSet(null, buffer2)) {
          buffer = buffer2;

          if (!BufferUtil.isMappedBuffer(buffer)
              && _cachedSize.addAndGet(BufferUtil.length(buffer)) > _maxCacheSize) shrinkCache();
        } else buffer = _directBuffer.get();
      }
      if (buffer == null) return null;
      return buffer.asReadOnlyBuffer();
    }
Ejemplo n.º 12
0
  /**
   * Asynchronous send of content.
   *
   * @param content The content to send
   * @param callback The callback to use to notify success or failure
   */
  public void sendContent(ByteBuffer content, final Callback callback) {
    if (content.hasArray() && content.limit() < content.capacity())
      content = content.asReadOnlyBuffer();
    _channel.write(
        content,
        true,
        new Callback() {
          @Override
          public void succeeded() {
            closed();
            callback.succeeded();
          }

          @Override
          public void failed(Throwable x) {
            callback.failed(x);
          }
        });
  }
Ejemplo n.º 13
0
    /**
     * {@inheritDoc}
     *
     * <p>Sends the given {@code message} to all local members of the specified channel.
     *
     * <p>TBD: (optimization) this method should handle sending multiple messages to a given
     * channel.
     */
    public void send(byte[] channelId, byte[] message) {
      callStarted();
      try {
        if (logger.isLoggable(Level.FINEST)) {
          logger.log(
              Level.FINEST,
              "send channelId:{0} message:{1}",
              HexDumper.toHexString(channelId),
              HexDumper.format(message, 0x50));
        }
        /*
         * TBD: (optimization) this should enqueue the send
         * request and return immediately so that the
         * coordinator can receive the acknowledgment and
         * continue processing of the event queue.  Right now,
         * process the send request inline here.
         */
        BigInteger channelRefId = new BigInteger(1, channelId);
        Set<BigInteger> localMembers = localChannelMembersMap.get(channelRefId);
        if (localMembers == null) {
          // TBD: there should be local channel members.
          // What error should be reported here?
          return;
        }

        ByteBuffer msg = ByteBuffer.allocate(3 + channelId.length + message.length);
        msg.put(SimpleSgsProtocol.CHANNEL_MESSAGE)
            .putShort((short) channelId.length)
            .put(channelId)
            .put(message)
            .flip();

        for (BigInteger sessionRefId : localMembers) {
          sessionService.sendProtocolMessageNonTransactional(
              sessionRefId, msg.asReadOnlyBuffer(), Delivery.RELIABLE);
        }

      } finally {
        callFinished();
      }
    }
Ejemplo n.º 14
0
    /**
     * {@inheritDoc}
     *
     * <p>Removes the channel from the per-channel cache of local member sessions, and sends a
     * CHANNEL_LEAVE protocol message to the channel's local member sessions.
     */
    public void leaveAll(byte[] channelId) {
      callStarted();
      try {
        if (logger.isLoggable(Level.FINEST)) {
          logger.log(Level.FINEST, "leaveAll channelId:{0}", HexDumper.toHexString(channelId));
        }
        BigInteger channelRefId = new BigInteger(1, channelId);
        Set<BigInteger> localMembers;
        localMembers = localChannelMembersMap.remove(channelRefId);
        if (localMembers != null) {
          ByteBuffer msg = ByteBuffer.allocate(1 + channelId.length);
          msg.put(SimpleSgsProtocol.CHANNEL_LEAVE).put(channelId).flip();
          for (BigInteger sessionRefId : localMembers) {
            sessionService.sendProtocolMessageNonTransactional(
                sessionRefId, msg.asReadOnlyBuffer(), Delivery.RELIABLE);
          }
        }

      } finally {
        callFinished();
      }
    }
Ejemplo n.º 15
0
    /**
     * {@inheritDoc}
     *
     * <p>Removes the specified {@code sessionId} from the per-channel cache for the given channel's
     * local member sessions, and sends a CHANNEL_LEAVE protocol message to the session with the
     * corresponding {@code sessionId}.
     */
    public void leave(byte[] channelId, byte[] sessionId) {
      callStarted();
      try {
        if (logger.isLoggable(Level.FINEST)) {
          logger.log(
              Level.FINEST,
              "leave channelId:{0} sessionId:{1}",
              HexDumper.toHexString(channelId),
              HexDumper.toHexString(sessionId));
        }

        // Update local channel membership cache.
        BigInteger channelRefId = new BigInteger(1, channelId);
        Set<BigInteger> localMembers;
        localMembers = localChannelMembersMap.get(channelRefId);
        if (localMembers == null) {
          return;
        }
        BigInteger sessionRefId = new BigInteger(1, sessionId);
        localMembers.remove(sessionRefId);

        // Update per-session channel set cache.
        Set<BigInteger> channelSet = localPerSessionChannelsMap.get(sessionRefId);
        if (channelSet != null) {
          channelSet.remove(channelRefId);
        }

        // Send CHANNEL_LEAVE protocol message.
        ByteBuffer msg = ByteBuffer.allocate(1 + channelId.length);
        msg.put(SimpleSgsProtocol.CHANNEL_LEAVE).put(channelId).flip();
        sessionService.sendProtocolMessageNonTransactional(
            sessionRefId, msg.asReadOnlyBuffer(), Delivery.RELIABLE);
      } finally {
        callFinished();
      }
    }
Ejemplo n.º 16
0
  /**
   * Core implementation updates the {@link Adler32} checksum from the data in the buffer. The
   * position, mark, and limit are unchanged by this operation. The operation is optimized when the
   * buffer is backed by an array.
   *
   * @param buf
   * @param pos
   * @param limit
   */
  protected void update(final ByteBuffer buf, final int pos, final int limit) {

    assert buf != null;
    assert pos >= 0;
    assert limit > pos;

    // reset before computing the checksum.
    //        chk.reset();

    if (buf.hasArray()) {

      /*
       * Optimized when the buffer is backed by an array.
       */

      final byte[] bytes = buf.array();

      final int len = limit - pos;

      if (pos > bytes.length - len) {

        throw new BufferUnderflowException();
      }

      chk.update(bytes, pos + buf.arrayOffset(), len);

    } else {

      /*
       * Compute the checksum of a byte[] on the native heap.
       *
       * @todo this should be a JNI call. The Adler32 class is already a
       * JNI implementation.
       */

      if (a == null) {

        a = new byte[512];
      }

      // isolate changes to (pos,limit).
      final ByteBuffer b = buf.asReadOnlyBuffer();

      // stopping point.
      final int m = limit;

      // update checksum a chunk at a time.
      for (int p = pos; p < m; p += a.length) {

        // #of bytes to copy into the local byte[].
        final int len = Math.min(m - p, a.length);

        // set the view
        b.limit(p + len);
        b.position(p);

        // copy into Java heap byte[], advancing b.position().
        b.get(a, 0 /* off */, len);

        // update the running checksum.
        chk.update(a, 0 /* off */, len);
      }

      // This is WAY to expensive since it is a JNI call per byte.
      //
      //            for (int i = pos; i < limit; i++) {
      //
      //                chk.update(buf.get(i));
      //
      //            }

    }
  }
Ejemplo n.º 17
0
 @Override
 public ByteBuffer asReadOnlyByteBuffer() {
   return buffer.asReadOnlyBuffer();
 }
Ejemplo n.º 18
0
 public ByteBuffer getDataBuffer() {
   return (dataBuffer != null) ? dataBuffer.asReadOnlyBuffer() : null;
 }
Ejemplo n.º 19
0
    /**
     * {@inheritDoc}
     *
     * <p>Reads the local membership list for the specified {@code channelId}, and updates the local
     * membership cache for that channel. If any join or leave notifications were missed, then send
     * the appropriate CHANNEL_JOIN or CHANNEL_LEAVE protocol message to the effected session(s).
     */
    public void refresh(String name, byte[] channelId) {
      callStarted();
      if (logger.isLoggable(Level.FINE)) {
        logger.log(Level.FINE, "refreshing channelId:{0}", HexDumper.toHexString(channelId));
      }
      try {
        BigInteger channelRefId = new BigInteger(1, channelId);
        GetLocalMembersTask getMembersTask = new GetLocalMembersTask(channelRefId);
        try {
          transactionScheduler.runTask(getMembersTask, taskOwner);
        } catch (Exception e) {
          // FIXME: what is the right thing to do here?
          logger.logThrow(
              Level.WARNING,
              e,
              "obtaining members of channel:{0} throws",
              HexDumper.toHexString(channelId));
        }
        Set<BigInteger> newLocalMembers =
            Collections.synchronizedSet(getMembersTask.getLocalMembers());
        if (logger.isLoggable(Level.FINEST)) {
          logger.log(
              Level.FINEST, "newLocalMembers for channel:{0}", HexDumper.toHexString(channelId));
          for (BigInteger sessionRefId : newLocalMembers) {
            logger.log(
                Level.FINEST, "member:{0}", HexDumper.toHexString(sessionRefId.toByteArray()));
          }
        }

        /*
         * Determine which join and leave events were missed and
         * send protocol messages to clients accordingly.
         */
        Set<BigInteger> oldLocalMembers = localChannelMembersMap.put(channelRefId, newLocalMembers);
        Set<BigInteger> joiners = null;
        Set<BigInteger> leavers = null;
        if (oldLocalMembers == null) {
          joiners = newLocalMembers;
        } else {
          for (BigInteger sessionRefId : newLocalMembers) {
            if (oldLocalMembers.contains(sessionRefId)) {
              oldLocalMembers.remove(sessionRefId);
            } else {
              if (joiners == null) {
                joiners = new HashSet<BigInteger>();
              }
              joiners.add(sessionRefId);
            }
          }
          if (!oldLocalMembers.isEmpty()) {
            leavers = oldLocalMembers;
          }
        }
        if (joiners != null) {
          for (BigInteger sessionRefId : joiners) {
            MessageBuffer msg =
                new MessageBuffer(1 + MessageBuffer.getSize(name) + channelId.length);
            msg.putByte(SimpleSgsProtocol.CHANNEL_JOIN).putString(name).putBytes(channelId);
            sessionService.sendProtocolMessageNonTransactional(
                sessionRefId,
                ByteBuffer.wrap(msg.getBuffer()).asReadOnlyBuffer(),
                Delivery.RELIABLE);
          }
        }
        if (leavers != null) {
          for (BigInteger sessionRefId : leavers) {
            ByteBuffer msg = ByteBuffer.allocate(1 + channelId.length);
            msg.put(SimpleSgsProtocol.CHANNEL_LEAVE).put(channelId).flip();
            sessionService.sendProtocolMessageNonTransactional(
                sessionRefId, msg.asReadOnlyBuffer(), Delivery.RELIABLE);
          }
        }

      } finally {
        callFinished();
      }
    }
Ejemplo n.º 20
0
  protected boolean parseContent(ByteBuffer buffer) {
    // Handle _content
    byte ch;
    while (_state.ordinal() < State.END.ordinal() && buffer.hasRemaining()) {
      switch (_state) {
        case EOF_CONTENT:
          _contentChunk = buffer.asReadOnlyBuffer();
          _contentPosition += _contentChunk.remaining();
          buffer.position(buffer.position() + _contentChunk.remaining());
          if (_handler.content(_contentChunk)) return true;
          break;

        case CONTENT:
          {
            long remaining = _contentLength - _contentPosition;
            if (remaining == 0) {
              setState(State.END);
              if (_handler.messageComplete()) return true;
            } else {
              _contentChunk = buffer.asReadOnlyBuffer();

              // limit content by expected size
              if (_contentChunk.remaining() > remaining) {
                // We can cast remaining to an int as we know that it is smaller than
                // or equal to length which is already an int.
                _contentChunk.limit(_contentChunk.position() + (int) remaining);
              }

              _contentPosition += _contentChunk.remaining();
              buffer.position(buffer.position() + _contentChunk.remaining());

              boolean handle = _handler.content(_contentChunk);
              if (_contentPosition == _contentLength) {
                setState(State.END);
                if (_handler.messageComplete()) return true;
              }
              if (handle) return true;
            }
            break;
          }

        case CHUNKED_CONTENT:
          {
            ch = next(buffer);
            if (ch > HttpTokens.SPACE) {
              _chunkLength = TypeUtil.convertHexDigit(ch);
              _chunkPosition = 0;
              setState(State.CHUNK_SIZE);
            }

            break;
          }

        case CHUNK_SIZE:
          {
            ch = next(buffer);
            if (ch == 0) break;
            if (ch == HttpTokens.LINE_FEED) {
              if (_chunkLength == 0) {
                setState(State.END);
                if (_handler.messageComplete()) return true;
              } else setState(State.CHUNK);
            } else if (ch <= HttpTokens.SPACE || ch == HttpTokens.SEMI_COLON)
              setState(State.CHUNK_PARAMS);
            else _chunkLength = _chunkLength * 16 + TypeUtil.convertHexDigit(ch);
            break;
          }

        case CHUNK_PARAMS:
          {
            ch = next(buffer);
            if (ch == HttpTokens.LINE_FEED) {
              if (_chunkLength == 0) {
                setState(State.END);
                if (_handler.messageComplete()) return true;
              } else setState(State.CHUNK);
            }
            break;
          }

        case CHUNK:
          {
            int remaining = _chunkLength - _chunkPosition;
            if (remaining == 0) {
              setState(State.CHUNKED_CONTENT);
            } else {
              _contentChunk = buffer.asReadOnlyBuffer();

              if (_contentChunk.remaining() > remaining)
                _contentChunk.limit(_contentChunk.position() + remaining);
              remaining = _contentChunk.remaining();

              _contentPosition += remaining;
              _chunkPosition += remaining;
              buffer.position(buffer.position() + remaining);
              if (_handler.content(_contentChunk)) return true;
            }
            break;
          }

        case CLOSED:
          {
            BufferUtil.clear(buffer);
            return false;
          }

        default:
          break;
      }
    }
    return false;
  }
Ejemplo n.º 21
0
 public ByteBuffer getData() {
   return data.asReadOnlyBuffer();
 }
Ejemplo n.º 22
0
 /**
  * Retrieves a read-only version of this buffer.
  *
  * <p><i>Trying to manually prepare this buffer for writing is discouraged. If this buffer needs
  * to be written, please use {@link #getWritableBuffer()}.</i>
  *
  * @return the buffer
  */
 public ByteBuffer getByteBuffer() {
   return buf.asReadOnlyBuffer();
 }
    @Override
    public void onFillable() {
      if (_latch != null) {
        try {
          _latch.await();
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }

      Callback blocking = _blockingRead;
      if (blocking != null) {
        _blockingRead = null;
        blocking.succeeded();
        return;
      }

      EndPoint _endp = getEndPoint();
      try {
        _last = System.currentTimeMillis();
        boolean progress = true;
        while (progress) {
          progress = false;

          // Fill the input buffer with everything available
          BufferUtil.compact(_in);
          if (BufferUtil.isFull(_in))
            throw new IllegalStateException("FULL " + BufferUtil.toDetailString(_in));
          int filled = _endp.fill(_in);
          if (filled > 0) progress = true;

          // If the tests wants to block, then block
          while (_blockAt > 0 && _endp.isOpen() && _in.remaining() < _blockAt) {
            FutureCallback future = _blockingRead = new FutureCallback();
            fillInterested();
            future.get();
            filled = _endp.fill(_in);
            progress |= filled > 0;
          }

          // Copy to the out buffer
          if (BufferUtil.hasContent(_in) && BufferUtil.append(_out, _in) > 0) progress = true;

          // Blocking writes
          if (BufferUtil.hasContent(_out)) {
            ByteBuffer out = _out.duplicate();
            BufferUtil.clear(_out);
            for (int i = 0; i < _writeCount; i++) {
              FutureCallback blockingWrite = new FutureCallback();
              _endp.write(blockingWrite, out.asReadOnlyBuffer());
              blockingWrite.get();
            }
            progress = true;
          }

          // are we done?
          if (_endp.isInputShutdown()) _endp.shutdownOutput();
        }

        if (_endp.isOpen()) fillInterested();
      } catch (ExecutionException e) {
        // Timeout does not close, so echo exception then shutdown
        try {
          FutureCallback blockingWrite = new FutureCallback();
          _endp.write(blockingWrite, BufferUtil.toBuffer("EE: " + BufferUtil.toString(_in)));
          blockingWrite.get();
          _endp.shutdownOutput();
        } catch (Exception e2) {
          // e2.printStackTrace();
        }
      } catch (InterruptedException | EofException e) {
        Log.getRootLogger().ignore(e);
      } catch (Exception e) {
        Log.getRootLogger().warn(e);
      } finally {
      }
    }
Ejemplo n.º 24
0
  protected boolean parseContent(ByteBuffer buffer) {
    int remaining = buffer.remaining();
    if (remaining == 0 && _state == State.CONTENT) {
      long content = _contentLength - _contentPosition;
      if (content == 0) {
        setState(State.END);
        return _handler.messageComplete();
      }
    }

    // Handle _content
    byte ch;
    while (_state.ordinal() < State.END.ordinal() && remaining > 0) {
      switch (_state) {
        case EOF_CONTENT:
          _contentChunk = buffer.asReadOnlyBuffer();
          _contentPosition += remaining;
          buffer.position(buffer.position() + remaining);
          if (_handler.content(_contentChunk)) return true;
          break;

        case CONTENT:
          {
            long content = _contentLength - _contentPosition;
            if (content == 0) {
              setState(State.END);
              return _handler.messageComplete();
            } else {
              _contentChunk = buffer.asReadOnlyBuffer();

              // limit content by expected size
              if (remaining > content) {
                // We can cast remaining to an int as we know that it is
                // smaller than
                // or equal to length which is already an int.
                _contentChunk.limit(_contentChunk.position() + (int) content);
              }

              _contentPosition += _contentChunk.remaining();
              buffer.position(buffer.position() + _contentChunk.remaining());

              if (_handler.content(_contentChunk)) return true;

              if (_contentPosition == _contentLength) {
                setState(State.END);
                return _handler.messageComplete();
              }
            }
            break;
          }

        case CHUNKED_CONTENT:
          {
            ch = next(buffer);
            if (ch > HttpTokens.SPACE) {
              _chunkLength = TypeUtils.convertHexDigit(ch);
              _chunkPosition = 0;
              setState(State.CHUNK_SIZE);
            }

            break;
          }

        case CHUNK_SIZE:
          {
            ch = next(buffer);
            if (ch == 0) break;
            if (ch == HttpTokens.LINE_FEED) {
              if (_chunkLength == 0) setState(State.CHUNK_END);
              else setState(State.CHUNK);
            } else if (ch <= HttpTokens.SPACE || ch == HttpTokens.SEMI_COLON)
              setState(State.CHUNK_PARAMS);
            else _chunkLength = _chunkLength * 16 + TypeUtils.convertHexDigit(ch);
            break;
          }

        case CHUNK_PARAMS:
          {
            ch = next(buffer);
            if (ch == HttpTokens.LINE_FEED) {
              if (_chunkLength == 0) setState(State.CHUNK_END);
              else setState(State.CHUNK);
            }
            break;
          }

        case CHUNK:
          {
            int chunk = _chunkLength - _chunkPosition;
            if (chunk == 0) {
              setState(State.CHUNKED_CONTENT);
            } else {
              _contentChunk = buffer.asReadOnlyBuffer();

              if (remaining > chunk) _contentChunk.limit(_contentChunk.position() + chunk);
              chunk = _contentChunk.remaining();

              _contentPosition += chunk;
              _chunkPosition += chunk;
              buffer.position(buffer.position() + chunk);
              if (_handler.content(_contentChunk)) return true;
            }
            break;
          }

        case CHUNK_END:
          {
            // TODO handle chunk trailer
            ch = next(buffer);
            if (ch == 0) break;
            if (ch == HttpTokens.LINE_FEED) {
              setState(State.END);
              return _handler.messageComplete();
            }
            throw new IllegalCharacterException(_state, ch, buffer);
          }

        case CLOSED:
          {
            BufferUtils.clear(buffer);
            return false;
          }

        default:
          break;
      }

      remaining = buffer.remaining();
    }
    return false;
  }