@Test
  public void testResponseWithContentLength() {
    EmbeddedChannel ch = new EmbeddedChannel(new HttpResponseDecoder());
    ch.writeInbound(
        Unpooled.copiedBuffer(
            "HTTP/1.1 200 OK\r\n" + "Content-Length: 10\r\n" + "\r\n", CharsetUtil.US_ASCII));

    byte[] data = new byte[10];
    for (int i = 0; i < data.length; i++) {
      data[i] = (byte) i;
    }
    ch.writeInbound(Unpooled.wrappedBuffer(data, 0, data.length / 2));
    ch.writeInbound(Unpooled.wrappedBuffer(data, 5, data.length / 2));

    HttpResponse res = ch.readInbound();
    assertThat(res.getProtocolVersion(), sameInstance(HttpVersion.HTTP_1_1));
    assertThat(res.getStatus(), is(HttpResponseStatus.OK));

    HttpContent firstContent = ch.readInbound();
    assertThat(firstContent.content().readableBytes(), is(5));
    assertEquals(Unpooled.wrappedBuffer(data, 0, 5), firstContent.content());
    firstContent.release();

    LastHttpContent lastContent = ch.readInbound();
    assertEquals(5, lastContent.content().readableBytes());
    assertEquals(Unpooled.wrappedBuffer(data, 5, 5), lastContent.content());
    lastContent.release();

    assertThat(ch.finish(), is(false));
    assertThat(ch.readInbound(), is(nullValue()));
  }
  @Test
  public void testResponseChunkedExceedMaxChunkSize() {
    EmbeddedChannel ch = new EmbeddedChannel(new HttpResponseDecoder(4096, 8192, 32));
    ch.writeInbound(
        Unpooled.copiedBuffer(
            "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n", CharsetUtil.US_ASCII));

    HttpResponse res = ch.readInbound();
    assertThat(res.getProtocolVersion(), sameInstance(HttpVersion.HTTP_1_1));
    assertThat(res.getStatus(), is(HttpResponseStatus.OK));

    byte[] data = new byte[64];
    for (int i = 0; i < data.length; i++) {
      data[i] = (byte) i;
    }

    for (int i = 0; i < 10; i++) {
      assertFalse(
          ch.writeInbound(
              Unpooled.copiedBuffer(
                  Integer.toHexString(data.length) + "\r\n", CharsetUtil.US_ASCII)));
      assertTrue(ch.writeInbound(Unpooled.wrappedBuffer(data)));

      byte[] decodedData = new byte[data.length];
      HttpContent content = ch.readInbound();
      assertEquals(32, content.content().readableBytes());
      content.content().readBytes(decodedData, 0, 32);
      content.release();

      content = ch.readInbound();
      assertEquals(32, content.content().readableBytes());

      content.content().readBytes(decodedData, 32, 32);

      assertArrayEquals(data, decodedData);
      content.release();

      assertFalse(ch.writeInbound(Unpooled.copiedBuffer("\r\n", CharsetUtil.US_ASCII)));
    }

    // Write the last chunk.
    ch.writeInbound(Unpooled.copiedBuffer("0\r\n\r\n", CharsetUtil.US_ASCII));

    // Ensure the last chunk was decoded.
    LastHttpContent content = ch.readInbound();
    assertFalse(content.content().isReadable());
    content.release();

    ch.finish();
    assertNull(ch.readInbound());
  }
  @Test
  public void testWebSocketResponse() {
    byte[] data =
        ("HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
                + "Upgrade: WebSocket\r\n"
                + "Connection: Upgrade\r\n"
                + "Sec-WebSocket-Origin: http://localhost:8080\r\n"
                + "Sec-WebSocket-Location: ws://localhost/some/path\r\n"
                + "\r\n"
                + "1234567812345678")
            .getBytes();
    EmbeddedChannel ch = new EmbeddedChannel(new HttpResponseDecoder());
    ch.writeInbound(Unpooled.wrappedBuffer(data));

    HttpResponse res = ch.readInbound();
    assertThat(res.getProtocolVersion(), sameInstance(HttpVersion.HTTP_1_1));
    assertThat(res.getStatus(), is(HttpResponseStatus.SWITCHING_PROTOCOLS));
    HttpContent content = ch.readInbound();
    assertThat(content.content().readableBytes(), is(16));
    content.release();

    assertThat(ch.finish(), is(false));

    assertThat(ch.readInbound(), is(nullValue()));
  }
  @Test
  public void testLastResponseWithHeaderRemoveTrailingSpaces() {
    EmbeddedChannel ch = new EmbeddedChannel(new HttpResponseDecoder());
    ch.writeInbound(
        Unpooled.copiedBuffer(
            "HTTP/1.1 200 OK\r\nX-Header: h2=h2v2; Expires=Wed, 09-Jun-2021 10:18:14 GMT       \r\n\r\n",
            CharsetUtil.US_ASCII));

    HttpResponse res = ch.readInbound();
    assertThat(res.getProtocolVersion(), sameInstance(HttpVersion.HTTP_1_1));
    assertThat(res.getStatus(), is(HttpResponseStatus.OK));
    assertThat(res.headers().get("X-Header"), is("h2=h2v2; Expires=Wed, 09-Jun-2021 10:18:14 GMT"));
    assertThat(ch.readInbound(), is(nullValue()));

    ch.writeInbound(Unpooled.wrappedBuffer(new byte[1024]));
    HttpContent content = ch.readInbound();
    assertThat(content.content().readableBytes(), is(1024));
    content.release();

    assertThat(ch.finish(), is(true));

    LastHttpContent lastContent = ch.readInbound();
    assertThat(lastContent.content().isReadable(), is(false));
    lastContent.release();

    assertThat(ch.readInbound(), is(nullValue()));
  }
  @Test
  public void testPrematureClosureWithChunkedEncoding2() throws Exception {
    EmbeddedChannel ch = new EmbeddedChannel(new HttpResponseDecoder());

    // Write the partial response.
    ch.writeInbound(
        Unpooled.copiedBuffer(
            "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n8\r\n12345678",
            CharsetUtil.US_ASCII));

    // Read the response headers.
    HttpResponse res = ch.readInbound();
    assertThat(res.getProtocolVersion(), sameInstance(HttpVersion.HTTP_1_1));
    assertThat(res.getStatus(), is(HttpResponseStatus.OK));
    assertThat(res.headers().get(Names.TRANSFER_ENCODING), is("chunked"));

    // Read the partial content.
    HttpContent content = ch.readInbound();
    assertThat(content.content().toString(CharsetUtil.US_ASCII), is("12345678"));
    assertThat(content, is(not(instanceOf(LastHttpContent.class))));
    content.release();

    assertThat(ch.readInbound(), is(nullValue()));

    // Close the connection.
    ch.finish();

    // The decoder should not generate the last chunk because it's closed prematurely.
    assertThat(ch.readInbound(), is(nullValue()));
  }
  @Test
  public void testClosureWithoutContentLength2() throws Exception {
    EmbeddedChannel ch = new EmbeddedChannel(new HttpResponseDecoder());

    // Write the partial response.
    ch.writeInbound(Unpooled.copiedBuffer("HTTP/1.1 200 OK\r\n\r\n12345678", CharsetUtil.US_ASCII));

    // Read the response headers.
    HttpResponse res = ch.readInbound();
    assertThat(res.getProtocolVersion(), sameInstance(HttpVersion.HTTP_1_1));
    assertThat(res.getStatus(), is(HttpResponseStatus.OK));

    // Read the partial content.
    HttpContent content = ch.readInbound();
    assertThat(content.content().toString(CharsetUtil.US_ASCII), is("12345678"));
    assertThat(content, is(not(instanceOf(LastHttpContent.class))));
    content.release();

    assertThat(ch.readInbound(), is(nullValue()));

    // Close the connection.
    assertTrue(ch.finish());

    // The decoder should still produce the last content.
    LastHttpContent lastContent = ch.readInbound();
    assertThat(lastContent.content().isReadable(), is(false));
    lastContent.release();

    // But nothing more.
    assertThat(ch.readInbound(), is(nullValue()));
  }
  private static void testResponseWithContentLengthFragmented(byte[] header, int fragmentSize) {
    EmbeddedChannel ch = new EmbeddedChannel(new HttpResponseDecoder());
    // split up the header
    for (int a = 0; a < header.length; ) {
      int amount = fragmentSize;
      if (a + amount > header.length) {
        amount = header.length - a;
      }

      ch.writeInbound(Unpooled.wrappedBuffer(header, a, amount));
      a += amount;
    }
    byte[] data = new byte[10];
    for (int i = 0; i < data.length; i++) {
      data[i] = (byte) i;
    }
    ch.writeInbound(Unpooled.wrappedBuffer(data, 0, data.length / 2));
    ch.writeInbound(Unpooled.wrappedBuffer(data, 5, data.length / 2));

    HttpResponse res = ch.readInbound();
    assertThat(res.getProtocolVersion(), sameInstance(HttpVersion.HTTP_1_1));
    assertThat(res.getStatus(), is(HttpResponseStatus.OK));

    HttpContent firstContent = ch.readInbound();
    assertThat(firstContent.content().readableBytes(), is(5));
    assertEquals(Unpooled.wrappedBuffer(data, 0, 5), firstContent.content());
    firstContent.release();

    LastHttpContent lastContent = ch.readInbound();
    assertEquals(5, lastContent.content().readableBytes());
    assertEquals(Unpooled.wrappedBuffer(data, 5, 5), lastContent.content());
    lastContent.release();

    assertThat(ch.finish(), is(false));
    assertThat(ch.readInbound(), is(nullValue()));
  }
  /** {@inheritDoc} */
  @Override
  public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
    if (msg instanceof HttpRequest) {
      HttpRequest request = this.request = (HttpRequest) msg;
      trackingType = setTrackingType(request);
    }

    if (msg instanceof HttpContent) {
      // New chunk is received
      HttpContent chunk = (HttpContent) msg;
      requestContent.append(chunk.content().toString(Charset.defaultCharset()));
      if (chunk instanceof LastHttpContent) {
        // All chunks have been received
        ResultTO resultTO = submitPayload(requestContent.toString());
        responseContent.append(gson.toJson(resultTO));
        writeResponse(ctx.channel());
        reset();
      }
    }
  }
  @Test
  public void testLastResponseWithoutContentLengthHeader() {
    EmbeddedChannel ch = new EmbeddedChannel(new HttpResponseDecoder());
    ch.writeInbound(Unpooled.copiedBuffer("HTTP/1.1 200 OK\r\n\r\n", CharsetUtil.US_ASCII));

    HttpResponse res = ch.readInbound();
    assertThat(res.getProtocolVersion(), sameInstance(HttpVersion.HTTP_1_1));
    assertThat(res.getStatus(), is(HttpResponseStatus.OK));
    assertThat(ch.readInbound(), is(nullValue()));

    ch.writeInbound(Unpooled.wrappedBuffer(new byte[1024]));
    HttpContent content = ch.readInbound();
    assertThat(content.content().readableBytes(), is(1024));
    content.release();

    assertThat(ch.finish(), is(true));

    LastHttpContent lastContent = ch.readInbound();
    assertThat(lastContent.content().isReadable(), is(false));
    lastContent.release();

    assertThat(ch.readInbound(), is(nullValue()));
  }
