Ejemplo n.º 1
0
  @Override
  protected void encode(ChannelHandlerContext chc, SubscribeMessage message, ByteBuf out) {
    if (message.subscriptions().isEmpty()) {
      throw new IllegalArgumentException("Found a subscribe message with empty topics");
    }

    if (message.getQos() != AbstractMessage.QOSType.LEAST_ONE) {
      throw new IllegalArgumentException(
          "Expected a message with QOS 1, found " + message.getQos());
    }

    ByteBuf variableHeaderBuff = chc.alloc().buffer(4);
    ByteBuf buff = null;
    try {
      variableHeaderBuff.writeShort(message.getMessageID());
      for (SubscribeMessage.Couple c : message.subscriptions()) {
        variableHeaderBuff.writeBytes(Utils.encodeString(c.topicFilter));
        variableHeaderBuff.writeByte(c.qos);
      }

      int variableHeaderSize = variableHeaderBuff.readableBytes();
      byte flags = Utils.encodeFlags(message);
      buff = chc.alloc().buffer(2 + variableHeaderSize);

      buff.writeByte(AbstractMessage.SUBSCRIBE << 4 | flags);
      buff.writeBytes(Utils.encodeRemainingLength(variableHeaderSize));
      buff.writeBytes(variableHeaderBuff);

      out.writeBytes(buff);
    } finally {
      variableHeaderBuff.release();
      buff.release();
    }
  }
Ejemplo n.º 2
0
  private void handleHTTP(OutPacketMessage msg, ChannelHandlerContext ctx, ChannelPromise promise)
      throws IOException {
    Channel channel = ctx.channel();
    Attribute<Boolean> attr = channel.attr(WRITE_ONCE);

    Queue<Packet> queue = msg.getClientHead().getPacketsQueue(msg.getTransport());

    if (!channel.isActive() || queue.isEmpty() || !attr.compareAndSet(null, true)) {
      promise.setSuccess();
      return;
    }

    ByteBuf out = encoder.allocateBuffer(ctx.alloc());
    Boolean b64 = ctx.channel().attr(EncoderHandler.B64).get();
    if (b64 != null && b64) {
      Integer jsonpIndex = ctx.channel().attr(EncoderHandler.JSONP_INDEX).get();
      encoder.encodeJsonP(jsonpIndex, queue, out, ctx.alloc(), 50);
      String type = "application/javascript";
      if (jsonpIndex == null) {
        type = "text/plain";
      }
      sendMessage(msg, channel, out, type, promise);
    } else {
      encoder.encodePackets(queue, out, ctx.alloc(), 50);
      sendMessage(msg, channel, out, "application/octet-stream", promise);
    }
  }
Ejemplo n.º 3
0
  private void encodeChunkedContent(
      ChannelHandlerContext ctx, Object msg, int contentLength, List<Object> out) {
    if (contentLength > 0) {
      byte[] length = Integer.toHexString(contentLength).getBytes(CharsetUtil.US_ASCII);
      ByteBuf buf = ctx.alloc().buffer(length.length + 2);
      buf.writeBytes(length);
      buf.writeBytes(CRLF);
      out.add(buf);
      out.add(encodeAndRetain(msg));
      out.add(CRLF_BUF.duplicate());
    }

    if (msg instanceof LastHttpContent) {
      HttpHeaders headers = ((LastHttpContent) msg).trailingHeaders();
      if (headers.isEmpty()) {
        out.add(ZERO_CRLF_CRLF_BUF.duplicate());
      } else {
        ByteBuf buf = ctx.alloc().buffer();
        buf.writeBytes(ZERO_CRLF);
        HttpHeaders.encode(headers, buf);
        buf.writeBytes(CRLF);
        out.add(buf);
      }

      state = ST_INIT;
    } else {
      if (contentLength == 0) {
        // Need to produce some output otherwise an
        // IllegalstateException will be thrown
        out.add(EMPTY_BUFFER);
      }
    }
  }
