OLD | NEW |
| (Empty) |
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 "chrome/browser/extensions/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 | |
OLD | NEW |