@Test
  public void testSeekEOF() throws Exception {
    StringEndPoint io = new StringEndPoint();
    io.setInput(
        "HTTP/1.1 200 OK\015\012"
            + "Content-Length: 0\015\012"
            + "Connection: close\015\012"
            + "\015\012"
            + "\015\012" // extra CRLF ignored
            + "HTTP/1.1 400 OK\015\012"); // extra data causes close

    ByteArrayBuffer buffer = new ByteArrayBuffer(4096);
    SimpleBuffers buffers = new SimpleBuffers(buffer, null);

    Handler handler = new Handler();
    HttpParser parser = new HttpParser(buffers, io, handler);

    parser.parse();
    assertEquals("HTTP/1.1", f0);
    assertEquals("200", f1);
    assertEquals("OK", f2);
    assertEquals(null, _content);
    assertTrue(headerCompleted);
    assertTrue(messageCompleted);
  }
  @Test
  public void testResponseParse2() throws Exception {
    StringEndPoint io = new StringEndPoint();
    io.setInput(
        "HTTP/1.1 204 No-Content\015\012"
            + "Header: value\015\012"
            + "\015\012"
            + "HTTP/1.1 200 Correct\015\012"
            + "Content-Length: 10\015\012"
            + "Content-Type: text/plain\015\012"
            + "\015\012"
            + "0123456789\015\012");
    ByteArrayBuffer buffer = new ByteArrayBuffer(4096);
    SimpleBuffers buffers = new SimpleBuffers(buffer, null);

    Handler handler = new Handler();
    HttpParser parser = new HttpParser(buffers, io, handler);
    parser.parse();
    assertEquals("HTTP/1.1", f0);
    assertEquals("204", f1);
    assertEquals("No-Content", f2);
    assertTrue(headerCompleted);
    assertTrue(messageCompleted);

    parser.parse();
    assertEquals("HTTP/1.1", f0);
    assertEquals("200", f1);
    assertEquals("Correct", f2);
    assertEquals(_content.length(), 10);
    assertTrue(headerCompleted);
    assertTrue(messageCompleted);
  }
  @Test
  public void testChunkParse() throws Exception {
    StringEndPoint io = new StringEndPoint();
    io.setInput(
        "GET /chunk HTTP/1.0\015\012"
            + "Header1: value1\015\012"
            + "Transfer-Encoding: chunked\015\012"
            + "\015\012"
            + "a;\015\012"
            + "0123456789\015\012"
            + "1a\015\012"
            + "ABCDEFGHIJKLMNOPQRSTUVWXYZ\015\012"
            + "0\015\012");
    ByteArrayBuffer buffer = new ByteArrayBuffer(4096);
    ByteArrayBuffer content = new ByteArrayBuffer(8192);
    SimpleBuffers buffers = new SimpleBuffers(buffer, content);

    Handler handler = new Handler();
    HttpParser parser = new HttpParser(buffers, io, handler);
    parser.parse();
    assertEquals("GET", f0);
    assertEquals("/chunk", f1);
    assertEquals("HTTP/1.0", f2);
    assertEquals(1, h);
    assertEquals("Header1", hdr[0]);
    assertEquals("value1", val[0]);
    assertEquals("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", _content);
  }
  @Test
  public void testMultiParse() throws Exception {
    StringEndPoint io = new StringEndPoint();
    io.setInput(
        "GET /mp HTTP/1.0\015\012"
            + "Connection: Keep-Alive\015\012"
            + "Header1: value1\015\012"
            + "Transfer-Encoding: chunked\015\012"
            + "\015\012"
            + "a;\015\012"
            + "0123456789\015\012"
            + "1a\015\012"
            + "ABCDEFGHIJKLMNOPQRSTUVWXYZ\015\012"
            + "0\015\012"
            + "POST /foo HTTP/1.0\015\012"
            + "Connection: Keep-Alive\015\012"
            + "Header2: value2\015\012"
            + "Content-Length: 0\015\012"
            + "\015\012"
            + "PUT /doodle HTTP/1.0\015\012"
            + "Connection: close\015\012"
            + "Header3: value3\015\012"
            + "Content-Length: 10\015\012"
            + "\015\012"
            + "0123456789\015\012");

    ByteArrayBuffer buffer = new ByteArrayBuffer(4096);
    ByteArrayBuffer content = new ByteArrayBuffer(8192);
    SimpleBuffers buffers = new SimpleBuffers(buffer, content);

    Handler handler = new Handler();
    HttpParser parser = new HttpParser(buffers, io, handler);
    parser.parse();
    assertEquals("GET", f0);
    assertEquals("/mp", f1);
    assertEquals("HTTP/1.0", f2);
    assertEquals(2, h);
    assertEquals("Header1", hdr[1]);
    assertEquals("value1", val[1]);
    assertEquals("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", _content);

    parser.parse();
    assertEquals("POST", f0);
    assertEquals("/foo", f1);
    assertEquals("HTTP/1.0", f2);
    assertEquals(2, h);
    assertEquals("Header2", hdr[1]);
    assertEquals("value2", val[1]);
    assertEquals(null, _content);

    parser.parse();
    assertEquals("PUT", f0);
    assertEquals("/doodle", f1);
    assertEquals("HTTP/1.0", f2);
    assertEquals(2, h);
    assertEquals("Header3", hdr[1]);
    assertEquals("value3", val[1]);
    assertEquals("0123456789", _content);
  }
  @Test
  public void testLineParse4() throws Exception {
    StringEndPoint io = new StringEndPoint();
    io.setInput("POST /foo?param=\u0690 HTTP/1.0\015\012" + "\015\012");
    ByteArrayBuffer buffer = new ByteArrayBuffer(4096);
    SimpleBuffers buffers = new SimpleBuffers(buffer, null);

    Handler handler = new Handler();
    HttpParser parser = new HttpParser(buffers, io, handler);
    parser.parse();
    assertEquals("POST", f0);
    assertEquals("/foo?param=\u0690", f1);
    assertEquals("HTTP/1.0", f2);
    assertEquals(-1, h);
  }
  @Test
  public void testLineParse2() throws Exception {
    StringEndPoint io = new StringEndPoint();
    io.setInput("POST /222  \015\012");
    ByteArrayBuffer buffer = new ByteArrayBuffer(4096);
    SimpleBuffers buffers = new SimpleBuffers(buffer, null);

    f2 = null;
    Handler handler = new Handler();
    HttpParser parser = new HttpParser(buffers, io, handler);
    parser.parse();
    assertEquals("POST", f0);
    assertEquals("/222", f1);
    assertEquals(null, f2);
    assertEquals(-1, h);
  }
  @Test
  public void testResponseParse1() throws Exception {
    StringEndPoint io = new StringEndPoint();
    io.setInput("HTTP/1.1 304 Not-Modified\015\012" + "Connection: close\015\012" + "\015\012");
    ByteArrayBuffer buffer = new ByteArrayBuffer(4096);
    SimpleBuffers buffers = new SimpleBuffers(buffer, null);

    Handler handler = new Handler();
    HttpParser parser = new HttpParser(buffers, io, handler);
    parser.parse();
    assertEquals("HTTP/1.1", f0);
    assertEquals("304", f1);
    assertEquals("Not-Modified", f2);
    assertTrue(headerCompleted);
    assertTrue(messageCompleted);
  }
  @Test
  public void testConnect() throws Exception {
    StringEndPoint io = new StringEndPoint();
    io.setInput("CONNECT 192.168.1.2:80 HTTP/1.1\015\012" + "\015\012");
    ByteArrayBuffer buffer = new ByteArrayBuffer(4096);
    SimpleBuffers buffers = new SimpleBuffers(buffer, null);

    Handler handler = new Handler();
    HttpParser parser = new HttpParser(buffers, io, handler);
    parser.parse();
    assertTrue(handler.request);
    assertEquals("CONNECT", f0);
    assertEquals("192.168.1.2:80", f1);
    assertEquals("HTTP/1.1", f2);
    assertEquals(-1, h);
  }
  protected boolean success() {
    HttpExchange exchange = connection.getExchange();
    if (exchange == null) return false;

    AtomicMarkableReference<Result> completion = exchange.responseComplete(null);
    if (!completion.isMarked()) return false;

    parser.reset();
    decoder = null;

    if (!updateState(State.RECEIVE, State.IDLE)) throw new IllegalStateException();

    exchange.terminateResponse();

    HttpResponse response = exchange.getResponse();
    List<Response.ResponseListener> listeners = exchange.getConversation().getResponseListeners();
    ResponseNotifier notifier = connection.getDestination().getResponseNotifier();
    notifier.notifySuccess(listeners, response);
    LOG.debug("Received {}", response);

    Result result = completion.getReference();
    if (result != null) {
      connection.complete(exchange, !result.isFailed());
      notifier.notifyComplete(listeners, result);
    }

    return true;
  }
  @Override
  public boolean startResponse(HttpVersion version, int status, String reason) {
    if (updateState(State.IDLE, State.RECEIVE)) {
      HttpExchange exchange = connection.getExchange();
      // The exchange may be null if it failed concurrently
      if (exchange != null) {
        HttpConversation conversation = exchange.getConversation();
        HttpResponse response = exchange.getResponse();

        String method = exchange.getRequest().method();
        parser.setHeadResponse(HttpMethod.HEAD.is(method) || HttpMethod.CONNECT.is(method));
        response.version(version).status(status).reason(reason);

        // Probe the protocol handlers
        HttpClient client = connection.getHttpClient();
        ProtocolHandler protocolHandler =
            client.findProtocolHandler(exchange.getRequest(), response);
        Response.Listener handlerListener = null;
        if (protocolHandler != null) {
          handlerListener = protocolHandler.getResponseListener();
          LOG.debug("Found protocol handler {}", protocolHandler);
        }
        exchange.getConversation().updateResponseListeners(handlerListener);

        LOG.debug("Receiving {}", response);
        ResponseNotifier notifier = connection.getDestination().getResponseNotifier();
        notifier.notifyBegin(conversation.getResponseListeners(), response);
      }
    }
    return false;
  }
  @Test
  public void testResponse304WithContentLength() throws Exception {
    StringEndPoint io = new StringEndPoint();
    io.setInput("HTTP/1.1 304 found\015\012" + "Content-Length: 10\015\012" + "\015\012");
    ByteArrayBuffer buffer = new ByteArrayBuffer(4096);
    SimpleBuffers buffers = new SimpleBuffers(buffer, null);

    Handler handler = new Handler();
    HttpParser parser = new HttpParser(buffers, io, handler);
    parser.parse();
    assertEquals("HTTP/1.1", f0);
    assertEquals("304", f1);
    assertEquals("found", f2);
    assertEquals(null, _content);
    assertTrue(headerCompleted);
    assertTrue(messageCompleted);
  }
 private void shutdown() {
   // Shutting down the parser may invoke messageComplete() or fail()
   parser.shutdownInput();
   State state = this.state.get();
   if (state == State.IDLE || state == State.RECEIVE) {
     if (!fail(new EOFException())) connection.close();
   }
 }
  @Test
  public void testHeaderParse() throws Exception {
    StringEndPoint io = new StringEndPoint();
    io.setInput(
        "GET / HTTP/1.0\015\012"
            + "Host: localhost\015\012"
            + "Header1: value1\015\012"
            + "Header2  :   value 2a  \015\012"
            + "                    value 2b  \015\012"
            + "Header3: \015\012"
            + "Header4 \015\012"
            + "  value4\015\012"
            + "Server5: notServer\015\012"
            + "\015\012");
    ByteArrayBuffer buffer = new ByteArrayBuffer(4096);
    SimpleBuffers buffers = new SimpleBuffers(buffer, null);

    Handler handler = new Handler();
    HttpParser parser = new HttpParser(buffers, io, handler);
    parser.parse();
    assertEquals("GET", f0);
    assertEquals("/", f1);
    assertEquals("HTTP/1.0", f2);
    assertEquals("Host", hdr[0]);
    assertEquals("localhost", val[0]);
    assertEquals("Header1", hdr[1]);
    assertEquals("value1", val[1]);
    assertEquals("Header2", hdr[2]);
    assertEquals("value 2a value 2b", val[2]);
    assertEquals("Header3", hdr[3]);
    assertEquals("", val[3]);
    assertEquals("Header4", hdr[4]);
    assertEquals("value4", val[4]);
    assertEquals("Server5", hdr[5]);
    assertEquals("notServer", val[5]);
    assertEquals(5, h);
  }
  protected boolean fail(Throwable failure) {
    HttpExchange exchange = connection.getExchange();
    // In case of a response error, the failure has already been notified
    // and it is possible that a further attempt to read in the receive
    // loop throws an exception that reenters here but without exchange;
    // or, the server could just have timed out the connection.
    if (exchange == null) return false;

    AtomicMarkableReference<Result> completion = exchange.responseComplete(failure);
    if (!completion.isMarked()) return false;

    parser.close();
    decoder = null;

    while (true) {
      State current = state.get();
      if (updateState(current, State.FAILURE)) break;
    }

    exchange.terminateResponse();

    HttpResponse response = exchange.getResponse();
    HttpConversation conversation = exchange.getConversation();
    ResponseNotifier notifier = connection.getDestination().getResponseNotifier();
    notifier.notifyFailure(conversation.getResponseListeners(), response, failure);
    LOG.debug("Failed {} {}", response, failure);

    Result result = completion.getReference();
    if (result != null) {
      connection.complete(exchange, false);

      notifier.notifyComplete(conversation.getResponseListeners(), result);
    }

    return true;
  }
  @Test
  public void testStreamParse() throws Exception {
    StringEndPoint io = new StringEndPoint();
    String http =
        "GET / HTTP/1.1\015\012"
            + "Host: test\015\012"
            + "Header1: value1\015\012"
            + "Transfer-Encoding: chunked\015\012"
            + "\015\012"
            + "a;\015\012"
            + "0123456789\015\012"
            + "1a\015\012"
            + "ABCDEFGHIJKLMNOPQRSTUVWXYZ\015\012"
            + "0\015\012"
            + "POST /foo HTTP/1.1\015\012"
            + "Host: test\015\012"
            + "Header2: value2\015\012"
            + "Content-Length: 0\015\012"
            + "\015\012"
            + "PUT /doodle HTTP/1.1\015\012"
            + "Host: test\015\012"
            + "Connection: close\015\012"
            + "Header3: value3\015\012"
            + "Content-Length: 10\015\012"
            + "\015\012"
            + "0123456789\015\012";

    int[] tests = {
      1024,
      http.length() + 3,
      http.length() + 2,
      http.length() + 1,
      http.length() + 0,
      http.length() - 1,
      http.length() - 2,
      http.length() / 2,
      http.length() / 3,
      128,
      32
    };

    for (int t = 0; t < tests.length; t++) {
      String tst = "t" + t + "=" + tests[t];
      try {
        f0 = f1 = f2 = null;
        h = 0;
        ByteArrayBuffer buffer = new ByteArrayBuffer(tests[t]);
        ByteArrayBuffer content = new ByteArrayBuffer(8192);
        SimpleBuffers buffers = new SimpleBuffers(buffer, content);

        Handler handler = new Handler();
        HttpParser parser = new HttpParser(buffers, io, handler);

        io.setInput(http);

        // System.err.println(tst);
        parser.parse();
        assertEquals(tst, "GET", f0);
        assertEquals(tst, "/", f1);
        assertEquals(tst, "HTTP/1.1", f2);
        assertEquals(tst, 2, h);
        assertEquals(tst, "Header1", hdr[1]);
        assertEquals(tst, "value1", val[1]);
        assertEquals(tst, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", _content);

        parser.parse();
        assertEquals(tst, "POST", f0);
        assertEquals(tst, "/foo", f1);
        assertEquals(tst, "HTTP/1.1", f2);
        assertEquals(tst, 2, h);
        assertEquals(tst, "Header2", hdr[1]);
        assertEquals(tst, "value2", val[1]);
        assertEquals(tst, null, _content);

        parser.parse();
        assertEquals(tst, "PUT", f0);
        assertEquals(tst, "/doodle", f1);
        assertEquals(tst, "HTTP/1.1", f2);
        assertEquals(tst, 3, h);
        assertEquals(tst, "Header3", hdr[2]);
        assertEquals(tst, "value3", val[2]);
        assertEquals(tst, "0123456789", _content);
      } catch (Exception e) {
        if (t + 1 < tests.length) throw e;
        assertTrue(e.toString().indexOf("FULL") >= 0);
      }
    }
  }
 private void parse(ByteBuffer buffer) {
   while (buffer.hasRemaining()) parser.parseNext(buffer);
 }