Example #10
0
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

      messageReceiveCount++;
      if (msg instanceof HttpResponse) {
        try {
          HttpResponse response = (HttpResponse) msg;

          StringBuilder sb = new StringBuilder();
          if (LOG.isDebugEnabled()) {
            sb.append("STATUS: ")
                .append(response.getStatus())
                .append(", VERSION: ")
                .append(response.getProtocolVersion())
                .append(", HEADER: ");
          }
          if (!response.headers().names().isEmpty()) {
            for (String name : response.headers().names()) {
              for (String value : response.headers().getAll(name)) {
                if (LOG.isDebugEnabled()) {
                  sb.append(name).append(" = ").append(value);
                }
                if (this.length == -1 && name.equals("Content-Length")) {
                  this.length = Long.parseLong(value);
                }
              }
            }
          }
          if (LOG.isDebugEnabled()) {
            LOG.debug(sb.toString());
          }

          if (response.getStatus().code() == HttpResponseStatus.NO_CONTENT.code()) {
            LOG.warn("There are no data corresponding to the request");
            length = 0;
            return;
          } else if (response.getStatus().code() != HttpResponseStatus.OK.code()) {
            LOG.error(response.getStatus().reasonPhrase());
            state = TajoProtos.FetcherState.FETCH_FAILED;
            return;
          }
        } catch (Exception e) {
          LOG.error(e.getMessage(), e);
        } finally {
          ReferenceCountUtil.release(msg);
        }
      }

      if (msg instanceof HttpContent) {
        try {
          HttpContent httpContent = (HttpContent) msg;
          ByteBuf content = httpContent.content();
          if (content.isReadable()) {
            content.readBytes(fc, content.readableBytes());
          }

          if (msg instanceof LastHttpContent) {
            if (raf != null) {
              fileLen = file.length();
            }

            finishTime = System.currentTimeMillis();
            if (state != TajoProtos.FetcherState.FETCH_FAILED) {
              state = TajoProtos.FetcherState.FETCH_FINISHED;
            }

            IOUtils.cleanup(LOG, fc, raf);
          }
        } catch (Exception e) {
          LOG.error(e.getMessage(), e);
        } finally {
          ReferenceCountUtil.release(msg);
        }
      }
    }
