From b00ed1aa3083800679d904223816e1a408bf4766 Mon Sep 17 00:00:00 2001
From: Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
Date: Fri, 6 Jul 2012 00:57:18 +0900
Subject: [PATCH] More http header parser tests

---
 src/HttpHeaderProcessor.cc      |  5 ++++
 test/HttpHeaderProcessorTest.cc | 45 +++++++++++++++++++++++++++++++++
 2 files changed, 50 insertions(+)

diff --git a/src/HttpHeaderProcessor.cc b/src/HttpHeaderProcessor.cc
index 1b027c5b..6baf7595 100644
--- a/src/HttpHeaderProcessor.cc
+++ b/src/HttpHeaderProcessor.cc
@@ -269,6 +269,9 @@ bool HttpHeaderProcessor::parse(const unsigned char* data, size_t length)
       break;
     case PREV_FIELD_NAME:
       if(util::isLws(c)) {
+        if(lastFieldName_.empty()) {
+          throw DL_ABORT_EX("Bad HTTP header: field name starts with LWS");
+        }
         // Evil Multi-line header field
         state_ = FIELD_VALUE;
       } else {
@@ -282,6 +285,8 @@ bool HttpHeaderProcessor::parse(const unsigned char* data, size_t length)
           state_ = HEADERS_COMPLETE;
         } else if(c == '\r') {
           state_ = PREV_EOH;
+        } else if(c == ':') {
+          throw DL_ABORT_EX("Bad HTTP header: field name starts with ':'");
         } else {
           state_ = FIELD_NAME;
           i = getFieldNameToken(lastFieldName_, data, length, i);
diff --git a/test/HttpHeaderProcessorTest.cc b/test/HttpHeaderProcessorTest.cc
index 7dc9cd0f..a46480e6 100644
--- a/test/HttpHeaderProcessorTest.cc
+++ b/test/HttpHeaderProcessorTest.cc
@@ -21,6 +21,7 @@ class HttpHeaderProcessorTest:public CppUnit::TestFixture {
   CPPUNIT_TEST(testGetHttpResponseHeader);
   CPPUNIT_TEST(testGetHttpResponseHeader_statusOnly);
   CPPUNIT_TEST(testGetHttpResponseHeader_insufficientStatusLength);
+  CPPUNIT_TEST(testGetHttpResponseHeader_nameStartsWs);
   CPPUNIT_TEST(testBeyondLimit);
   CPPUNIT_TEST(testGetHeaderString);
   CPPUNIT_TEST(testGetHttpRequestHeader);
@@ -35,6 +36,7 @@ public:
   void testGetHttpResponseHeader();
   void testGetHttpResponseHeader_statusOnly();
   void testGetHttpResponseHeader_insufficientStatusLength();
+  void testGetHttpResponseHeader_nameStartsWs();
   void testBeyondLimit();
   void testGetHeaderString();
   void testGetHttpRequestHeader();
@@ -71,6 +73,7 @@ void HttpHeaderProcessorTest::testParse3()
     "  text3\r\n"
     "Duplicate: foo\r\n"
     "Duplicate: bar\r\n"
+    "No-value:\r\n"
     "\r\n";
   CPPUNIT_ASSERT(proc.parse(s));
   SharedHandle<HttpHeader> h = proc.getResult();
@@ -84,6 +87,8 @@ void HttpHeaderProcessorTest::testParse3()
                        h->findAll("duplicate")[0]);
   CPPUNIT_ASSERT_EQUAL(std::string("bar"),
                        h->findAll("duplicate")[1]);
+  CPPUNIT_ASSERT_EQUAL(std::string(""), h->find("no-value"));
+  CPPUNIT_ASSERT(h->defined("no-value"));
 }
 
 void HttpHeaderProcessorTest::testGetLastBytesProcessed()
@@ -172,6 +177,46 @@ void HttpHeaderProcessorTest::testGetHttpResponseHeader_insufficientStatusLength
   }
 }
 
+void HttpHeaderProcessorTest::testGetHttpResponseHeader_nameStartsWs()
+{
+  HttpHeaderProcessor proc(HttpHeaderProcessor::CLIENT_PARSER);
+
+  std::string hd =
+    "HTTP/1.1 200\r\n"
+    " foo:bar\r\n"
+    "\r\n";
+  try {
+    proc.parse(hd);
+    CPPUNIT_FAIL("Exception must be thrown.");
+  } catch(DlAbortEx& ex) {
+    // Success
+  }
+
+  proc.clear();
+  hd =
+    "HTTP/1.1 200\r\n"
+    ":foo:bar\r\n"
+    "\r\n";
+  try {
+    proc.parse(hd);
+    CPPUNIT_FAIL("Exception must be thrown.");
+  } catch(DlAbortEx& ex) {
+    // Success
+  }
+
+  proc.clear();
+  hd =
+    "HTTP/1.1 200\r\n"
+    ":foo\r\n"
+    "\r\n";
+  try {
+    proc.parse(hd);
+    CPPUNIT_FAIL("Exception must be thrown.");
+  } catch(DlAbortEx& ex) {
+    // Success
+  }
+}
+
 void HttpHeaderProcessorTest::testBeyondLimit()
 {
   HttpHeaderProcessor proc(HttpHeaderProcessor::CLIENT_PARSER);