@Test
  public void test204() throws Exception {
    ByteBuffer header = BufferUtils.allocate(8096);
    ByteBuffer content = BufferUtils.toBuffer("0123456789");

    HttpGenerator gen = new HttpGenerator();

    MetaData.Response info =
        new MetaData.Response(HttpVersion.HTTP_1_1, 204, "Foo", new HttpFields(), 10);
    info.getFields().add("Content-Type", "test/data");
    info.getFields().add("Last-Modified", DateGenerator.__01Jan1970);

    HttpGenerator.Result result = gen.generateResponse(info, header, null, content, true);

    assertEquals(gen.isNoContent(), true);
    assertEquals(HttpGenerator.Result.FLUSH, result);
    assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
    String responseheaders = BufferUtils.toString(header);
    BufferUtils.clear(header);

    result = gen.generateResponse(null, null, null, content, false);
    assertEquals(HttpGenerator.Result.DONE, result);
    assertEquals(HttpGenerator.State.END, gen.getState());

    assertThat(responseheaders, containsString("HTTP/1.1 204 Foo"));
    assertThat(responseheaders, containsString("Last-Modified: Thu, 01 Jan 1970 00:00:00 GMT"));
    assertThat(responseheaders, not(containsString("Content-Length: 10")));

    // Note: the HttpConnection.process() method is responsible for actually
    // excluding the content from the response based on
    // generator.isNoContent()==true
  }
  @Test
  public void testResponseUpgrade() throws Exception {
    ByteBuffer header = BufferUtils.allocate(8096);

    HttpGenerator gen = new HttpGenerator();

    HttpGenerator.Result result = gen.generateResponse(null, null, null, null, true);
    assertEquals(HttpGenerator.Result.NEED_INFO, result);
    assertEquals(HttpGenerator.State.START, gen.getState());

    MetaData.Response info =
        new MetaData.Response(HttpVersion.HTTP_1_1, 101, null, new HttpFields(), -1);
    info.getFields().add("Upgrade", "WebSocket");
    info.getFields().add("Connection", "Upgrade");
    info.getFields().add("Sec-WebSocket-Accept", "123456789==");

    result = gen.generateResponse(info, header, null, null, true);
    assertEquals(HttpGenerator.Result.FLUSH, result);
    assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
    String head = BufferUtils.toString(header);
    BufferUtils.clear(header);

    result = gen.generateResponse(info, null, null, null, false);
    assertEquals(HttpGenerator.Result.DONE, result);
    assertEquals(HttpGenerator.State.END, gen.getState());

    assertEquals(0, gen.getContentPrepared());

    assertThat(head, startsWith("HTTP/1.1 101 Switching Protocols"));
    assertThat(head, containsString("Upgrade: WebSocket\r\n"));
    assertThat(head, containsString("Connection: Upgrade\r\n"));
  }
  @Test
  public void testCachedPut() throws Exception {
    HttpFields header = new HttpFields();

    header.put("Connection", "Keep-Alive");
    header.put("tRansfer-EncOding", "CHUNKED");
    header.put("CONTENT-ENCODING", "gZIP");

    ByteBuffer buffer = BufferUtils.allocate(1024);
    BufferUtils.flipToFill(buffer);
    HttpGenerator.putTo(header, buffer);
    BufferUtils.flipToFlush(buffer, 0);
    String out = BufferUtils.toString(buffer).toLowerCase();

    Assert.assertThat(
        out,
        Matchers.containsString(
            (HttpHeader.CONNECTION + ": " + HttpHeaderValue.KEEP_ALIVE).toLowerCase()));
    Assert.assertThat(
        out,
        Matchers.containsString(
            (HttpHeader.TRANSFER_ENCODING + ": " + HttpHeaderValue.CHUNKED).toLowerCase()));
    Assert.assertThat(
        out,
        Matchers.containsString(
            (HttpHeader.CONTENT_ENCODING + ": " + HttpHeaderValue.GZIP).toLowerCase()));
  }
  @Test
  public void testResponseNoContent() throws Exception {
    ByteBuffer header = BufferUtils.allocate(8096);

    HttpGenerator gen = new HttpGenerator();

    HttpGenerator.Result result = gen.generateResponse(null, null, null, null, true);
    assertEquals(HttpGenerator.Result.NEED_INFO, result);
    assertEquals(HttpGenerator.State.START, gen.getState());

    MetaData.Response info =
        new MetaData.Response(HttpVersion.HTTP_1_1, 200, null, new HttpFields(), -1);
    info.getFields().add("Last-Modified", DateGenerator.__01Jan1970);

    result = gen.generateResponse(info, null, null, null, true);
    assertEquals(HttpGenerator.Result.NEED_HEADER, result);

    result = gen.generateResponse(info, header, null, null, true);
    assertEquals(HttpGenerator.Result.FLUSH, result);
    assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
    String head = BufferUtils.toString(header);
    BufferUtils.clear(header);

    result = gen.generateResponse(null, null, null, null, false);
    assertEquals(HttpGenerator.Result.DONE, result);
    assertEquals(HttpGenerator.State.END, gen.getState());

    assertEquals(0, gen.getContentPrepared());
    assertThat(head, containsString("HTTP/1.1 200 OK"));
    assertThat(head, containsString("Last-Modified: Thu, 01 Jan 1970 00:00:00 GMT"));
    assertThat(head, containsString("Content-Length: 0"));
  }
  @Test
  public void testConnectionKeepAliveWithAdditionalCustomValue() throws Exception {
    HttpGenerator generator = new HttpGenerator();

    HttpFields fields = new HttpFields();
    fields.put(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE);
    String customValue = "test";
    fields.add(HttpHeader.CONNECTION, customValue);
    MetaData.Response info = new MetaData.Response(HttpVersion.HTTP_1_0, 200, "OK", fields, -1);
    ByteBuffer header = BufferUtils.allocate(4096);
    HttpGenerator.Result result = generator.generateResponse(info, header, null, null, true);
    Assert.assertSame(HttpGenerator.Result.FLUSH, result);
    String headers = BufferUtils.toString(header);
    Assert.assertTrue(headers.contains(HttpHeaderValue.KEEP_ALIVE.asString()));
    Assert.assertTrue(headers.contains(customValue));
  }