Example #11
0
 @Override
 public ByteBuf content() {
   return content.content();
 }
Example #12
0
  @Override
  protected void decode(ChannelHandlerContext ctx, HttpObject msg, MessageBuf<Object> out)
      throws Exception {
    FullHttpMessage currentMessage = this.currentMessage;

    if (msg instanceof HttpMessage) {
      assert currentMessage == null;

      HttpMessage m = (HttpMessage) msg;

      // Handle the 'Expect: 100-continue' header if necessary.
      // TODO: Respond with 413 Request Entity Too Large
      //   and discard the traffic or close the connection.
      //       No need to notify the upstream handlers - just log.
      //       If decoding a response, just throw an exception.
      if (is100ContinueExpected(m)) {
        ctx.write(CONTINUE.duplicate());
      }

      if (!m.getDecoderResult().isSuccess()) {
        removeTransferEncodingChunked(m);
        this.currentMessage = null;
        out.add(BufUtil.retain(m));
        return;
      }
      if (msg instanceof HttpRequest) {
        HttpRequest header = (HttpRequest) msg;
        this.currentMessage =
            currentMessage =
                new DefaultFullHttpRequest(
                    header.getProtocolVersion(),
                    header.getMethod(),
                    header.getUri(),
                    Unpooled.compositeBuffer(maxCumulationBufferComponents));
      } else if (msg instanceof HttpResponse) {
        HttpResponse header = (HttpResponse) msg;
        this.currentMessage =
            currentMessage =
                new DefaultFullHttpResponse(
                    header.getProtocolVersion(),
                    header.getStatus(),
                    Unpooled.compositeBuffer(maxCumulationBufferComponents));
      } else {
        throw new Error();
      }

      currentMessage.headers().set(m.headers());

      // A streamed message - initialize the cumulative buffer, and wait for incoming chunks.
      removeTransferEncodingChunked(currentMessage);
    } else if (msg instanceof HttpContent) {
      assert currentMessage != null;

      // Merge the received chunk into the content of the current message.
      HttpContent chunk = (HttpContent) msg;
      CompositeByteBuf content = (CompositeByteBuf) currentMessage.content();

      if (content.readableBytes() > maxContentLength - chunk.content().readableBytes()) {
        // TODO: Respond with 413 Request Entity Too Large
        //   and discard the traffic or close the connection.
        //       No need to notify the upstream handlers - just log.
        //       If decoding a response, just throw an exception.
        throw new TooLongFrameException(
            "HTTP content length exceeded " + maxContentLength + " bytes.");
      }

      // Append the content of the chunk
      if (chunk.content().isReadable()) {
        chunk.retain();
        content.addComponent(chunk.content());
        content.writerIndex(content.writerIndex() + chunk.content().readableBytes());
      }

      final boolean last;
      if (!chunk.getDecoderResult().isSuccess()) {
        currentMessage.setDecoderResult(DecoderResult.failure(chunk.getDecoderResult().cause()));
        last = true;
      } else {
        last = chunk instanceof LastHttpContent;
      }

      if (last) {
        this.currentMessage = null;

        // Merge trailing headers into the message.
        if (chunk instanceof LastHttpContent) {
          LastHttpContent trailer = (LastHttpContent) chunk;
          currentMessage.headers().add(trailer.trailingHeaders());
        }

        // Set the 'Content-Length' header.
        currentMessage
            .headers()
            .set(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(content.readableBytes()));

        // All done
        out.add(currentMessage);
      }
    } else {
      throw new Error();
    }
  }
  @Override
  protected void encode(ChannelHandlerContext ctx, HttpObject msg, ByteBuf out) throws Exception {
    if (msg instanceof HttpMessage) {
      if (state != ST_INIT) {
        throw new IllegalStateException(
            "unexpected message type: " + msg.getClass().getSimpleName());
      }

      @SuppressWarnings({"unchecked", "CastConflictsWithInstanceof"})
      H m = (H) msg;

      // Encode the message.
      encodeInitialLine(out, m);
      encodeHeaders(out, m);
      out.writeByte(CR);
      out.writeByte(LF);

      state = HttpHeaders.isTransferEncodingChunked(m) ? ST_CONTENT_CHUNK : ST_CONTENT_NON_CHUNK;
    }

    if (msg instanceof HttpContent) {
      if (state == ST_INIT) {
        throw new IllegalStateException(
            "unexpected message type: " + msg.getClass().getSimpleName());
      }

      HttpContent chunk = (HttpContent) msg;
      ByteBuf content = chunk.content();
      int contentLength = content.readableBytes();

      if (state == ST_CONTENT_NON_CHUNK) {
        if (contentLength > 0) {
          out.writeBytes(content, content.readerIndex(), content.readableBytes());
        }

        if (chunk instanceof LastHttpContent) {
          state = ST_INIT;
        }
      } else if (state == ST_CONTENT_CHUNK) {
        if (contentLength > 0) {
          out.writeBytes(copiedBuffer(Integer.toHexString(contentLength), CharsetUtil.US_ASCII));
          out.writeByte(CR);
          out.writeByte(LF);
          out.writeBytes(content, content.readerIndex(), contentLength);
          out.writeByte(CR);
          out.writeByte(LF);
        }

        if (chunk instanceof LastHttpContent) {
          out.writeByte((byte) '0');
          out.writeByte(CR);
          out.writeByte(LF);
          encodeTrailingHeaders(out, (LastHttpContent) chunk);
          out.writeByte(CR);
          out.writeByte(LF);
          state = ST_INIT;
        }
      } else {
        throw new Error();
      }
    }
  }