Ejemplo n.º 4
0
 @Override
 protected void encode(ChannelHandlerContext ctx, Event event, List<Object> out) throws Exception {
   ByteBuf msg = null;
   if (null != event.getSource()) {
     LOG.trace("Event class: {}", event.getClass());
     ByteBuf buf = ctx.alloc().buffer(1);
     buf.writeByte(event.getType());
     msg = Unpooled.wrappedBuffer(buf, Unpooled.wrappedBuffer(msgPack.write(event.getSource())));
   } else {
     msg = ctx.alloc().buffer(1);
     msg.writeByte(event.getType());
   }
   out.add(msg);
 }
Ejemplo n.º 5
0
  @Override
  public HttpContent readChunk(ChannelHandlerContext ctx) throws Exception {
    long offset = this.offset;
    if (offset >= endOffset) {
      if (sentLastChunk) {
        return null;
      } else {
        // Send last chunk for this file
        sentLastChunk = true;
        return new DefaultLastHttpContent();
      }
    }

    int chunkSize = (int) Math.min(this.chunkSize, endOffset - offset);
    // Check if the buffer is backed by an byte array. If so we can optimize it a bit an safe a copy

    ByteBuf buf = ctx.alloc().heapBuffer(chunkSize);
    boolean release = true;
    try {
      file.readFully(buf.array(), buf.arrayOffset(), chunkSize);
      buf.writerIndex(chunkSize);
      this.offset = offset + chunkSize;
      release = false;
      return new DefaultHttpContent(buf);
    } finally {
      if (release) {
        buf.release();
      }
    }
  }
Ejemplo n.º 6
0
  /**
   * 发送消息
   *
   * @param message
   * @param msg
   */
  public void send(final Payload message, final byte[] msg) {
    try {

      final ByteBuf data = context.alloc().buffer(msg.length); // (2)
      data.writeBytes(msg);

      final ChannelFuture cf = context.writeAndFlush(data);
      cf.addListener(
          new GenericFutureListener<Future<? super Void>>() {
            @Override
            public void operationComplete(Future<? super Void> future) throws Exception {
              if (cf.cause() != null) {
                logger.error("{}, Send Error.", context, cf.cause());
                PayloadServiceImpl.instance.updateSendStatus(
                    message,
                    userId,
                    new PushStatus(PushStatus.WriterError, cf.cause().getMessage()));
              } else {
                updateOpTime();
                PayloadServiceImpl.instance.updateSendStatus(
                    message, userId, new PushStatus(PushStatus.Success));
                ClientServiceImpl.instance.updateBadge(userId, 1);
                if (logger.isDebugEnabled()) {
                  logger.debug("Send Done, userId={}, messageId={}", userId, message.getId());
                }
              }
            }
          });

    } catch (Exception e) {
      message.setStatus(userId, new PushStatus(PushStatus.UnKnown, e.getMessage()));
      logger.error(e.getMessage(), e);
    }
  }
Ejemplo n.º 7
0
  public ByteBuf readChunk(ChannelHandlerContext var1) throws Exception {
    long var2 = this.offset;
    if (var2 >= this.endOffset) {
      return null;
    } else {
      int var4 = (int) Math.min((long) this.chunkSize, this.endOffset - var2);
      ByteBuf var5 = var1.alloc().buffer(var4);
      boolean var6 = true;

      try {
        int var7 = 0;

        while (true) {
          int var8 = var5.writeBytes((ScatteringByteChannel) this.in, var4 - var7);
          if (var8 >= 0) {
            var7 += var8;
            if (var7 != var4) {
              continue;
            }
          }

          this.offset += (long) var7;
          var6 = false;
          ByteBuf var12 = var5;
          return var12;
        }
      } finally {
        if (var6) {
          var5.release();
        }
      }
    }
  }