Example #6
0
 private IllegalCharacterException(State state, byte ch, ByteBuffer buffer) {
   super(400, String.format("Illegal character 0x%X", ch));
   // Bug #460642 - don't reveal buffers to end user
   log.warn(
       String.format(
           "Illegal character 0x%X in state=%s for buffer %s",
           ch, state, BufferUtils.toDetailString(buffer)));
 }
  @Test
  public void testCRLF() throws Exception {
    HttpFields header = new HttpFields();

    header.put("name0", "value\r\n0");
    header.put("name\r\n1", "value1");
    header.put("name:2", "value:\r\n2");

    ByteBuffer buffer = BufferUtils.allocate(1024);
    BufferUtils.flipToFill(buffer);
    HttpGenerator.putTo(header, buffer);
    BufferUtils.flipToFlush(buffer, 0);
    String out = BufferUtils.toString(buffer);
    assertThat(out, containsString("name0: value  0"));
    assertThat(out, containsString("name??1: value1"));
    assertThat(out, containsString("name?2: value:  2"));
  }
  @Test
  public void testResponseWithKnownContent() throws Exception {
    ByteBuffer header = BufferUtils.allocate(4096);
    ByteBuffer content0 = BufferUtils.toBuffer("Hello World! ");
    ByteBuffer content1 = BufferUtils.toBuffer("The quick brown fox jumped over the lazy dog. ");
    HttpGenerator gen = new HttpGenerator();

    HttpGenerator.Result result = gen.generateResponse(null, null, null, content0, false);
    assertEquals(HttpGenerator.Result.NEED_INFO, result);
    assertEquals(HttpGenerator.State.START, gen.getState());

    MetaData.Response info =
        new MetaData.Response(HttpVersion.HTTP_1_1, 200, null, new HttpFields(), 59);
    info.getFields().add("Last-Modified", DateGenerator.__01Jan1970);
    result = gen.generateResponse(info, null, null, content0, false);
    assertEquals(HttpGenerator.Result.NEED_HEADER, result);
    assertEquals(HttpGenerator.State.START, gen.getState());

    result = gen.generateResponse(info, header, null, content0, false);
    assertEquals(HttpGenerator.Result.FLUSH, result);
    assertEquals(HttpGenerator.State.COMMITTED, gen.getState());

    String out = BufferUtils.toString(header);
    BufferUtils.clear(header);
    out += BufferUtils.toString(content0);
    BufferUtils.clear(content0);

    result = gen.generateResponse(null, null, null, content1, false);
    assertEquals(HttpGenerator.Result.FLUSH, result);
    assertEquals(HttpGenerator.State.COMMITTED, gen.getState());
    out += BufferUtils.toString(content1);
    BufferUtils.clear(content1);

    result = gen.generateResponse(null, null, null, null, true);
    assertEquals(HttpGenerator.Result.CONTINUE, result);
    assertEquals(HttpGenerator.State.COMPLETING, gen.getState());

    result = gen.generateResponse(null, null, null, null, true);
    assertEquals(HttpGenerator.Result.DONE, result);
    assertEquals(HttpGenerator.State.END, gen.getState());

    assertThat(out, containsString("HTTP/1.1 200 OK"));
    assertThat(out, containsString("Last-Modified: Thu, 01 Jan 1970 00:00:00 GMT"));
    assertThat(out, not(containsString("chunked")));
    assertThat(out, containsString("Content-Length: 59"));
    assertThat(
        out, containsString("\r\n\r\nHello World! The quick brown fox jumped over the lazy dog. "));
  }
  @Test
  public void testPutTo() throws Exception {
    HttpFields header = new HttpFields();

    header.put("name0", "value0");
    header.put("name1", "value:A");
    header.add("name1", "value:B");
    header.add("name2", "");

    ByteBuffer buffer = BufferUtils.allocate(1024);
    BufferUtils.flipToFill(buffer);
    HttpGenerator.putTo(header, buffer);
    BufferUtils.flipToFlush(buffer, 0);
    String result = BufferUtils.toString(buffer);

    assertThat(result, Matchers.containsString("name0: value0"));
    assertThat(result, Matchers.containsString("name1: value:A"));
    assertThat(result, Matchers.containsString("name1: value:B"));
  }
  @Test
  public void testSendServerXPoweredBy() throws Exception {
    ByteBuffer header = BufferUtils.allocate(8096);
    MetaData.Response info =
        new MetaData.Response(HttpVersion.HTTP_1_1, 200, null, new HttpFields(), -1);
    HttpFields fields = new HttpFields();
    fields.add(HttpHeader.SERVER, "SomeServer");
    fields.add(HttpHeader.X_POWERED_BY, "SomePower");
    MetaData.Response infoF = new MetaData.Response(HttpVersion.HTTP_1_1, 200, null, fields, -1);
    String head;

    HttpGenerator gen = new HttpGenerator(true, true);
    gen.generateResponse(info, header, null, null, true);
    head = BufferUtils.toString(header);
    BufferUtils.clear(header);
    assertThat(head, containsString("HTTP/1.1 200 OK"));
    assertThat(head, containsString("Server: Firefly 3.0"));
    assertThat(head, containsString("X-Powered-By: Firefly 3.0"));
    gen.reset();
    gen.generateResponse(infoF, header, null, null, true);
    head = BufferUtils.toString(header);
    BufferUtils.clear(header);
    assertThat(head, containsString("HTTP/1.1 200 OK"));
    assertThat(head, not(containsString("Server: Firefly 3.0")));
    assertThat(head, containsString("Server: SomeServer"));
    assertThat(head, containsString("X-Powered-By: Firefly 3.0"));
    assertThat(head, containsString("X-Powered-By: SomePower"));
    gen.reset();

    gen = new HttpGenerator(false, false);
    gen.generateResponse(info, header, null, null, true);
    head = BufferUtils.toString(header);
    BufferUtils.clear(header);
    assertThat(head, containsString("HTTP/1.1 200 OK"));
    assertThat(head, not(containsString("Server: Firefly 3.0")));
    assertThat(head, not(containsString("X-Powered-By: Firefly 3.0")));
    gen.reset();
    gen.generateResponse(infoF, header, null, null, true);
    head = BufferUtils.toString(header);
    BufferUtils.clear(header);
    assertThat(head, containsString("HTTP/1.1 200 OK"));
    assertThat(head, not(containsString("Server: Firefly 3.0")));
    assertThat(head, containsString("Server: SomeServer"));
    assertThat(head, not(containsString("X-Powered-By: Firefly 3.0")));
    assertThat(head, containsString("X-Powered-By: SomePower"));
    gen.reset();
  }
  @Test
  public void test101SwitchingProtocolsResponse() throws Exception {
    MetaData.Response switchingProtocolsResponse =
        new MetaData.Response(HttpVersion.HTTP_1_1, 101, new HttpFields());
    switchingProtocolsResponse.getFields().put(HttpHeader.CONNECTION, HttpHeaderValue.UPGRADE);
    switchingProtocolsResponse.getFields().put(HttpHeader.UPGRADE, "h2c");

    ByteBuffer header = BufferUtils.allocate(4096);
    HttpGenerator gen = new HttpGenerator();
    HttpGenerator.Result result =
        gen.generateResponse(switchingProtocolsResponse, header, null, null, true);
    System.out.println(result + "|" + gen.getState());
    assertEquals(HttpGenerator.Result.FLUSH, result);
    assertEquals(HttpGenerator.State.COMPLETING, gen.getState());

    result = gen.generateResponse(null, null, null, null, true);
    System.out.println(result + "|" + gen.getState());
    assertEquals(HttpGenerator.Result.DONE, result);
    assertEquals(HttpGenerator.State.END, gen.getState());
    System.out.println(BufferUtils.toString(header));
  }
  @Test
  public void testComplexChars() throws Exception {
    ByteBuffer header = BufferUtils.allocate(8096);
    ByteBuffer content = BufferUtils.toBuffer("0123456789");

    HttpGenerator gen = new HttpGenerator();

    HttpGenerator.Result result = gen.generateResponse(null, null, null, content, true);
    assertEquals(HttpGenerator.Result.NEED_INFO, result);
    assertEquals(HttpGenerator.State.START, gen.getState());

    MetaData.Response info =
        new MetaData.Response(HttpVersion.HTTP_1_1, 200, null, new HttpFields(), 10);
    info.getFields().add("Content-Type", "test/data;\r\nextra=value");
    info.getFields().add("Last-Modified", DateGenerator.__01Jan1970);

    result = gen.generateResponse(info, null, null, content, true);
    assertEquals(HttpGenerator.Result.NEED_HEADER, result);

    result = gen.generateResponse(info, header, null, content, true);
    assertEquals(HttpGenerator.Result.FLUSH, result);
    assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
    String response = BufferUtils.toString(header);
    BufferUtils.clear(header);
    response += BufferUtils.toString(content);
    BufferUtils.clear(content);

    result = gen.generateResponse(null, null, null, content, false);
    assertEquals(HttpGenerator.Result.DONE, result);
    assertEquals(HttpGenerator.State.END, gen.getState());

    assertEquals(10, gen.getContentPrepared());

    assertThat(response, containsString("HTTP/1.1 200 OK"));
    assertThat(response, containsString("Last-Modified: Thu, 01 Jan 1970 00:00:00 GMT"));
    assertThat(response, containsString("Content-Type: test/data;  extra=value"));
    assertThat(response, containsString("Content-Length: 10"));
    assertThat(response, containsString("\r\n0123456789"));
  }
