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.
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"
11 namespace extensions
{
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
>();
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
) {
34 scoped_ptr
<FormDataParser
> parser(InitParser(content_type_header
));
37 FormDataParser::Result result
;
38 for (size_t block
= 0; block
< bytes
.size(); ++block
) {
39 if (!parser
->SetSource(*(bytes
[block
])))
41 while (parser
->GetNextNameValue(&result
)) {
42 output
->push_back(result
.name());
43 output
->push_back(result
.value());
46 return parser
->AllDataReadOK();
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
));
58 FormDataParser::Result result
;
59 for (size_t block
= 0; block
< bytes
.size(); ++block
) {
60 if (!parser
->SetSource(*(bytes
[block
])))
62 while (parser
->GetNextNameValue(&result
)) {
63 output
.push_back(result
.name());
64 output
.push_back(result
.value());
67 return !parser
->AllDataReadOK();
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
+
79 "Content-Disposition: form-data; name=\"text\"\r\n"
81 "test\rtext\nwith non-CRLF line breaks\r\n"
85 "Content-Disposition: form-data; name=\"file\"; filename=\"test\"\r\n"
86 "Content-Type: application/octet-stream\r\n"
88 const std::string kBlockStr2
=
89 std::string("\r\n--") + kBoundary
+
91 "Content-Disposition: form-data; name=\"password\"\r\n"
97 "Content-Disposition: form-data; name=\"radio\"\r\n"
103 "Content-Disposition: form-data; name=\"check\"\r\n"
109 "Content-Disposition: form-data; name=\"check\"\r\n"
115 "Content-Disposition: form-data; name=\"txtarea\"\r\n"
123 "Content-Disposition: form-data; name=\"select\"\r\n"
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
);
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
;
146 const char* kPairs
[] = {
147 "text", "test\rtext\nwith non-CRLF line breaks",
149 "password", "test password",
153 "txtarea", "Some text.\r\nOther.\r\n",
156 const std::vector
<std::string
> kExpected(kPairs
, kPairs
+ arraysize(kPairs
));
158 std::vector
<const base::StringPiece
*> input
;
159 std::vector
<std::string
> output
;
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
);
166 // Second test: multipart POST data in several lumps.
168 input
.push_back(&kMultipartBytesSplit1
);
169 input
.push_back(&kMultipartBytesSplit2
);
170 EXPECT_TRUE(RunParser(kMultipart
, input
, &output
));
171 EXPECT_EQ(kExpected
, output
);
173 // Third test: URL-encoded POST data.
175 input
.push_back(&kUrlEncodedBytes
);
176 EXPECT_TRUE(RunParser(kUrlEncoded
, input
, &output
));
177 EXPECT_EQ(kExpected
, output
);
179 // Fourth test: text/plain POST data in one lump.
181 input
.push_back(&kTextPlainBytes
);
182 // This should fail, text/plain is ambiguous and thus unparseable.
183 EXPECT_FALSE(RunParser(kTextPlain
, input
, &output
));
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
+
193 "Content-Disposition: form-data; name=\"text\"\r\n"
195 "test\rtext\nwith non-CRLF line breaks\r\n"
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. */
205 "Content-Disposition: form-data; name=\"select\"\r\n"
209 kBoundary
+ "-" /* Missing '-' at the end. */;
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("");
221 const std::string kUrlEncoded
= "application/x-www-form-urlencoded";
222 const std::string kMultipart
=
223 std::string("multipart/form-data; boundary=") + kBoundary
;
225 std::vector
<const base::StringPiece
*> input
;
227 // First test: malformed multipart POST data.
228 input
.push_back(&kMultipartBytes
);
229 EXPECT_TRUE(CheckParserFails(kMultipart
, input
));
231 // Second test: empty multipart POST data.
233 input
.push_back(&kMultipartBytesEmpty
);
234 EXPECT_TRUE(CheckParserFails(kMultipart
, input
));
236 // Third test: malformed URL-encoded POST data.
238 input
.push_back(&kUrlEncodedBytes
);
239 EXPECT_TRUE(CheckParserFails(kUrlEncoded
, input
));
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
;
245 input
.push_back(&kUrlEncodedBytesEmpty
);
246 EXPECT_TRUE(RunParser(kUrlEncoded
, input
, &output
));
247 EXPECT_EQ(0u, output
.size());
250 } // namespace extensions