Ejemplo n.º 8
0
 @Override
 public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise)
     throws Exception {
   if (msg instanceof WebSocketFrameInternal) {
     WebSocketFrameInternal frame = (WebSocketFrameInternal) msg;
     ByteBuf buf = frame.getBinaryData();
     if (buf != Unpooled.EMPTY_BUFFER) {
       buf = safeBuffer(buf, ctx.alloc());
     }
     switch (frame.type()) {
       case BINARY:
         msg = new BinaryWebSocketFrame(frame.isFinal(), 0, buf);
         break;
       case TEXT:
         msg = new TextWebSocketFrame(frame.isFinal(), 0, buf);
         break;
       case CLOSE:
         msg = new CloseWebSocketFrame(true, 0, buf);
         break;
       case CONTINUATION:
         msg = new ContinuationWebSocketFrame(frame.isFinal(), 0, buf);
         break;
       case PONG:
         msg = new PongWebSocketFrame(buf);
         break;
       case PING:
         msg = new PingWebSocketFrame(buf);
         break;
       default:
         throw new IllegalStateException("Unsupported websocket msg " + msg);
     }
   }
   ctx.write(msg, promise);
 }
  private boolean handleCompressedFrame(ChannelHandlerContext ctx, ByteBuf in, List<Object> out)
      throws Exception {
    if (!in.isReadable(FRAME_COMPRESS_HEADER_LENGTH)) {
      return false;
    }

    int compressedPayloadLength = in.readInt();
    if (!in.isReadable(compressedPayloadLength)) {
      return false;
    }

    // decompress payload
    Inflater inflater = new Inflater();
    if (in.hasArray()) {
      inflater.setInput(in.array(), in.arrayOffset() + in.readerIndex(), compressedPayloadLength);
      in.skipBytes(compressedPayloadLength);
    } else {
      byte[] array = new byte[compressedPayloadLength];
      in.readBytes(array);
      inflater.setInput(array);
    }

    while (!inflater.finished()) {
      ByteBuf decompressed = ctx.alloc().heapBuffer(1024, 1024);
      byte[] outArray = decompressed.array();
      int count =
          inflater.inflate(outArray, decompressed.arrayOffset(), decompressed.writableBytes());
      decompressed.writerIndex(count);
      // put data in the pipeline
      out.add(decompressed);
    }

    return true;
  }
  @Override
  public void sendError(int status, String message) throws IOException {
    if (committed) {
      throw new IllegalStateException();
    }

    final HttpResponseStatus responseStatus;
    if (message != null) {
      responseStatus = new HttpResponseStatus(status, message);
      setStatus(status);
    } else {
      responseStatus = HttpResponseStatus.valueOf(status);
      setStatus(status);
    }
    io.netty.handler.codec.http.HttpResponse response = null;
    if (message != null) {
      ByteBuf byteBuf = ctx.alloc().buffer();
      byteBuf.writeBytes(message.getBytes());

      response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, responseStatus, byteBuf);
    } else {
      response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, responseStatus);
    }
    if (keepAlive) {
      // Add keep alive and content length if needed
      response.headers().add(Names.CONNECTION, Values.KEEP_ALIVE);
      if (message == null) response.headers().add(Names.CONTENT_LENGTH, 0);
      else response.headers().add(Names.CONTENT_LENGTH, message.getBytes().length);
    }
    ctx.writeAndFlush(response);
    committed = true;
  }
Ejemplo n.º 11
0
  private void wrap(ChannelHandlerContext ctx, boolean inUnwrap) throws SSLException {
    ByteBuf out = null;
    ChannelPromise promise = null;
    ByteBufAllocator alloc = ctx.alloc();
    try {
      for (; ; ) {
        Object msg = pendingUnencryptedWrites.current();
        if (msg == null) {
          break;
        }

        ByteBuf buf = (ByteBuf) msg;
        if (out == null) {
          out = allocateOutNetBuf(ctx, buf.readableBytes());
        }

        SSLEngineResult result = wrap(alloc, engine, buf, out);
        if (!buf.isReadable()) {
          promise = pendingUnencryptedWrites.remove();
        } else {
          promise = null;
        }

        if (result.getStatus() == Status.CLOSED) {
          // SSLEngine has been closed already.
          // Any further write attempts should be denied.
          pendingUnencryptedWrites.removeAndFailAll(SSLENGINE_CLOSED);
          return;
        } else {
          switch (result.getHandshakeStatus()) {
            case NEED_TASK:
              runDelegatedTasks();
              break;
            case FINISHED:
              setHandshakeSuccess();
              // deliberate fall-through
            case NOT_HANDSHAKING:
              setHandshakeSuccessIfStillHandshaking();
              // deliberate fall-through
            case NEED_WRAP:
              finishWrap(ctx, out, promise, inUnwrap);
              promise = null;
              out = null;
              break;
            case NEED_UNWRAP:
              return;
            default:
              throw new IllegalStateException(
                  "Unknown handshake status: " + result.getHandshakeStatus());
          }
        }
      }
    } catch (SSLException e) {
      setHandshakeFailure(ctx, e);
      throw e;
    } finally {
      finishWrap(ctx, out, promise, inUnwrap);
    }
  }
