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.
5 #include "net/http/http_chunked_decoder.h"
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"
16 typedef testing::Test HttpChunkedDecoderTest
;
18 void RunTest(const char* inputs
[], size_t num_inputs
,
19 const char* expected_output
,
21 int bytes_after_eof
) {
22 HttpChunkedDecoder decoder
;
23 EXPECT_FALSE(decoder
.reached_eof());
27 for (size_t i
= 0; i
< num_inputs
; ++i
) {
28 std::string input
= inputs
[i
];
29 int n
= decoder
.FilterBuf(&input
[0], static_cast<int>(input
.size()));
32 result
.append(input
.data(), n
);
35 EXPECT_EQ(expected_output
, result
);
36 EXPECT_EQ(expected_eof
, decoder
.reached_eof());
37 EXPECT_EQ(bytes_after_eof
, decoder
.bytes_after_eof());
40 // Feed the inputs to the decoder, until it returns an error.
41 void RunTestUntilFailure(const char* inputs
[],
44 HttpChunkedDecoder decoder
;
45 EXPECT_FALSE(decoder
.reached_eof());
47 for (size_t i
= 0; i
< num_inputs
; ++i
) {
48 std::string input
= inputs
[i
];
49 int n
= decoder
.FilterBuf(&input
[0], static_cast<int>(input
.size()));
51 EXPECT_EQ(ERR_INVALID_CHUNKED_ENCODING
, n
);
52 EXPECT_EQ(fail_index
, i
);
56 FAIL(); // We should have failed on the |fail_index| iteration of the loop.
59 TEST(HttpChunkedDecoderTest
, Basic
) {
60 const char* inputs
[] = {
61 "B\r\nhello hello\r\n0\r\n\r\n"
63 RunTest(inputs
, arraysize(inputs
), "hello hello", true, 0);
66 TEST(HttpChunkedDecoderTest
, OneChunk
) {
67 const char* inputs
[] = {
70 RunTest(inputs
, arraysize(inputs
), "hello", false, 0);
73 TEST(HttpChunkedDecoderTest
, Typical
) {
74 const char* inputs
[] = {
80 RunTest(inputs
, arraysize(inputs
), "hello world", true, 0);
83 TEST(HttpChunkedDecoderTest
, Incremental
) {
84 const char* inputs
[] = {
97 RunTest(inputs
, arraysize(inputs
), "hello", true, 0);
100 // Same as above, but group carriage returns with previous input.
101 TEST(HttpChunkedDecoderTest
, Incremental2
) {
102 const char* inputs
[] = {
111 RunTest(inputs
, arraysize(inputs
), "hello", true, 0);
114 TEST(HttpChunkedDecoderTest
, LF_InsteadOf_CRLF
) {
115 // Compatibility: [RFC 2616 - Invalid]
116 // {Firefox3} - Valid
117 // {IE7, Safari3.1, Opera9.51} - Invalid
118 const char* inputs
[] = {
124 RunTest(inputs
, arraysize(inputs
), "hello world", true, 0);
127 TEST(HttpChunkedDecoderTest
, Extensions
) {
128 const char* inputs
[] = {
129 "5;x=0\r\nhello\r\n",
132 RunTest(inputs
, arraysize(inputs
), "hello", true, 0);
135 TEST(HttpChunkedDecoderTest
, Trailers
) {
136 const char* inputs
[] = {
143 RunTest(inputs
, arraysize(inputs
), "hello", true, 0);
146 TEST(HttpChunkedDecoderTest
, TrailersUnfinished
) {
147 const char* inputs
[] = {
152 RunTest(inputs
, arraysize(inputs
), "hello", false, 0);
155 TEST(HttpChunkedDecoderTest
, InvalidChunkSize_TooBig
) {
156 const char* inputs
[] = {
157 // This chunked body is not terminated.
158 // However we will fail decoding because the chunk-size
159 // number is larger than we can handle.
160 "48469410265455838241\r\nhello\r\n",
163 RunTestUntilFailure(inputs
, arraysize(inputs
), 0);
166 TEST(HttpChunkedDecoderTest
, InvalidChunkSize_0X
) {
167 const char* inputs
[] = {
168 // Compatibility [RFC 2616 - Invalid]:
169 // {Safari3.1, IE7} - Invalid
170 // {Firefox3, Opera 9.51} - Valid
174 RunTestUntilFailure(inputs
, arraysize(inputs
), 0);
177 TEST(HttpChunkedDecoderTest
, ChunkSize_TrailingSpace
) {
178 const char* inputs
[] = {
179 // Compatibility [RFC 2616 - Invalid]:
180 // {IE7, Safari3.1, Firefox3, Opera 9.51} - Valid
182 // At least yahoo.com depends on this being valid.
186 RunTest(inputs
, arraysize(inputs
), "hello", true, 0);
189 TEST(HttpChunkedDecoderTest
, InvalidChunkSize_TrailingTab
) {
190 const char* inputs
[] = {
191 // Compatibility [RFC 2616 - Invalid]:
192 // {IE7, Safari3.1, Firefox3, Opera 9.51} - Valid
196 RunTestUntilFailure(inputs
, arraysize(inputs
), 0);
199 TEST(HttpChunkedDecoderTest
, InvalidChunkSize_TrailingFormFeed
) {
200 const char* inputs
[] = {
201 // Compatibility [RFC 2616- Invalid]:
202 // {Safari3.1} - Invalid
203 // {IE7, Firefox3, Opera 9.51} - Valid
207 RunTestUntilFailure(inputs
, arraysize(inputs
), 0);
210 TEST(HttpChunkedDecoderTest
, InvalidChunkSize_TrailingVerticalTab
) {
211 const char* inputs
[] = {
212 // Compatibility [RFC 2616 - Invalid]:
213 // {Safari 3.1} - Invalid
214 // {IE7, Firefox3, Opera 9.51} - Valid
218 RunTestUntilFailure(inputs
, arraysize(inputs
), 0);
221 TEST(HttpChunkedDecoderTest
, InvalidChunkSize_TrailingNonHexDigit
) {
222 const char* inputs
[] = {
223 // Compatibility [RFC 2616 - Invalid]:
224 // {Safari 3.1} - Invalid
225 // {IE7, Firefox3, Opera 9.51} - Valid
229 RunTestUntilFailure(inputs
, arraysize(inputs
), 0);
232 TEST(HttpChunkedDecoderTest
, InvalidChunkSize_LeadingSpace
) {
233 const char* inputs
[] = {
234 // Compatibility [RFC 2616 - Invalid]:
236 // {Safari 3.1, Firefox3, Opera 9.51} - Valid
240 RunTestUntilFailure(inputs
, arraysize(inputs
), 0);
243 TEST(HttpChunkedDecoderTest
, InvalidLeadingSeparator
) {
244 const char* inputs
[] = {
245 "\r\n5\r\nhello\r\n",
248 RunTestUntilFailure(inputs
, arraysize(inputs
), 0);
251 TEST(HttpChunkedDecoderTest
, InvalidChunkSize_NoSeparator
) {
252 const char* inputs
[] = {
257 RunTestUntilFailure(inputs
, arraysize(inputs
), 1);
260 TEST(HttpChunkedDecoderTest
, InvalidChunkSize_Negative
) {
261 const char* inputs
[] = {
262 "8\r\n12345678\r\n-5\r\nhello\r\n",
265 RunTestUntilFailure(inputs
, arraysize(inputs
), 0);
268 TEST(HttpChunkedDecoderTest
, InvalidChunkSize_Plus
) {
269 const char* inputs
[] = {
270 // Compatibility [RFC 2616 - Invalid]:
271 // {IE7, Safari 3.1} - Invalid
272 // {Firefox3, Opera 9.51} - Valid
276 RunTestUntilFailure(inputs
, arraysize(inputs
), 0);
279 TEST(HttpChunkedDecoderTest
, InvalidConsecutiveCRLFs
) {
280 const char* inputs
[] = {
285 RunTestUntilFailure(inputs
, arraysize(inputs
), 1);
288 TEST(HttpChunkedDecoderTest
, ExcessiveChunkLen
) {
289 const char* inputs
[] = {
290 "c0000000\r\nhello\r\n"
292 RunTestUntilFailure(inputs
, arraysize(inputs
), 0);
295 TEST(HttpChunkedDecoderTest
, BasicExtraData
) {
296 const char* inputs
[] = {
297 "5\r\nhello\r\n0\r\n\r\nextra bytes"
299 RunTest(inputs
, arraysize(inputs
), "hello", true, 11);
302 TEST(HttpChunkedDecoderTest
, IncrementalExtraData
) {
303 const char* inputs
[] = {
316 RunTest(inputs
, arraysize(inputs
), "hello", true, 11);
319 TEST(HttpChunkedDecoderTest
, MultipleExtraDataBlocks
) {
320 const char* inputs
[] = {
321 "5\r\nhello\r\n0\r\n\r\nextra",
324 RunTest(inputs
, arraysize(inputs
), "hello", true, 11);
327 // Test when the line with the chunk length is too long.
328 TEST(HttpChunkedDecoderTest
, LongChunkLengthLine
) {
329 int big_chunk_length
= HttpChunkedDecoder::kMaxLineBufLen
;
330 scoped_ptr
<char[]> big_chunk(new char[big_chunk_length
+ 1]);
331 memset(big_chunk
.get(), '0', big_chunk_length
);
332 big_chunk
[big_chunk_length
] = 0;
333 const char* inputs
[] = {
337 RunTestUntilFailure(inputs
, arraysize(inputs
), 1);
340 // Test when the extension portion of the line with the chunk length is too
342 TEST(HttpChunkedDecoderTest
, LongLengthLengthLine
) {
343 int big_chunk_length
= HttpChunkedDecoder::kMaxLineBufLen
;
344 scoped_ptr
<char[]> big_chunk(new char[big_chunk_length
+ 1]);
345 memset(big_chunk
.get(), '0', big_chunk_length
);
346 big_chunk
[big_chunk_length
] = 0;
347 const char* inputs
[] = {
351 RunTestUntilFailure(inputs
, arraysize(inputs
), 1);