OLD | NEW |
| (Empty) |
1 // Copyright (c) 2006-2008 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 "net/http/http_chunked_decoder.h" | |
6 | |
7 #include "base/basictypes.h" | |
8 #include "base/memory/scoped_ptr.h" | |
9 #include "net/base/net_errors.h" | |
10 #include "testing/gtest/include/gtest/gtest.h" | |
11 | |
12 namespace net { | |
13 | |
14 namespace { | |
15 | |
16 typedef testing::Test HttpChunkedDecoderTest; | |
17 | |
18 void RunTest(const char* const inputs[], | |
19 size_t num_inputs, | |
20 const char* expected_output, | |
21 bool expected_eof, | |
22 int bytes_after_eof) { | |
23 HttpChunkedDecoder decoder; | |
24 EXPECT_FALSE(decoder.reached_eof()); | |
25 | |
26 std::string result; | |
27 | |
28 for (size_t i = 0; i < num_inputs; ++i) { | |
29 std::string input = inputs[i]; | |
30 int n = decoder.FilterBuf(&input[0], static_cast<int>(input.size())); | |
31 EXPECT_GE(n, 0); | |
32 if (n > 0) | |
33 result.append(input.data(), n); | |
34 } | |
35 | |
36 EXPECT_EQ(expected_output, result); | |
37 EXPECT_EQ(expected_eof, decoder.reached_eof()); | |
38 EXPECT_EQ(bytes_after_eof, decoder.bytes_after_eof()); | |
39 } | |
40 | |
41 // Feed the inputs to the decoder, until it returns an error. | |
42 void RunTestUntilFailure(const char* const inputs[], | |
43 size_t num_inputs, | |
44 size_t fail_index) { | |
45 HttpChunkedDecoder decoder; | |
46 EXPECT_FALSE(decoder.reached_eof()); | |
47 | |
48 for (size_t i = 0; i < num_inputs; ++i) { | |
49 std::string input = inputs[i]; | |
50 int n = decoder.FilterBuf(&input[0], static_cast<int>(input.size())); | |
51 if (n < 0) { | |
52 EXPECT_EQ(ERR_INVALID_CHUNKED_ENCODING, n); | |
53 EXPECT_EQ(fail_index, i); | |
54 return; | |
55 } | |
56 } | |
57 FAIL(); // We should have failed on the |fail_index| iteration of the loop. | |
58 } | |
59 | |
60 TEST(HttpChunkedDecoderTest, Basic) { | |
61 const char* const inputs[] = { | |
62 "B\r\nhello hello\r\n0\r\n\r\n" | |
63 }; | |
64 RunTest(inputs, arraysize(inputs), "hello hello", true, 0); | |
65 } | |
66 | |
67 TEST(HttpChunkedDecoderTest, OneChunk) { | |
68 const char* const inputs[] = { | |
69 "5\r\nhello\r\n" | |
70 }; | |
71 RunTest(inputs, arraysize(inputs), "hello", false, 0); | |
72 } | |
73 | |
74 TEST(HttpChunkedDecoderTest, Typical) { | |
75 const char* const inputs[] = { | |
76 "5\r\nhello\r\n", | |
77 "1\r\n \r\n", | |
78 "5\r\nworld\r\n", | |
79 "0\r\n\r\n" | |
80 }; | |
81 RunTest(inputs, arraysize(inputs), "hello world", true, 0); | |
82 } | |
83 | |
84 TEST(HttpChunkedDecoderTest, Incremental) { | |
85 const char* const inputs[] = { | |
86 "5", | |
87 "\r", | |
88 "\n", | |
89 "hello", | |
90 "\r", | |
91 "\n", | |
92 "0", | |
93 "\r", | |
94 "\n", | |
95 "\r", | |
96 "\n" | |
97 }; | |
98 RunTest(inputs, arraysize(inputs), "hello", true, 0); | |
99 } | |
100 | |
101 // Same as above, but group carriage returns with previous input. | |
102 TEST(HttpChunkedDecoderTest, Incremental2) { | |
103 const char* const inputs[] = { | |
104 "5\r", | |
105 "\n", | |
106 "hello\r", | |
107 "\n", | |
108 "0\r", | |
109 "\n\r", | |
110 "\n" | |
111 }; | |
112 RunTest(inputs, arraysize(inputs), "hello", true, 0); | |
113 } | |
114 | |
115 TEST(HttpChunkedDecoderTest, LF_InsteadOf_CRLF) { | |
116 // Compatibility: [RFC 2616 - Invalid] | |
117 // {Firefox3} - Valid | |
118 // {IE7, Safari3.1, Opera9.51} - Invalid | |
119 const char* const inputs[] = { | |
120 "5\nhello\n", | |
121 "1\n \n", | |
122 "5\nworld\n", | |
123 "0\n\n" | |
124 }; | |
125 RunTest(inputs, arraysize(inputs), "hello world", true, 0); | |
126 } | |
127 | |
128 TEST(HttpChunkedDecoderTest, Extensions) { | |
129 const char* const inputs[] = { | |
130 "5;x=0\r\nhello\r\n", | |
131 "0;y=\"2 \"\r\n\r\n" | |
132 }; | |
133 RunTest(inputs, arraysize(inputs), "hello", true, 0); | |
134 } | |
135 | |
136 TEST(HttpChunkedDecoderTest, Trailers) { | |
137 const char* const inputs[] = { | |
138 "5\r\nhello\r\n", | |
139 "0\r\n", | |
140 "Foo: 1\r\n", | |
141 "Bar: 2\r\n", | |
142 "\r\n" | |
143 }; | |
144 RunTest(inputs, arraysize(inputs), "hello", true, 0); | |
145 } | |
146 | |
147 TEST(HttpChunkedDecoderTest, TrailersUnfinished) { | |
148 const char* const inputs[] = { | |
149 "5\r\nhello\r\n", | |
150 "0\r\n", | |
151 "Foo: 1\r\n" | |
152 }; | |
153 RunTest(inputs, arraysize(inputs), "hello", false, 0); | |
154 } | |
155 | |
156 TEST(HttpChunkedDecoderTest, InvalidChunkSize_TooBig) { | |
157 const char* const inputs[] = { | |
158 // This chunked body is not terminated. | |
159 // However we will fail decoding because the chunk-size | |
160 // number is larger than we can handle. | |
161 "48469410265455838241\r\nhello\r\n", | |
162 "0\r\n\r\n" | |
163 }; | |
164 RunTestUntilFailure(inputs, arraysize(inputs), 0); | |
165 } | |
166 | |
167 TEST(HttpChunkedDecoderTest, InvalidChunkSize_0X) { | |
168 const char* const inputs[] = { | |
169 // Compatibility [RFC 2616 - Invalid]: | |
170 // {Safari3.1, IE7} - Invalid | |
171 // {Firefox3, Opera 9.51} - Valid | |
172 "0x5\r\nhello\r\n", | |
173 "0\r\n\r\n" | |
174 }; | |
175 RunTestUntilFailure(inputs, arraysize(inputs), 0); | |
176 } | |
177 | |
178 TEST(HttpChunkedDecoderTest, ChunkSize_TrailingSpace) { | |
179 const char* const inputs[] = { | |
180 // Compatibility [RFC 2616 - Invalid]: | |
181 // {IE7, Safari3.1, Firefox3, Opera 9.51} - Valid | |
182 // | |
183 // At least yahoo.com depends on this being valid. | |
184 "5 \r\nhello\r\n", | |
185 "0\r\n\r\n" | |
186 }; | |
187 RunTest(inputs, arraysize(inputs), "hello", true, 0); | |
188 } | |
189 | |
190 TEST(HttpChunkedDecoderTest, InvalidChunkSize_TrailingTab) { | |
191 const char* const inputs[] = { | |
192 // Compatibility [RFC 2616 - Invalid]: | |
193 // {IE7, Safari3.1, Firefox3, Opera 9.51} - Valid | |
194 "5\t\r\nhello\r\n", | |
195 "0\r\n\r\n" | |
196 }; | |
197 RunTestUntilFailure(inputs, arraysize(inputs), 0); | |
198 } | |
199 | |
200 TEST(HttpChunkedDecoderTest, InvalidChunkSize_TrailingFormFeed) { | |
201 const char* const inputs[] = { | |
202 // Compatibility [RFC 2616- Invalid]: | |
203 // {Safari3.1} - Invalid | |
204 // {IE7, Firefox3, Opera 9.51} - Valid | |
205 "5\f\r\nhello\r\n", | |
206 "0\r\n\r\n" | |
207 }; | |
208 RunTestUntilFailure(inputs, arraysize(inputs), 0); | |
209 } | |
210 | |
211 TEST(HttpChunkedDecoderTest, InvalidChunkSize_TrailingVerticalTab) { | |
212 const char* const inputs[] = { | |
213 // Compatibility [RFC 2616 - Invalid]: | |
214 // {Safari 3.1} - Invalid | |
215 // {IE7, Firefox3, Opera 9.51} - Valid | |
216 "5\v\r\nhello\r\n", | |
217 "0\r\n\r\n" | |
218 }; | |
219 RunTestUntilFailure(inputs, arraysize(inputs), 0); | |
220 } | |
221 | |
222 TEST(HttpChunkedDecoderTest, InvalidChunkSize_TrailingNonHexDigit) { | |
223 const char* const inputs[] = { | |
224 // Compatibility [RFC 2616 - Invalid]: | |
225 // {Safari 3.1} - Invalid | |
226 // {IE7, Firefox3, Opera 9.51} - Valid | |
227 "5H\r\nhello\r\n", | |
228 "0\r\n\r\n" | |
229 }; | |
230 RunTestUntilFailure(inputs, arraysize(inputs), 0); | |
231 } | |
232 | |
233 TEST(HttpChunkedDecoderTest, InvalidChunkSize_LeadingSpace) { | |
234 const char* const inputs[] = { | |
235 // Compatibility [RFC 2616 - Invalid]: | |
236 // {IE7} - Invalid | |
237 // {Safari 3.1, Firefox3, Opera 9.51} - Valid | |
238 " 5\r\nhello\r\n", | |
239 "0\r\n\r\n" | |
240 }; | |
241 RunTestUntilFailure(inputs, arraysize(inputs), 0); | |
242 } | |
243 | |
244 TEST(HttpChunkedDecoderTest, InvalidLeadingSeparator) { | |
245 const char* const inputs[] = { | |
246 "\r\n5\r\nhello\r\n", | |
247 "0\r\n\r\n" | |
248 }; | |
249 RunTestUntilFailure(inputs, arraysize(inputs), 0); | |
250 } | |
251 | |
252 TEST(HttpChunkedDecoderTest, InvalidChunkSize_NoSeparator) { | |
253 const char* const inputs[] = { | |
254 "5\r\nhello", | |
255 "1\r\n \r\n", | |
256 "0\r\n\r\n" | |
257 }; | |
258 RunTestUntilFailure(inputs, arraysize(inputs), 1); | |
259 } | |
260 | |
261 TEST(HttpChunkedDecoderTest, InvalidChunkSize_Negative) { | |
262 const char* const inputs[] = { | |
263 "8\r\n12345678\r\n-5\r\nhello\r\n", | |
264 "0\r\n\r\n" | |
265 }; | |
266 RunTestUntilFailure(inputs, arraysize(inputs), 0); | |
267 } | |
268 | |
269 TEST(HttpChunkedDecoderTest, InvalidChunkSize_Plus) { | |
270 const char* const inputs[] = { | |
271 // Compatibility [RFC 2616 - Invalid]: | |
272 // {IE7, Safari 3.1} - Invalid | |
273 // {Firefox3, Opera 9.51} - Valid | |
274 "+5\r\nhello\r\n", | |
275 "0\r\n\r\n" | |
276 }; | |
277 RunTestUntilFailure(inputs, arraysize(inputs), 0); | |
278 } | |
279 | |
280 TEST(HttpChunkedDecoderTest, InvalidConsecutiveCRLFs) { | |
281 const char* const inputs[] = { | |
282 "5\r\nhello\r\n", | |
283 "\r\n\r\n\r\n\r\n", | |
284 "0\r\n\r\n" | |
285 }; | |
286 RunTestUntilFailure(inputs, arraysize(inputs), 1); | |
287 } | |
288 | |
289 TEST(HttpChunkedDecoderTest, ExcessiveChunkLen) { | |
290 const char* const inputs[] = { | |
291 "c0000000\r\nhello\r\n" | |
292 }; | |
293 RunTestUntilFailure(inputs, arraysize(inputs), 0); | |
294 } | |
295 | |
296 TEST(HttpChunkedDecoderTest, BasicExtraData) { | |
297 const char* const inputs[] = { | |
298 "5\r\nhello\r\n0\r\n\r\nextra bytes" | |
299 }; | |
300 RunTest(inputs, arraysize(inputs), "hello", true, 11); | |
301 } | |
302 | |
303 TEST(HttpChunkedDecoderTest, IncrementalExtraData) { | |
304 const char* const inputs[] = { | |
305 "5", | |
306 "\r", | |
307 "\n", | |
308 "hello", | |
309 "\r", | |
310 "\n", | |
311 "0", | |
312 "\r", | |
313 "\n", | |
314 "\r", | |
315 "\nextra bytes" | |
316 }; | |
317 RunTest(inputs, arraysize(inputs), "hello", true, 11); | |
318 } | |
319 | |
320 TEST(HttpChunkedDecoderTest, MultipleExtraDataBlocks) { | |
321 const char* const inputs[] = { | |
322 "5\r\nhello\r\n0\r\n\r\nextra", | |
323 " bytes" | |
324 }; | |
325 RunTest(inputs, arraysize(inputs), "hello", true, 11); | |
326 } | |
327 | |
328 // Test when the line with the chunk length is too long. | |
329 TEST(HttpChunkedDecoderTest, LongChunkLengthLine) { | |
330 int big_chunk_length = HttpChunkedDecoder::kMaxLineBufLen; | |
331 scoped_ptr<char[]> big_chunk(new char[big_chunk_length + 1]); | |
332 memset(big_chunk.get(), '0', big_chunk_length); | |
333 big_chunk[big_chunk_length] = 0; | |
334 const char* const inputs[] = { | |
335 big_chunk.get(), | |
336 "5" | |
337 }; | |
338 RunTestUntilFailure(inputs, arraysize(inputs), 1); | |
339 } | |
340 | |
341 // Test when the extension portion of the line with the chunk length is too | |
342 // long. | |
343 TEST(HttpChunkedDecoderTest, LongLengthLengthLine) { | |
344 int big_chunk_length = HttpChunkedDecoder::kMaxLineBufLen; | |
345 scoped_ptr<char[]> big_chunk(new char[big_chunk_length + 1]); | |
346 memset(big_chunk.get(), '0', big_chunk_length); | |
347 big_chunk[big_chunk_length] = 0; | |
348 const char* const inputs[] = { | |
349 "5;", | |
350 big_chunk.get() | |
351 }; | |
352 RunTestUntilFailure(inputs, arraysize(inputs), 1); | |
353 } | |
354 | |
355 } // namespace | |
356 | |
357 } // namespace net | |
OLD | NEW |