Example #13
0
  /*
   * Parse the message headers and return true if the handler has signaled for
   * a return
   */
  protected boolean parseHeaders(ByteBuffer buffer) {
    boolean handle = false;

    // Process headers
    while (_state.ordinal() < State.CONTENT.ordinal() && buffer.hasRemaining() && !handle) {
      // process each character
      byte ch = next(buffer);
      if (ch == 0) break;

      if (_maxHeaderBytes > 0 && ++_headerBytes > _maxHeaderBytes) {
        log.warn("Header is too large > {}", _maxHeaderBytes);
        throw new BadMessageException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413);
      }

      switch (_state) {
        case HEADER:
          switch (ch) {
            case HttpTokens.COLON:
            case HttpTokens.SPACE:
            case HttpTokens.TAB:
              throw new BadMessageException(HttpStatus.BAD_REQUEST_400, "Bad Continuation");

            case HttpTokens.LINE_FEED:
              {
                _contentPosition = 0;

                // End of headers!

                // Was there a required host header?
                if (!_host && _version == HttpVersion.HTTP_1_1 && _requestHandler != null) {
                  throw new BadMessageException(HttpStatus.BAD_REQUEST_400, "No Host");
                }

                // is it a response that cannot have a body?
                if (_responseHandler != null
                    && // response
                    (_responseStatus == 304
                        || // not-modified response
                        _responseStatus == 204
                        || // no-content
                        // response
                        _responseStatus < 200)) // 1xx response
                _endOfContent = EndOfContent.NO_CONTENT; // ignore any
                // other
                // headers
                // set

                // else if we don't know framing
                else if (_endOfContent == EndOfContent.UNKNOWN_CONTENT) {
                  if (_responseStatus == 0 // request
                      || _responseStatus == 304 // not-modified
                      // response
                      || _responseStatus == 204 // no-content response
                      || _responseStatus < 200) // 1xx response
                  _endOfContent = EndOfContent.NO_CONTENT;
                  else _endOfContent = EndOfContent.EOF_CONTENT;
                }

                // How is the message ended?
                switch (_endOfContent) {
                  case EOF_CONTENT:
                    setState(State.EOF_CONTENT);
                    handle = _handler.headerComplete() || handle;
                    return handle;

                  case CHUNKED_CONTENT:
                    setState(State.CHUNKED_CONTENT);
                    handle = _handler.headerComplete() || handle;
                    return handle;

                  case NO_CONTENT:
                    handle = _handler.headerComplete() || handle;
                    setState(State.END);
                    handle = _handler.messageComplete() || handle;
                    return handle;

                  default:
                    setState(State.CONTENT);
                    handle = _handler.headerComplete() || handle;
                    return handle;
                }
              }

            default:
              {
                // now handle the ch
                if (ch <= HttpTokens.SPACE) throw new BadMessageException();

                if (buffer.hasRemaining()) {
                  // Try a look ahead for the known header name and value.
                  HttpField field =
                      _connectionFields == null
                          ? null
                          : _connectionFields.getBest(buffer, -1, buffer.remaining());
                  if (field == null) field = CACHE.getBest(buffer, -1, buffer.remaining());

                  if (field != null) {
                    final String n;
                    final String v;

                    if (_strict) {
                      // Have to get the fields exactly from the
                      // buffer to match case
                      String fn = field.getName();
                      String fv = field.getValue();
                      n =
                          BufferUtils.toString(
                              buffer,
                              buffer.position() - 1,
                              fn.length(),
                              StandardCharsets.US_ASCII);
                      if (fv == null) v = null;
                      else {
                        v =
                            BufferUtils.toString(
                                buffer,
                                buffer.position() + fn.length() + 1,
                                fv.length(),
                                StandardCharsets.ISO_8859_1);
                        field = new HttpField(field.getHeader(), n, v);
                      }
                    } else {
                      n = field.getName();
                      v = field.getValue();
                    }

                    _header = field.getHeader();
                    _headerString = n;

                    if (v == null) {
                      // Header only
                      setState(State.HEADER_VALUE);
                      _string.setLength(0);
                      _length = 0;
                      buffer.position(buffer.position() + n.length() + 1);
                      break;
                    } else {
                      // Header and value
                      int pos = buffer.position() + n.length() + v.length() + 1;
                      byte b = buffer.get(pos);

                      if (b == HttpTokens.CARRIAGE_RETURN || b == HttpTokens.LINE_FEED) {
                        _field = field;
                        _valueString = v;
                        setState(State.HEADER_IN_VALUE);

                        if (b == HttpTokens.CARRIAGE_RETURN) {
                          _cr = true;
                          buffer.position(pos + 1);
                        } else buffer.position(pos);
                        break;
                      } else {
                        setState(State.HEADER_IN_VALUE);
                        setString(v);
                        buffer.position(pos);
                        break;
                      }
                    }
                  }
                }

                // New header
                setState(State.HEADER_IN_NAME);
                _string.setLength(0);
                _string.append((char) ch);
                _length = 1;
              }
          }
          break;

        case HEADER_IN_NAME:
          if (ch == HttpTokens.COLON) {
            if (_headerString == null) {
              _headerString = takeString();
              _header = HttpHeader.CACHE.get(_headerString);
            }
            _length = -1;

            setState(State.HEADER_VALUE);
            break;
          }

          if (ch > HttpTokens.SPACE) {
            if (_header != null) {
              setString(_header.asString());
              _header = null;
              _headerString = null;
            }

            _string.append((char) ch);
            if (ch > HttpTokens.SPACE) _length = _string.length();
            break;
          }

          throw new IllegalCharacterException(_state, ch, buffer);

        case HEADER_VALUE:
          if (ch > HttpTokens.SPACE || ch < 0) {
            _string.append((char) (0xff & ch));
            _length = _string.length();
            setState(State.HEADER_IN_VALUE);
            break;
          }

          if (ch == HttpTokens.SPACE || ch == HttpTokens.TAB) break;

          if (ch == HttpTokens.LINE_FEED) {
            _value = null;
            _string.setLength(0);
            _valueString = null;
            _length = -1;

            parsedHeader();
            setState(State.HEADER);
            break;
          }
          throw new IllegalCharacterException(_state, ch, buffer);

        case HEADER_IN_VALUE:
          if (ch >= HttpTokens.SPACE || ch < 0 || ch == HttpTokens.TAB) {
            if (_valueString != null) {
              setString(_valueString);
              _valueString = null;
              _field = null;
            }
            _string.append((char) (0xff & ch));
            if (ch > HttpTokens.SPACE || ch < 0) _length = _string.length();
            break;
          }

          if (ch == HttpTokens.LINE_FEED) {
            if (_length > 0) {
              _value = null;
              _valueString = takeString();
              _length = -1;
            }
            parsedHeader();
            setState(State.HEADER);
            break;
          }

          throw new IllegalCharacterException(_state, ch, buffer);

        default:
          throw new IllegalStateException(_state.toString());
      }
    }

    return handle;
  }