Ejemplo n.º 12
0
 /**
  * Always prefer a direct buffer when it's pooled, so that we reduce the number of memory copies
  * in {@link OpenSslEngine}.
  */
 private ByteBuf allocate(ChannelHandlerContext ctx, int capacity) {
   ByteBufAllocator alloc = ctx.alloc();
   if (wantsDirectBuffer) {
     return alloc.directBuffer(capacity);
   } else {
     return alloc.buffer(capacity);
   }
 }
Ejemplo n.º 13
0
 public ChannelOutputStream(
     ChannelHandlerContext chc, String id, int bufSize, final Object monitor) {
   this.chc = chc;
   this.id = id;
   this.bufSize = bufSize;
   this.buf = chc.alloc().buffer(bufSize);
   this.channelWritabilityMonitor = monitor;
 }
Ejemplo n.º 14
0
  private void wrapNonAppData(ChannelHandlerContext ctx, boolean inUnwrap) throws SSLException {
    ByteBuf out = null;
    try {
      for (; ; ) {
        if (out == null) {
          out = ctx.alloc().buffer(maxPacketBufferSize);
        }
        SSLEngineResult result = wrap(engine, Unpooled.EMPTY_BUFFER, out);

        if (result.bytesProduced() > 0) {
          ctx.write(out);
          if (inUnwrap) {
            needsFlush = true;
          }
          out = null;
        }

        switch (result.getHandshakeStatus()) {
          case FINISHED:
            setHandshakeSuccess();
            break;
          case NEED_TASK:
            runDelegatedTasks();
            break;
          case NEED_UNWRAP:
            if (!inUnwrap) {
              unwrapNonApp(ctx);
            }
            break;
          case NEED_WRAP:
            break;
          case NOT_HANDSHAKING:
            setHandshakeSuccessIfStillHandshaking();
            // Workaround for TLS False Start problem reported at:
            // https://github.com/netty/netty/issues/1108#issuecomment-14266970
            if (!inUnwrap) {
              unwrapNonApp(ctx);
            }
            break;
          default:
            throw new IllegalStateException(
                "Unknown handshake status: " + result.getHandshakeStatus());
        }

        if (result.bytesProduced() == 0) {
          break;
        }
      }
    } catch (SSLException e) {
      setHandshakeFailure(e);
      throw e;
    } finally {
      if (out != null) {
        out.release();
      }
    }
  }
Ejemplo n.º 15
0
  @Override
  protected ByteBuf encodeMessage(ChannelHandlerContext ctx, M msg) {
    ByteBuf buf = ctx.alloc().buffer();

    encodeHeader(buf, msg.getHeader());
    encodeExtras(buf, msg.getExtras());
    encodeKey(buf, msg.getKey());

    return buf;
  }
Ejemplo n.º 16
0
 private static ByteBuf encodeContent(StompContentSubframe content, ChannelHandlerContext ctx) {
   if (content instanceof LastStompContentSubframe) {
     ByteBuf buf = ctx.alloc().buffer(content.content().readableBytes() + 1);
     buf.writeBytes(content.content());
     buf.writeByte(StompConstants.NUL);
     return buf;
   } else {
     return content.content().retain();
   }
 }
Ejemplo n.º 17
0
  @Override
  public void channelActive(ChannelHandlerContext ctx) {
    this.ctx = ctx;

    // Initialize the message.
    content = ctx.alloc().directBuffer(DiscardClient.SIZE).writeZero(DiscardClient.SIZE);

    // Send the initial messages.
    generateTraffic();
  }
