Home | History | Annotate | Download | only in web_request
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/basictypes.h"
      6 #include "base/logging.h"
      7 #include "base/strings/string_piece.h"
      8 #include "extensions/browser/api/web_request/form_data_parser.h"
      9 #include "testing/gtest/include/gtest/gtest.h"
     10 
     11 namespace extensions {
     12 
     13 namespace {
     14 
     15 // Attempts to create a parser corresponding to the |content_type_header|.
     16 // On success, returns the parser.
     17 scoped_ptr<FormDataParser> InitParser(const std::string& content_type_header) {
     18   scoped_ptr<FormDataParser> parser(
     19       FormDataParser::CreateFromContentTypeHeader(&content_type_header));
     20   if (parser.get() == NULL)
     21     return scoped_ptr<FormDataParser>();
     22   return parser.Pass();
     23 }
     24 
     25 // Attempts to run the parser corresponding to the |content_type_header|
     26 // on the source represented by the concatenation of blocks from |bytes|.
     27 // On success, returns true and the parsed |output|, else false.
     28 // Parsed |output| has names on even positions (0, 2, ...), values on odd ones.
     29 bool RunParser(const std::string& content_type_header,
     30                const std::vector<const base::StringPiece*>& bytes,
     31                std::vector<std::string>* output) {
     32   DCHECK(output);
     33   output->clear();
     34   scoped_ptr<FormDataParser> parser(InitParser(content_type_header));
     35   if (!parser.get())
     36     return false;
     37   FormDataParser::Result result;
     38   for (size_t block = 0; block < bytes.size(); ++block) {
     39     if (!parser->SetSource(*(bytes[block])))
     40       return false;
     41     while (parser->GetNextNameValue(&result)) {
     42       output->push_back(result.name());
     43       output->push_back(result.value());
     44     }
     45   }
     46   return parser->AllDataReadOK();
     47 }
     48 
     49 // Attempts to run the parser corresponding to the |content_type_header|
     50 // on the source represented by the concatenation of blocks from |bytes|.
     51 // Checks that the parser fails parsing.
     52 bool CheckParserFails(const std::string& content_type_header,
     53                       const std::vector<const base::StringPiece*>& bytes) {
     54   std::vector<std::string> output;
     55   scoped_ptr<FormDataParser> parser(InitParser(content_type_header));
     56   if (!parser.get())
     57     return false;
     58   FormDataParser::Result result;
     59   for (size_t block = 0; block < bytes.size(); ++block) {
     60     if (!parser->SetSource(*(bytes[block])))
     61       break;
     62     while (parser->GetNextNameValue(&result)) {
     63       output.push_back(result.name());
     64       output.push_back(result.value());
     65     }
     66   }
     67   return !parser->AllDataReadOK();
     68 }
     69 
     70 }  // namespace
     71 
     72 TEST(WebRequestFormDataParserTest, Parsing) {
     73   // We verify that POST data parsers cope with various formats of POST data.
     74   // Construct the test data.
     75   const std::string kBoundary = "THIS_IS_A_BOUNDARY";
     76   const std::string kBlockStr1 =
     77       std::string("--") + kBoundary +
     78       "\r\n"
     79       "Content-Disposition: form-data; name=\"text\"\r\n"
     80       "\r\n"
     81       "test\rtext\nwith non-CRLF line breaks\r\n"
     82       "--" +
     83       kBoundary +
     84       "\r\n"
     85       "Content-Disposition: form-data; name=\"file\"; filename=\"test\"\r\n"
     86       "Content-Type: application/octet-stream\r\n"
     87       "\r\n";
     88   const std::string kBlockStr2 =
     89       std::string("\r\n--") + kBoundary +
     90       "\r\n"
     91       "Content-Disposition: form-data; name=\"password\"\r\n"
     92       "\r\n"
     93       "test password\r\n"
     94       "--" +
     95       kBoundary +
     96       "\r\n"
     97       "Content-Disposition: form-data; name=\"radio\"\r\n"
     98       "\r\n"
     99       "Yes\r\n"
    100       "--" +
    101       kBoundary +
    102       "\r\n"
    103       "Content-Disposition: form-data; name=\"check\"\r\n"
    104       "\r\n"
    105       "option A\r\n"
    106       "--" +
    107       kBoundary +
    108       "\r\n"
    109       "Content-Disposition: form-data; name=\"check\"\r\n"
    110       "\r\n"
    111       "option B\r\n"
    112       "--" +
    113       kBoundary +
    114       "\r\n"
    115       "Content-Disposition: form-data; name=\"txtarea\"\r\n"
    116       "\r\n"
    117       "Some text.\r\n"
    118       "Other.\r\n"
    119       "\r\n"
    120       "--" +
    121       kBoundary +
    122       "\r\n"
    123       "Content-Disposition: form-data; name=\"select\"\r\n"
    124       "\r\n"
    125       "one\r\n"
    126       "--" +
    127       kBoundary + "--";
    128   // POST data input.
    129   const std::string kBigBlock = kBlockStr1 + kBlockStr2;
    130   const std::string kUrlEncodedBlock =
    131       "text=test%0Dtext%0Awith+non-CRLF+line+breaks"
    132       "&file=test&password=test+password&radio=Yes&check=option+A"
    133       "&check=option+B&txtarea=Some+text.%0D%0AOther.%0D%0A&select=one";
    134   const base::StringPiece kMultipartBytes(kBigBlock);
    135   const base::StringPiece kMultipartBytesSplit1(kBlockStr1);
    136   const base::StringPiece kMultipartBytesSplit2(kBlockStr2);
    137   const base::StringPiece kUrlEncodedBytes(kUrlEncodedBlock);
    138   const std::string kPlainBlock = "abc";
    139   const base::StringPiece kTextPlainBytes(kPlainBlock);
    140   // Headers.
    141   const std::string kUrlEncoded = "application/x-www-form-urlencoded";
    142   const std::string kTextPlain = "text/plain";
    143   const std::string kMultipart =
    144       std::string("multipart/form-data; boundary=") + kBoundary;
    145   // Expected output.
    146   const char* kPairs[] = {
    147     "text", "test\rtext\nwith non-CRLF line breaks",
    148     "file", "test",
    149     "password", "test password",
    150     "radio", "Yes",
    151     "check", "option A",
    152     "check", "option B",
    153     "txtarea", "Some text.\r\nOther.\r\n",
    154     "select", "one"
    155   };
    156   const std::vector<std::string> kExpected(kPairs, kPairs + arraysize(kPairs));
    157 
    158   std::vector<const base::StringPiece*> input;
    159   std::vector<std::string> output;
    160 
    161   // First test: multipart POST data in one lump.
    162   input.push_back(&kMultipartBytes);
    163   EXPECT_TRUE(RunParser(kMultipart, input, &output));
    164   EXPECT_EQ(kExpected, output);
    165 
    166   // Second test: multipart POST data in several lumps.
    167   input.clear();
    168   input.push_back(&kMultipartBytesSplit1);
    169   input.push_back(&kMultipartBytesSplit2);
    170   EXPECT_TRUE(RunParser(kMultipart, input, &output));
    171   EXPECT_EQ(kExpected, output);
    172 
    173   // Third test: URL-encoded POST data.
    174   input.clear();
    175   input.push_back(&kUrlEncodedBytes);
    176   EXPECT_TRUE(RunParser(kUrlEncoded, input, &output));
    177   EXPECT_EQ(kExpected, output);
    178 
    179   // Fourth test: text/plain POST data in one lump.
    180   input.clear();
    181   input.push_back(&kTextPlainBytes);
    182   // This should fail, text/plain is ambiguous and thus unparseable.
    183   EXPECT_FALSE(RunParser(kTextPlain, input, &output));
    184 }
    185 
    186 TEST(WebRequestFormDataParserTest, MalformedPayload) {
    187   // We verify that POST data parsers reject malformed data.
    188   // Construct the test data.
    189   const std::string kBoundary = "THIS_IS_A_BOUNDARY";
    190   const std::string kBlockStr =
    191       std::string("--") + kBoundary +
    192       "\r\n"
    193       "Content-Disposition: form-data; name=\"text\"\r\n"
    194       "\r\n"
    195       "test\rtext\nwith non-CRLF line breaks\r\n"
    196       "-" +
    197       kBoundary +
    198       "\r\n" /* Missing '-'. */
    199       "Content-Disposition: form-data; name=\"file\"; filename=\"test\"\r\n"
    200       "Content-Type: application/octet-stream\r\n"
    201       /* Two CRLF missing. */
    202       "--" +
    203       kBoundary +
    204       "\r\n"
    205       "Content-Disposition: form-data; name=\"select\"\r\n"
    206       "\r\n"
    207       "one\r\n"
    208       "--" +
    209       kBoundary + "-" /* Missing '-' at the end. */;
    210   // POST data input.
    211   // The following block is corrupted -- contains a "==" substring.
    212   const std::string kUrlEncodedBlock =
    213       "text=test%0Dtext%0Awith+non-CRLF+line+breaks"
    214       "&file==test&password=test+password&radio=Yes&check=option+A"
    215       "&check=option+B&txtarea=Some+text.%0D%0AOther.%0D%0A&select=one";
    216   const base::StringPiece kMultipartBytes(kBlockStr);
    217   const base::StringPiece kMultipartBytesEmpty("");
    218   const base::StringPiece kUrlEncodedBytes(kUrlEncodedBlock);
    219   const base::StringPiece kUrlEncodedBytesEmpty("");
    220   // Headers.
    221   const std::string kUrlEncoded = "application/x-www-form-urlencoded";
    222   const std::string kMultipart =
    223       std::string("multipart/form-data; boundary=") + kBoundary;
    224 
    225   std::vector<const base::StringPiece*> input;
    226 
    227   // First test: malformed multipart POST data.
    228   input.push_back(&kMultipartBytes);
    229   EXPECT_TRUE(CheckParserFails(kMultipart, input));
    230 
    231   // Second test: empty multipart POST data.
    232   input.clear();
    233   input.push_back(&kMultipartBytesEmpty);
    234   EXPECT_TRUE(CheckParserFails(kMultipart, input));
    235 
    236   // Third test: malformed URL-encoded POST data.
    237   input.clear();
    238   input.push_back(&kUrlEncodedBytes);
    239   EXPECT_TRUE(CheckParserFails(kUrlEncoded, input));
    240 
    241   // Fourth test: empty URL-encoded POST data. Note that an empty string is a
    242   // valid url-encoded value, so this should parse correctly.
    243   std::vector<std::string> output;
    244   input.clear();
    245   input.push_back(&kUrlEncodedBytesEmpty);
    246   EXPECT_TRUE(RunParser(kUrlEncoded, input, &output));
    247   EXPECT_EQ(0u, output.size());
    248 }
    249 
    250 }  // namespace extensions
    251