Example #14
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;
  }
Example #15
0
  /**
   * Parse until next Event.
   *
   * @param buffer the buffer to parse
   * @return True if an {@link RequestHandler} method was called and it returned true;
   */
  public boolean parseNext(ByteBuffer buffer) {
    if (DEBUG) log.debug("parseNext s={} {}", _state, BufferUtils.toDetailString(buffer));
    try {
      // Start a request/response
      if (_state == State.START) {
        _version = null;
        _method = null;
        _methodString = null;
        _endOfContent = EndOfContent.UNKNOWN_CONTENT;
        _header = null;
        if (quickStart(buffer)) return true;
      }

      // Request/response line
      if (_state.ordinal() >= State.START.ordinal() && _state.ordinal() < State.HEADER.ordinal()) {
        if (parseLine(buffer)) return true;
      }

      // parse headers
      if (_state.ordinal() >= State.HEADER.ordinal()
          && _state.ordinal() < State.CONTENT.ordinal()) {
        if (parseHeaders(buffer)) return true;
      }

      // parse content
      if (_state.ordinal() >= State.CONTENT.ordinal() && _state.ordinal() < State.END.ordinal()) {
        // Handle HEAD response
        if (_responseStatus > 0 && _headResponse) {
          setState(State.END);
          return _handler.messageComplete();
        } else {
          if (parseContent(buffer)) return true;
        }
      }

      // handle end states
      if (_state == State.END) {
        // eat white space
        while (buffer.remaining() > 0 && buffer.get(buffer.position()) <= HttpTokens.SPACE)
          buffer.get();
      } else if (_state == State.CLOSE) {
        // Seeking EOF
        if (BufferUtils.hasContent(buffer)) {
          // Just ignore data when closed
          _headerBytes += buffer.remaining();
          BufferUtils.clear(buffer);
          if (_maxHeaderBytes > 0 && _headerBytes > _maxHeaderBytes) {
            // Don't want to waste time reading data of a closed
            // request
            throw new IllegalStateException("too much data seeking EOF");
          }
        }
      } else if (_state == State.CLOSED) {
        BufferUtils.clear(buffer);
      }

      // Handle EOF
      if (_eof && !buffer.hasRemaining()) {
        switch (_state) {
          case CLOSED:
            break;

          case START:
            setState(State.CLOSED);
            _handler.earlyEOF();
            break;

          case END:
          case CLOSE:
            setState(State.CLOSED);
            break;

          case EOF_CONTENT:
            setState(State.CLOSED);
            return _handler.messageComplete();

          case CONTENT:
          case CHUNKED_CONTENT:
          case CHUNK_SIZE:
          case CHUNK_PARAMS:
          case CHUNK:
            setState(State.CLOSED);
            _handler.earlyEOF();
            break;

          default:
            if (DEBUG) log.debug("{} EOF in {}", this, _state);
            setState(State.CLOSED);
            _handler.badMessage(400, null);
            break;
        }
      }
    } catch (BadMessageException e) {
      BufferUtils.clear(buffer);

      Throwable cause = e.getCause();
      boolean stack =
          log.isDebugEnable()
              || (!(cause instanceof NumberFormatException)
                  && (cause instanceof RuntimeException || cause instanceof Error));

      if (stack)
        log.warn(
            "bad HTTP parsed: "
                + e._code
                + (e.getReason() != null ? " " + e.getReason() : "")
                + " for "
                + _handler,
            e);
      else
        log.warn(
            "bad HTTP parsed: "
                + e._code
                + (e.getReason() != null ? " " + e.getReason() : "")
                + " for "
                + _handler);
      setState(State.CLOSE);
      _handler.badMessage(e.getCode(), e.getReason());
    } catch (NumberFormatException | IllegalStateException e) {
      BufferUtils.clear(buffer);
      log.warn("parse exception: {} in {} for {}", e.toString(), _state, _handler);
      if (DEBUG) log.debug("parse exception", e);

      switch (_state) {
        case CLOSED:
          break;
        case CLOSE:
          _handler.earlyEOF();
          break;
        default:
          setState(State.CLOSE);
          _handler.badMessage(400, null);
      }
    } catch (Exception | Error e) {
      BufferUtils.clear(buffer);

      log.warn("parse exception: " + e.toString() + " for " + _handler, e);

      switch (_state) {
        case CLOSED:
          break;
        case CLOSE:
          _handler.earlyEOF();
          break;
        default:
          setState(State.CLOSE);
          _handler.badMessage(400, null);
      }
    }
    return false;
  }