Ejemplo n.º 18
0
  private void write(XHROptionsMessage msg, ChannelHandlerContext ctx, ChannelPromise promise) {
    HttpResponse res = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.OK);

    HttpHeaders.addHeader(res, "Set-Cookie", "io=" + msg.getSessionId());
    HttpHeaders.addHeader(res, CONNECTION, KEEP_ALIVE);
    HttpHeaders.addHeader(res, ACCESS_CONTROL_ALLOW_HEADERS, CONTENT_TYPE);
    addOriginHeaders(ctx.channel(), res);

    ByteBuf out = encoder.allocateBuffer(ctx.alloc());
    sendMessage(msg, ctx.channel(), out, res, promise);
  }
    @Override
    public void write(ChannelHandlerContext ctx, Object msg) throws Exception {
      Assert.assertSame(t, Thread.currentThread());

      ByteBuf out = ctx.alloc().buffer(4);
      int m = (Integer) msg;
      int expected = outCnt++;
      Assert.assertEquals(expected, m);
      out.writeInt(m);

      ctx.write(out);
    }
Ejemplo n.º 20
0
  @Override
  public void channelActive(ChannelHandlerContext ctx) {
    this.ctx = ctx;

    System.out.println("1: " + System.currentTimeMillis());

    // Initialize the message.
    content = ctx.alloc().directBuffer(DiscardClient.SIZE).writeZero(DiscardClient.SIZE);

    // Send the initial messages.
    generateTraffic();
  }
 protected ByteBuf extractFrame(ChannelHandlerContext ctx, ByteBuf buffer, int index, int length) {
   // make a sliced buffer for reading full contents, then if enough data doesn't reached yet,
   // ReplayingDecoder will throw an error for replaying decode operation at this line.
   //
   // Don't create a new buffer with ctx.alloc().buffer() before enough data has come. It will not
   // be released (and leaked).
   // If sliced buffer is created successfully, enough data has come.
   ByteBuf slice = buffer.slice(index, length);
   ByteBuf frame = ctx.alloc().buffer(length);
   frame.writeBytes(slice, 0, length);
   return frame;
 }
Ejemplo n.º 22
0
  @Override
  public void channelRead0(final ChannelHandlerContext c, Object msg) throws Exception {
    Channel ch = ctx.channel();
    if (response == null) {
      if (msg instanceof FullHttpResponse) {
        response = (FullHttpResponse) msg;
      } else {
        response = new WSResponse((DefaultHttpResponse) msg, ctx.alloc().buffer());
      }
      if (completeHandshake(ctx)) {
        return;
      }
    }
    if (msg instanceof LastHttpContent) {
      return;
    }

    if (!(msg instanceof WebSocketFrame)) {
      throw new Exception(
          "Unexpected FullHttpResponse (getStatus="
              + response.getStatus()
              + ", "
              + "content="
              + response.content().toString(CharsetUtil.UTF_8)
              + ')');
    }

    final WebSocketFrame frame = (WebSocketFrame) msg;
    if (frame instanceof TextWebSocketFrame) {
      for (WebSocketEventListener l : listensers) {
        l.onMessage(c, new WebSocketMessage(((TextWebSocketFrame) frame).text()));
      }
    } else if (frame instanceof PingWebSocketFrame) {
      if (autoPong) {
        ctx.writeAndFlush(new PongWebSocketFrame(frame.content().copy()));
      }
      for (WebSocketEventListener l : listensers) {
        l.onPing(c, (PingWebSocketFrame) frame.copy());
      }
    } else if (frame instanceof PongWebSocketFrame) {
      Logger.getLogger(getClass())
          .warn(
              String.format(
                  "WebSocketClient received a PongWebSocketFrame, that shouldn't happen! Data : %s",
                  frame.content().toString(CharsetUtil.UTF_8)));
    } else if (frame instanceof CloseWebSocketFrame) {
      ch.close();
      for (WebSocketEventListener l : listensers) {
        l.onClose(c, (CloseWebSocketFrame) frame.copy());
      }
    }
  }
Ejemplo n.º 23
0
  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));
      }
    }
  }
