]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Unit-tests for the new HTTP request-line parser
authorAmos Jeffries <amosjeffries@squid-cache.org>
Thu, 2 Sep 2010 03:09:42 +0000 (21:09 -0600)
committerAmos Jeffries <amosjeffries@squid-cache.org>
Thu, 2 Sep 2010 03:09:42 +0000 (21:09 -0600)
src/tests/testHttpRequest.cc
src/tests/testHttpRequest.h

index 841b1cf225ce4a6642d112943e2c94dd75b3e7d6..6cdcb3b0569cd022bc414ea5f42775d2173b31b9 100644 (file)
@@ -199,3 +199,845 @@ testHttpRequest::testSanityCheckStartLine()
     input.reset();
     error = HTTP_STATUS_NONE;
 }
+
+void
+testHttpRequest::testParseRequestLine()
+{
+    MemBuf input;
+    HttpParser output;
+    input.init();
+
+    // TEST: Do we comply with RFC 1945 section 5.1 ?
+    // TEST: Do we comply with RFC 2616 section 5.1 ?
+
+    // RFC 1945 : HTTP/0.9 simple-request
+    input.append("GET /\r\n", 7);
+    HttpParserInit(&output, input.content(), input.contentSize());
+    CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_OK, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(0, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(input.contentSize()-1, output.req_end);
+    CPPUNIT_ASSERT(memcmp("GET /\r\n", &output.buf[output.req_start],(output.req_end-output.req_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(0, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(2, output.m_end);
+    CPPUNIT_ASSERT(memcmp("GET", &output.buf[output.m_start], (output.m_end-output.m_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(4, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(4, output.u_end);
+    CPPUNIT_ASSERT(memcmp("/", &output.buf[output.u_start], (output.u_end-output.u_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(-1, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.v_end);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(9, output.v_min);
+    input.reset();
+
+    // RFC 1945 and 2616 : HTTP/1.0 full-request
+    input.append("GET / HTTP/1.0\r\n", 16);
+    HttpParserInit(&output, input.content(), input.contentSize());
+    CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_OK, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(0, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(input.contentSize()-1, output.req_end);
+    CPPUNIT_ASSERT(memcmp("GET / HTTP/1.0\r\n", &output.buf[output.req_start],(output.req_end-output.req_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(0, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(2, output.m_end);
+    CPPUNIT_ASSERT(memcmp("GET", &output.buf[output.m_start],(output.m_end-output.m_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(4, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(4, output.u_end);
+    CPPUNIT_ASSERT(memcmp("/", &output.buf[output.u_start],(output.u_end-output.u_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(6, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(13, output.v_end);
+    CPPUNIT_ASSERT(memcmp("HTTP/1.0", &output.buf[output.v_start],(output.v_end-output.v_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(1, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_min);
+    input.reset();
+
+
+    // RFC 2616 : HTTP/1.1 full-request
+    input.append("GET / HTTP/1.1\r\n", 16);
+    HttpParserInit(&output, input.content(), input.contentSize());
+    CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_OK, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(0, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(input.contentSize()-1, output.req_end);
+    CPPUNIT_ASSERT(memcmp("GET / HTTP/1.1\r\n", &output.buf[output.req_start],(output.req_end-output.req_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(0, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(2, output.m_end);
+    CPPUNIT_ASSERT(memcmp("GET", &output.buf[output.m_start],(output.m_end-output.m_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(4, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(4, output.u_end);
+    CPPUNIT_ASSERT(memcmp("/", &output.buf[output.u_start],(output.u_end-output.u_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(6, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(13, output.v_end);
+    CPPUNIT_ASSERT(memcmp("HTTP/1.1", &output.buf[output.v_start],(output.v_end-output.v_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(1, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(1, output.v_min);
+    input.reset();
+
+    // RFC 2616 : future version full-request
+    input.append("GET / HTTP/10.12\r\n", 18);
+    //printf("TEST: '%s'\n",input.content());
+    HttpParserInit(&output, input.content(), input.contentSize());
+    CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_OK, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(0, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(input.contentSize()-1, output.req_end);
+    CPPUNIT_ASSERT(memcmp("GET / HTTP/10.12\r\n", &output.buf[output.req_start],(output.req_end-output.req_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(0, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(2, output.m_end);
+    CPPUNIT_ASSERT(memcmp("GET", &output.buf[output.m_start],(output.m_end-output.m_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(4, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(4, output.u_end);
+    CPPUNIT_ASSERT(memcmp("/", &output.buf[output.u_start],(output.u_end-output.u_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(6, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(15, output.v_end);
+    CPPUNIT_ASSERT(memcmp("HTTP/10.12", &output.buf[output.v_start],(output.v_end-output.v_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(10, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(12, output.v_min);
+    input.reset();
+
+    // space padded URL
+    input.append("GET  /     HTTP/1.1\r\n", 21);
+    //printf("TEST: '%s'\n",input.content());
+    HttpParserInit(&output, input.content(), input.contentSize());
+    CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_OK, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(0, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(input.contentSize()-1, output.req_end);
+    CPPUNIT_ASSERT(memcmp("GET  /     HTTP/1.1\r\n", &output.buf[output.req_start],(output.req_end-output.req_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(0, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(2, output.m_end);
+    CPPUNIT_ASSERT(memcmp("GET", &output.buf[output.m_start],(output.m_end-output.m_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(5, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(5, output.u_end);
+    CPPUNIT_ASSERT(memcmp("/", &output.buf[output.u_start],(output.u_end-output.u_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(11, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(18, output.v_end);
+    CPPUNIT_ASSERT(memcmp("HTTP/1.1", &output.buf[output.v_start],(output.v_end-output.v_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(1, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(1, output.v_min);
+    input.reset();
+
+    // space padded version
+    // RFC 1945 and 2616 specify version is followed by CRLF. No intermediary bytes.
+    // NP: the terminal whitespace is a special case: invalid for even HTTP/0.9 with no version tag
+    input.append("GET / HTTP/1.1 \n", 16);
+    //printf("TEST: '%s'\n",input.content());
+    HttpParserInit(&output, input.content(), input.contentSize());
+    CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_BAD_REQUEST, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(0, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(input.contentSize()-1, output.req_end);
+    CPPUNIT_ASSERT(memcmp("GET / HTTP/1.1 \n", &output.buf[output.req_start],(output.req_end-output.req_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(0, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(2, output.m_end);
+    CPPUNIT_ASSERT(memcmp("GET", &output.buf[output.m_start],(output.m_end-output.m_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(4, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(13, output.u_end);
+    CPPUNIT_ASSERT(memcmp("/ HTTP/1.1", &output.buf[output.u_start],(output.u_end-output.u_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(-1, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.v_end);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_min);
+    input.reset();
+
+    // whitespace inside URI. (nasty but happens)
+    input.append("GET /fo o/ HTTP/1.1\n", 20);
+    //printf("TEST: '%s'\n",input.content());
+    HttpParserInit(&output, input.content(), input.contentSize());
+    CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_OK, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(0, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(input.contentSize()-1, output.req_end);
+    CPPUNIT_ASSERT(memcmp("GET /fo o/ HTTP/1.1\n", &output.buf[output.req_start],(output.req_end-output.req_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(0, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(2, output.m_end);
+    CPPUNIT_ASSERT(memcmp("GET", &output.buf[output.m_start],(output.m_end-output.m_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(4, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(9, output.u_end);
+    CPPUNIT_ASSERT(memcmp("/fo o/", &output.buf[output.u_start],(output.u_end-output.u_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(11, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(18, output.v_end);
+    CPPUNIT_ASSERT(memcmp("HTTP/1.1", &output.buf[output.v_start],(output.v_end-output.v_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(1, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(1, output.v_min);
+    input.reset();
+
+    // additional data in buffer
+    input.append("GET /     HTTP/1.1\nboo!", 23);
+    //printf("TEST: '%s'\n",input.content());
+    HttpParserInit(&output, input.content(), input.contentSize());
+    CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_OK, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(0, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(input.contentSize()-5, output.req_end);
+    CPPUNIT_ASSERT(memcmp("GET /     HTTP/1.1\n", &output.buf[output.req_start],(output.req_end-output.req_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(0, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(2, output.m_end);
+    CPPUNIT_ASSERT(memcmp("GET", &output.buf[output.m_start],(output.m_end-output.m_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(4, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(4, output.u_end); // strangeness generated by following RFC
+    CPPUNIT_ASSERT(memcmp("/", &output.buf[output.u_start],(output.u_end-output.u_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(10, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(17, output.v_end);
+    CPPUNIT_ASSERT(memcmp("HTTP/1.1", &output.buf[output.v_start],(output.v_end-output.v_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(1, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(1, output.v_min);
+    input.reset();
+
+    // alternative EOL sequence: NL-only
+    input.append("GET / HTTP/1.1\n", 15);
+    //printf("TEST: '%s'\n",input.content());
+    HttpParserInit(&output, input.content(), input.contentSize());
+    CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_OK, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(0, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(input.contentSize()-1, output.req_end);
+    CPPUNIT_ASSERT(memcmp("GET / HTTP/1.1\n", &output.buf[output.req_start],(output.req_end-output.req_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(0, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(2, output.m_end);
+    CPPUNIT_ASSERT(memcmp("GET", &output.buf[output.m_start],(output.m_end-output.m_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(4, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(4, output.u_end);
+    CPPUNIT_ASSERT(memcmp("/", &output.buf[output.u_start],(output.u_end-output.u_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(6, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(13, output.v_end);
+    CPPUNIT_ASSERT(memcmp("HTTP/1.1", &output.buf[output.v_start],(output.v_end-output.v_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(1, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(1, output.v_min);
+    input.reset();
+
+    // alternative EOL sequence: double-NL-only
+    input.append("GET / HTTP/1.1\n\n", 16);
+    //printf("TEST: '%s'\n",input.content());
+    HttpParserInit(&output, input.content(), input.contentSize());
+    CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_OK, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(0, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(input.contentSize()-2, output.req_end);
+    CPPUNIT_ASSERT(memcmp("GET / HTTP/1.1\n", &output.buf[output.req_start],(output.req_end-output.req_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(0, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(2, output.m_end);
+    CPPUNIT_ASSERT(memcmp("GET", &output.buf[output.m_start],(output.m_end-output.m_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(4, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(4, output.u_end);
+    CPPUNIT_ASSERT(memcmp("/", &output.buf[output.u_start],(output.u_end-output.u_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(6, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(13, output.v_end);
+    CPPUNIT_ASSERT(memcmp("HTTP/1.1", &output.buf[output.v_start],(output.v_end-output.v_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(1, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(1, output.v_min);
+    input.reset();
+
+    // RELAXED alternative EOL sequence: multi-CR-NL
+    input.append("GET / HTTP/1.1\r\r\r\n", 18);
+    //printf("TEST: '%s'\n",input.content());
+    HttpParserInit(&output, input.content(), input.contentSize());
+    Config.onoff.relaxed_header_parser = 1;
+    // Being tolerant we can ignore and elide these apparently benign CR
+    CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_OK, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(0, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(input.contentSize()-1, output.req_end);
+    CPPUNIT_ASSERT(memcmp("GET / HTTP/1.1\r\r\r\n", &output.buf[output.req_start],(output.req_end-output.req_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(0, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(2, output.m_end);
+    CPPUNIT_ASSERT(memcmp("GET", &output.buf[output.m_start],(output.m_end-output.m_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(4, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(4, output.u_end);
+    CPPUNIT_ASSERT(memcmp("/", &output.buf[output.u_start],(output.u_end-output.u_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(6, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(13, output.v_end);
+    CPPUNIT_ASSERT(memcmp("HTTP/1.1", &output.buf[output.v_start],(output.v_end-output.v_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(1, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(1, output.v_min);
+    input.reset();
+
+    // STRICT alternative EOL sequence: multi-CR-NL
+    input.append("GET / HTTP/1.1\r\r\r\n", 18);
+    //printf("TEST: '%s'\n",input.content());
+    HttpParserInit(&output, input.content(), input.contentSize());
+    // strict mode treats these as several bare-CR in the request line which is explicitly invalid.
+    Config.onoff.relaxed_header_parser = 0;
+    CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_BAD_REQUEST, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(0, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.req_end);
+    CPPUNIT_ASSERT_EQUAL(-1, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.m_end);
+    CPPUNIT_ASSERT_EQUAL(-1, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.u_end);
+    CPPUNIT_ASSERT_EQUAL(-1, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.v_end);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_min);
+    input.reset();
+
+    // RFC 2616 : . method
+    input.append(". / HTTP/1.1\n", 13);
+    //printf("TEST: '%s'\n",input.content());
+    HttpParserInit(&output, input.content(), input.contentSize());
+    CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_OK, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(0, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(input.contentSize()-1, output.req_end);
+    CPPUNIT_ASSERT(memcmp(". / HTTP/1.1\n", &output.buf[output.req_start],(output.req_end-output.req_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(0, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(0, output.m_end);
+    CPPUNIT_ASSERT(memcmp(".", &output.buf[output.m_start],(output.m_end-output.m_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(2, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(2, output.u_end);
+    CPPUNIT_ASSERT(memcmp("/", &output.buf[output.u_start],(output.u_end-output.u_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(4, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(11, output.v_end);
+    CPPUNIT_ASSERT(memcmp("HTTP/1.1", &output.buf[output.v_start],(output.v_end-output.v_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(1, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(1, output.v_min);
+    input.reset();
+
+    // OPTIONS with * URL
+    input.append("OPTIONS * HTTP/1.1\n", 19);
+    //printf("TEST: '%s'\n",input.content());
+    HttpParserInit(&output, input.content(), input.contentSize());
+    CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_OK, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(0, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(input.contentSize()-1, output.req_end);
+    CPPUNIT_ASSERT(memcmp("OPTIONS * HTTP/1.1\n", &output.buf[output.req_start],(output.req_end-output.req_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(0, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(6, output.m_end);
+    CPPUNIT_ASSERT(memcmp("OPTIONS", &output.buf[output.m_start],(output.m_end-output.m_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(8, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(8, output.u_end);
+    CPPUNIT_ASSERT(memcmp("*", &output.buf[output.u_start],(output.u_end-output.u_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(10, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(17, output.v_end);
+    CPPUNIT_ASSERT(memcmp("HTTP/1.1", &output.buf[output.v_start],(output.v_end-output.v_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(1, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(1, output.v_min);
+    input.reset();
+
+    // unknown method
+    input.append("HELLOWORLD / HTTP/1.1\n", 22);
+    //printf("TEST: '%s'\n",input.content());
+    HttpParserInit(&output, input.content(), input.contentSize());
+    CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_OK, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(0, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(input.contentSize()-1, output.req_end);
+    CPPUNIT_ASSERT(memcmp("HELLOWORLD / HTTP/1.1\n", &output.buf[output.req_start],(output.req_end-output.req_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(0, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(9, output.m_end);
+    CPPUNIT_ASSERT(memcmp("HELLOWORLD", &output.buf[output.m_start],(output.m_end-output.m_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(11, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(11, output.u_end);
+    CPPUNIT_ASSERT(memcmp("/", &output.buf[output.u_start],(output.u_end-output.u_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(13, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(20, output.v_end);
+    CPPUNIT_ASSERT(memcmp("HTTP/1.1", &output.buf[output.v_start],(output.v_end-output.v_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(1, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(1, output.v_min);
+    input.reset();
+
+    // This stage of the parser does not yet accept non-HTTP protocol names.
+    // violations mode treats them as HTTP/0.9 requests!
+    input.append("GET / FOO/1.0\n", 14);
+    //printf("TEST: '%s'\n",input.content());
+    HttpParserInit(&output, input.content(), input.contentSize());
+#if USE_HTTP_VIOLATIONS
+    CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_OK, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(12, output.u_end);
+    CPPUNIT_ASSERT(memcmp("/ FOO/1.0", &output.buf[output.u_start],(output.u_end-output.u_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(9, output.v_min);
+#else
+    CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_HTTP_VERSION_NOT_SUPPORTED, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(4, output.u_end);
+    CPPUNIT_ASSERT(memcmp("/", &output.buf[output.u_start],(output.u_end-output.u_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_min);
+#endif
+    CPPUNIT_ASSERT_EQUAL(0, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(input.contentSize()-1, output.req_end);
+    CPPUNIT_ASSERT(memcmp("GET / FOO/1.0\n", &output.buf[output.req_start],(output.req_end-output.req_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(0, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(2, output.m_end);
+    CPPUNIT_ASSERT(memcmp("GET", &output.buf[output.m_start],(output.m_end-output.m_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(4, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(6, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(12, output.v_end);
+    CPPUNIT_ASSERT(memcmp("FOO/1.0", &output.buf[output.v_start],(output.v_end-output.v_start+1)) == 0);
+    input.reset();
+
+    // RELAXED space padded method (in strict mode SP is reserved so invalid as a method byte)
+    input.append(" GET / HTTP/1.1\n", 16);
+    //printf("TEST: '%s'\n",input.content());
+    HttpParserInit(&output, input.content(), input.contentSize());
+    Config.onoff.relaxed_header_parser = 1;
+    CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_OK, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(1, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(input.contentSize()-1, output.req_end);
+    CPPUNIT_ASSERT(memcmp("GET / HTTP/1.1\n", &output.buf[output.req_start],(output.req_end-output.req_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(1, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(3, output.m_end);
+    CPPUNIT_ASSERT(memcmp("GET", &output.buf[output.m_start],(output.m_end-output.m_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(5, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(5, output.u_end);
+    CPPUNIT_ASSERT(memcmp("/", &output.buf[output.u_start],(output.u_end-output.u_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(7, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(14, output.v_end);
+    CPPUNIT_ASSERT(memcmp("HTTP/1.1", &output.buf[output.v_start],(output.v_end-output.v_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(1, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(1, output.v_min);
+    input.reset();
+
+    // STRICT space padded method (in strict mode SP is reserved so invalid as a method byte)
+    input.append(" GET / HTTP/1.1\n", 16);
+    //printf("TEST: '%s'\n",input.content());
+    HttpParserInit(&output, input.content(), input.contentSize());
+    Config.onoff.relaxed_header_parser = 0;
+    CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_BAD_REQUEST, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(0, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(input.contentSize()-1, output.req_end);
+    CPPUNIT_ASSERT(memcmp(" GET / HTTP/1.1\n", &output.buf[output.req_start],(output.req_end-output.req_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(0, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.m_end);
+    CPPUNIT_ASSERT_EQUAL(-1, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.u_end);
+    CPPUNIT_ASSERT_EQUAL(-1, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.v_end);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_min);
+    input.reset();
+
+    // tab padded method (NP: tab is not SP so treated as any other binary)
+    input.append("\tGET / HTTP/1.1\n", 16);
+    //printf("TEST: '%s'\n",input.content());
+    HttpParserInit(&output, input.content(), input.contentSize());
+    CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_OK, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(0, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(input.contentSize()-1, output.req_end);
+    CPPUNIT_ASSERT(memcmp("\tGET / HTTP/1.1\n", &output.buf[output.req_start],(output.req_end-output.req_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(0, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(3, output.m_end);
+    CPPUNIT_ASSERT(memcmp("\tGET", &output.buf[output.m_start],(output.m_end-output.m_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(5, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(5, output.u_end);
+    CPPUNIT_ASSERT(memcmp("/", &output.buf[output.u_start],(output.u_end-output.u_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(7, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(14, output.v_end);
+    CPPUNIT_ASSERT(memcmp("HTTP/1.1", &output.buf[output.v_start],(output.v_end-output.v_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(1, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(1, output.v_min);
+    input.reset();
+
+    input.append("GET", 3);
+    //printf("TEST: '%s'\n",input.content());
+    HttpParserInit(&output, input.content(), input.contentSize());
+    CPPUNIT_ASSERT_EQUAL(0, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_STATUS_NONE, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(0, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.req_end);
+    CPPUNIT_ASSERT_EQUAL(-1, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.m_end);
+    CPPUNIT_ASSERT_EQUAL(-1, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.u_end);
+    CPPUNIT_ASSERT_EQUAL(-1, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.v_end);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_min);
+    input.reset();
+
+    input.append("GET ", 4);
+    //printf("TEST: '%s'\n",input.content());
+    HttpParserInit(&output, input.content(), input.contentSize());
+    CPPUNIT_ASSERT_EQUAL(0, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_STATUS_NONE, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(0, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.req_end);
+    CPPUNIT_ASSERT_EQUAL(-1, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.m_end);
+    CPPUNIT_ASSERT_EQUAL(-1, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.u_end);
+    CPPUNIT_ASSERT_EQUAL(-1, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.v_end);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_min);
+    input.reset();
+
+    input.append("GET / HT", 8);
+    //printf("TEST: '%s'\n",input.content());
+    HttpParserInit(&output, input.content(), input.contentSize());
+    CPPUNIT_ASSERT_EQUAL(0, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_STATUS_NONE, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(0, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.req_end);
+    CPPUNIT_ASSERT_EQUAL(-1, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.m_end);
+    CPPUNIT_ASSERT_EQUAL(-1, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.u_end);
+    CPPUNIT_ASSERT_EQUAL(-1, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.v_end);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_min);
+    input.reset();
+
+    input.append("GET / HTTP/1.1", 14);
+    //printf("TEST: '%s'\n",input.content());
+    HttpParserInit(&output, input.content(), input.contentSize());
+    CPPUNIT_ASSERT_EQUAL(0, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_STATUS_NONE, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(0, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.req_end);
+    CPPUNIT_ASSERT_EQUAL(-1, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.m_end);
+    CPPUNIT_ASSERT_EQUAL(-1, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.u_end);
+    CPPUNIT_ASSERT_EQUAL(-1, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.v_end);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_min);
+    input.reset();
+
+    // method-only
+    input.append("A\n", 2);
+    //printf("TEST: '%s'\n",input.content());
+    HttpParserInit(&output, input.content(), input.contentSize());
+    CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_BAD_REQUEST, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(0, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(input.contentSize()-1, output.req_end);
+    CPPUNIT_ASSERT(memcmp("A\n", &output.buf[output.req_start],(output.req_end-output.req_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(0, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.m_end);
+    CPPUNIT_ASSERT_EQUAL(-1, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.u_end);
+    CPPUNIT_ASSERT_EQUAL(-1, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.v_end);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_min);
+    input.reset();
+
+    // no method (but in a form which is ambiguous with HTTP/0.9 simple-request)
+    input.append("/ HTTP/1.0\n", 11);
+    //printf("TEST: '%s'\n",input.content());
+    HttpParserInit(&output, input.content(), input.contentSize());
+    CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_OK, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(0, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(input.contentSize()-1, output.req_end);
+    CPPUNIT_ASSERT(memcmp("/ HTTP/1.0\n", &output.buf[output.req_start],(output.req_end-output.req_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(0, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(0, output.m_end);
+    CPPUNIT_ASSERT(memcmp("/", &output.buf[output.m_start],(output.m_end-output.m_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(2, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(9, output.u_end);
+    CPPUNIT_ASSERT(memcmp("HTTP/1.0", &output.buf[output.u_start],(output.u_end-output.u_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(-1, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.v_end);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(9, output.v_min);
+    input.reset();
+
+    // RELAXED no method (an invalid format)
+    input.append(" / HTTP/1.0\n", 12);
+    //printf("TEST: '%s'\n",input.content());
+    HttpParserInit(&output, input.content(), input.contentSize());
+    // When tolerantly ignoring SP prefix this case becomes ambiguous with HTTP/0.9 simple-request)
+    Config.onoff.relaxed_header_parser = 1;
+    CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_OK, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(1, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(input.contentSize()-1, output.req_end);
+    CPPUNIT_ASSERT(memcmp("/ HTTP/1.0\n", &output.buf[output.req_start],(output.req_end-output.req_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(1, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(1, output.m_end);
+    CPPUNIT_ASSERT(memcmp("/", &output.buf[output.m_start],(output.m_end-output.m_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(3, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(10, output.u_end);
+    CPPUNIT_ASSERT(memcmp("HTTP/1.0", &output.buf[output.u_start],(output.u_end-output.u_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(-1, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.v_end);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(9, output.v_min);
+    input.reset();
+
+    // STRICT no method (an invalid format)
+    input.append(" / HTTP/1.0\n", 12);
+    //printf("TEST: '%s'\n",input.content());
+    HttpParserInit(&output, input.content(), input.contentSize());
+    // When tolerantly ignoring SP prefix this case becomes ambiguous with HTTP/0.9 simple-request)
+    Config.onoff.relaxed_header_parser = 0;
+    CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_BAD_REQUEST, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(0, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(input.contentSize()-1, output.req_end);
+    CPPUNIT_ASSERT(memcmp(" / HTTP/1.0\n", &output.buf[output.req_start],(output.req_end-output.req_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(0, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.m_end);
+    CPPUNIT_ASSERT_EQUAL(-1, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.u_end);
+    CPPUNIT_ASSERT_EQUAL(-1, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.v_end);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_min);
+    input.reset();
+
+    // binary code in method (strange but ...)
+    input.append("GET\x0B / HTTP/1.1\n", 16);
+    //printf("TEST: %d-%d/%d '%.*s'\n", output.req_start, output.req_end, input.contentSize(), 16, input.content());
+    HttpParserInit(&output, input.content(), input.contentSize());
+    CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_OK, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(0, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(input.contentSize()-1, output.req_end);
+    CPPUNIT_ASSERT(memcmp("GET\x0B / HTTP/1.1\n", &output.buf[output.req_start],(output.req_end-output.req_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(0, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(3, output.m_end);
+    CPPUNIT_ASSERT(memcmp("GET\x0B", &output.buf[output.m_start],(output.m_end-output.m_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(5, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(5, output.u_end);
+    CPPUNIT_ASSERT(memcmp("/", &output.buf[output.u_start],(output.u_end-output.u_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(7, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(14, output.v_end);
+    CPPUNIT_ASSERT(memcmp("HTTP/1.1", &output.buf[output.v_start],(output.v_end-output.v_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(1, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(1, output.v_min);
+    input.reset();
+
+    // CR in method
+    // RFC 2616 sec 5.1 prohibits CR other than in terminator.
+    input.append("GET\r / HTTP/1.1\r\n", 16);
+    //printf("TEST: '%s'\n",input.content());
+    HttpParserInit(&output, input.content(), input.contentSize());
+    CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_BAD_REQUEST, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(0, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.req_end);
+    CPPUNIT_ASSERT_EQUAL(-1, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.m_end);
+    CPPUNIT_ASSERT_EQUAL(-1, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.u_end);
+    CPPUNIT_ASSERT_EQUAL(-1, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.v_end);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_min);
+    input.reset();
+
+    // binary code NUL! in method (strange but ...)
+    input.append("GET\0 / HTTP/1.1\n", 16);
+    //printf("TEST: %d-%d/%d '%.*s'\n", output.req_start, output.req_end, input.contentSize(), 16, input.content());
+    HttpParserInit(&output, input.content(), input.contentSize());
+    CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_OK, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(0, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(input.contentSize()-1, output.req_end);
+    CPPUNIT_ASSERT(memcmp("GET\0 / HTTP/1.1\n", &output.buf[output.req_start],(output.req_end-output.req_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(0, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(3, output.m_end);
+    CPPUNIT_ASSERT(memcmp("GET\0", &output.buf[output.m_start],(output.m_end-output.m_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(5, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(5, output.u_end);
+    CPPUNIT_ASSERT(memcmp("/", &output.buf[output.u_start],(output.u_end-output.u_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(7, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(14, output.v_end);
+    CPPUNIT_ASSERT(memcmp("HTTP/1.1", &output.buf[output.v_start],(output.v_end-output.v_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(1, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(1, output.v_min);
+    input.reset();
+
+    // no URL (grammer otherwise correct)
+    input.append("GET  HTTP/1.1\n", 14);
+    //printf("TEST: '%s'\n",input.content());
+    HttpParserInit(&output, input.content(), input.contentSize());
+    CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_OK, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(0, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(input.contentSize()-1, output.req_end);
+    CPPUNIT_ASSERT(memcmp("GET  HTTP/1.1\n", &output.buf[output.req_start],(output.req_end-output.req_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(0, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(2, output.m_end);
+    CPPUNIT_ASSERT(memcmp("GET", &output.buf[output.m_start],(output.m_end-output.m_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(5, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(12, output.u_end);
+    CPPUNIT_ASSERT_EQUAL(-1, output.v_start);
+    CPPUNIT_ASSERT(memcmp("HTTP/1.1", &output.buf[output.u_start],(output.u_end-output.u_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(-1, output.v_end);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(9, output.v_min);
+    input.reset();
+
+    // no URL (grammer invalid, ambiguous with RFC 1945 HTTP/0.9 simple-request)
+    input.append("GET HTTP/1.1\n", 13);
+    //printf("TEST: '%s'\n",input.content());
+    HttpParserInit(&output, input.content(), input.contentSize());
+    CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_OK, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(0, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(input.contentSize()-1, output.req_end);
+    CPPUNIT_ASSERT(memcmp("GET HTTP/1.1\n", &output.buf[output.req_start],(output.req_end-output.req_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(0, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(2, output.m_end);
+    CPPUNIT_ASSERT(memcmp("GET", &output.buf[output.m_start],(output.m_end-output.m_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(4, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(11, output.u_end);
+    CPPUNIT_ASSERT(memcmp("HTTP/1.1", &output.buf[output.u_start],(output.u_end-output.u_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(-1, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.v_end);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(9, output.v_min);
+    input.reset();
+
+    // no version
+    input.append("GET / HTTP/\n", 12);
+    //printf("TEST: '%s'\n",input.content());
+    HttpParserInit(&output, input.content(), input.contentSize());
+    CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_HTTP_VERSION_NOT_SUPPORTED, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(0, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(input.contentSize()-1, output.req_end);
+    CPPUNIT_ASSERT(memcmp("GET / HTTP/\n", &output.buf[output.req_start],(output.req_end-output.req_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(0, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(2, output.m_end);
+    CPPUNIT_ASSERT(memcmp("GET", &output.buf[output.m_start],(output.m_end-output.m_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(4, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(4, output.u_end);
+    CPPUNIT_ASSERT(memcmp("/", &output.buf[output.u_start],(output.u_end-output.u_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(6, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(10, output.v_end);
+    CPPUNIT_ASSERT(memcmp("HTTP/", &output.buf[output.v_start],(output.v_end-output.v_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_min);
+    input.reset();
+
+    // no major version
+    input.append("GET / HTTP/.1\n", 14);
+    //printf("TEST: '%s'\n",input.content());
+    HttpParserInit(&output, input.content(), input.contentSize());
+    CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_HTTP_VERSION_NOT_SUPPORTED, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(0, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(input.contentSize()-1, output.req_end);
+    CPPUNIT_ASSERT(memcmp("GET / HTTP/.1\n", &output.buf[output.req_start],(output.req_end-output.req_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(0, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(2, output.m_end);
+    CPPUNIT_ASSERT(memcmp("GET", &output.buf[output.m_start],(output.m_end-output.m_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(4, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(4, output.u_end);
+    CPPUNIT_ASSERT(memcmp("/", &output.buf[output.u_start],(output.u_end-output.u_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(6, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(12, output.v_end);
+    CPPUNIT_ASSERT(memcmp("HTTP/.1", &output.buf[output.v_start],(output.v_end-output.v_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_min);
+    input.reset();
+
+    // no version dot
+    input.append("GET / HTTP/11\n", 14);
+    //printf("TEST: '%s'\n",input.content());
+    HttpParserInit(&output, input.content(), input.contentSize());
+    CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_HTTP_VERSION_NOT_SUPPORTED, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(0, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(input.contentSize()-1, output.req_end);
+    CPPUNIT_ASSERT(memcmp("GET / HTTP/11\n", &output.buf[output.req_start],(output.req_end-output.req_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(0, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(2, output.m_end);
+    CPPUNIT_ASSERT(memcmp("GET", &output.buf[output.m_start],(output.m_end-output.m_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(4, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(4, output.u_end);
+    CPPUNIT_ASSERT(memcmp("/", &output.buf[output.u_start],(output.u_end-output.u_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(6, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(12, output.v_end);
+    CPPUNIT_ASSERT(memcmp("HTTP/11", &output.buf[output.v_start],(output.v_end-output.v_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_min);
+    input.reset();
+
+    // no minor version
+    input.append("GET / HTTP/1.\n", 14);
+    //printf("TEST: '%s'\n",input.content());
+    HttpParserInit(&output, input.content(), input.contentSize());
+    CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_HTTP_VERSION_NOT_SUPPORTED, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(0, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(input.contentSize()-1, output.req_end);
+    CPPUNIT_ASSERT(memcmp("GET / HTTP/1.\n", &output.buf[output.req_start],(output.req_end-output.req_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(0, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(2, output.m_end);
+    CPPUNIT_ASSERT(memcmp("GET", &output.buf[output.m_start],(output.m_end-output.m_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(4, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(4, output.u_end);
+    CPPUNIT_ASSERT(memcmp("/", &output.buf[output.u_start],(output.u_end-output.u_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(6, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(12, output.v_end);
+    CPPUNIT_ASSERT(memcmp("HTTP/1.", &output.buf[output.v_start],(output.v_end-output.v_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(1, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_min);
+    input.reset();
+
+    // binary line
+    input.append("\xB\xC\xE\xF\n", 5);
+    //printf("TEST: binary-line\n");
+    HttpParserInit(&output, input.content(), input.contentSize());
+    CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_BAD_REQUEST, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(0, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(input.contentSize()-1, output.req_end);
+    CPPUNIT_ASSERT(memcmp("\xB\xC\xE\xF\n", &output.buf[output.req_start],(output.req_end-output.req_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(0, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.m_end);
+    CPPUNIT_ASSERT_EQUAL(-1, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.u_end);
+    CPPUNIT_ASSERT_EQUAL(-1, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.v_end);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_min);
+    input.reset();
+
+    // mixed whitespace line
+    // We accept non-space binary bytes for method so first \t shows up as that
+    // but remaining space and tabs are skipped searching for URI-start
+    input.append("\t \t \t\n", 6);
+    //printf("TEST: mixed whitespace\n");
+    HttpParserInit(&output, input.content(), input.contentSize());
+    CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_BAD_REQUEST, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(0, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(input.contentSize()-1, output.req_end);
+    CPPUNIT_ASSERT(memcmp("\t \t \t\n", &output.buf[output.req_start],(output.req_end-output.req_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(0, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(0, output.m_end);
+    CPPUNIT_ASSERT(memcmp("\t", &output.buf[output.m_start],(output.m_end-output.m_start+1)) == 0);
+    CPPUNIT_ASSERT_EQUAL(-1, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.u_end);
+    CPPUNIT_ASSERT_EQUAL(-1, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.v_end);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_min);
+    input.reset();
+
+    // mixed whitespace line with CR middle
+    // CR aborts on sight, so even initial \t method is not marked as above
+    // (not when parsing clean with whole line available anyway)
+    input.append("\t  \r \n", 6);
+    //printf("TEST: mixed whitespace with CR\n");
+    HttpParserInit(&output, input.content(), input.contentSize());
+    CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output));
+    CPPUNIT_ASSERT_EQUAL(HTTP_BAD_REQUEST, output.request_parse_status);
+    CPPUNIT_ASSERT_EQUAL(0, output.req_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.req_end);
+    CPPUNIT_ASSERT_EQUAL(-1, output.m_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.m_end);
+    CPPUNIT_ASSERT_EQUAL(-1, output.u_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.u_end);
+    CPPUNIT_ASSERT_EQUAL(-1, output.v_start);
+    CPPUNIT_ASSERT_EQUAL(-1, output.v_end);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_maj);
+    CPPUNIT_ASSERT_EQUAL(0, output.v_min);
+    input.reset();
+}
index efdec613a6247a581d11380327cdb7db6af3f4e1..2d7f081847ed12c14d6c79aa68d9cc0c2a4cb00c 100644 (file)
@@ -15,6 +15,7 @@ class testHttpRequest : public CPPUNIT_NS::TestFixture
     CPPUNIT_TEST( testCreateFromUrl );
     CPPUNIT_TEST( testIPv6HostColonBug );
     CPPUNIT_TEST( testSanityCheckStartLine );
+    CPPUNIT_TEST( testParseRequestLine );
     CPPUNIT_TEST_SUITE_END();
 
 public:
@@ -25,6 +26,7 @@ protected:
     void testCreateFromUrl();
     void testIPv6HostColonBug();
     void testSanityCheckStartLine();
+    void testParseRequestLine();
 };
 
 #endif