Ejemplo n.º 24
0
 @Override
 protected void encode(ChannelHandlerContext ctx, NetBuffer buf, ByteBuf out) throws Exception {
   out = ctx.alloc().directBuffer();
   byte[] data = buf.getMessages();
   int dataLength = data.length;
   logger.debug("=====length:[\t" + dataLength + "\t]=======");
   // д��Ϣ
   out.writeShort(NetConstants.MAGIC_HEADER); // Matgic Header
   out.writeInt(dataLength); // ��Ҫ����ָ����
   out.writeBytes(data); // ����protobuf����
   ctx.write(out);
   ctx.flush();
 }
Ejemplo n.º 25
0
  @Override
  public void channelActive(final ChannelHandlerContext ctx) { // (1)
    final ByteBuf time = ctx.alloc().buffer(4); // (2)
    time.writeInt((int) (System.currentTimeMillis() / 1000L + 2208988800L));

    final ChannelFuture f = ctx.writeAndFlush(time); // (3)
    f.addListener(
        new ChannelFutureListener() {
          @Override
          public void operationComplete(ChannelFuture future) {
            assert f == future;
            ctx.close();
          }
        }); // (4)
  }
Ejemplo n.º 26
0
  @Override
  protected void encode(ChannelHandlerContext ctx, OnDemandResponse response, List<Object> out) {
    FileDescriptor descriptor = response.getFileDescriptor();
    int fileSize = response.getFileSize();
    int chunkId = response.getChunkId();
    ByteBuf chunkData = response.getChunkData();

    ByteBuf buffer = ctx.alloc().buffer(6 + chunkData.readableBytes());
    buffer.writeByte(descriptor.getType() - 1);
    buffer.writeShort(descriptor.getFile());
    buffer.writeShort(fileSize);
    buffer.writeByte(chunkId);
    buffer.writeBytes(chunkData);

    out.add(buffer);
  }
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
      Thread t = this.t;
      if (t == null) {
        this.t = Thread.currentThread();
      } else {
        Assert.assertSame(t, Thread.currentThread());
      }

      ByteBuf out = ctx.alloc().buffer(4);
      int m = ((Integer) msg).intValue();
      int expected = inCnt++;
      Assert.assertEquals(expected, m);
      out.writeInt(m);

      ctx.fireChannelRead(out);
    }
Ejemplo n.º 28
0
  private static ByteBuf encodeFrame(StompHeadersSubframe frame, ChannelHandlerContext ctx) {
    ByteBuf buf = ctx.alloc().buffer();

    buf.writeBytes(frame.command().toString().getBytes(CharsetUtil.US_ASCII));
    buf.writeByte(StompConstants.CR).writeByte(StompConstants.LF);

    StompHeaders headers = frame.headers();
    for (String k : headers.keySet()) {
      List<String> values = headers.getAll(k);
      for (String v : values) {
        buf.writeBytes(k.getBytes(CharsetUtil.US_ASCII))
            .writeByte(StompConstants.COLON)
            .writeBytes(v.getBytes(CharsetUtil.US_ASCII));
        buf.writeByte(StompConstants.CR).writeByte(StompConstants.LF);
      }
    }
    buf.writeByte(StompConstants.CR).writeByte(StompConstants.LF);
    return buf;
  }
  @Override
  public ByteBuf encode(ChannelHandlerContext ctx, SpdyHeadersFrame frame) throws Exception {
    if (frame == null) {
      throw new IllegalArgumentException("frame");
    }

    if (finished) {
      return Unpooled.EMPTY_BUFFER;
    }

    ByteBuf decompressed = super.encode(ctx, frame);
    if (decompressed.readableBytes() == 0) {
      return Unpooled.EMPTY_BUFFER;
    }

    ByteBuf compressed = ctx.alloc().buffer();
    setInput(decompressed);
    encode(compressed);
    return compressed;
  }
  @Override
  public ByteBuf readChunk(ChannelHandlerContext ctx) throws Exception {

    if (endOfInput) return null;

    ByteBuf buffer = ctx.alloc().buffer(DEFAULT_CHUNK_SIZE);
    Body.BodyState state = body.transferTo(buffer);
    switch (state) {
      case STOP:
        endOfInput = true;
        return buffer;
      case SUSPEND:
        // this will suspend the stream in ChunkedWriteHandler
        return null;
      case CONTINUE:
        return buffer;
      default:
        throw new IllegalStateException("Unknown state: " + state);
    }
  }