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* const inputs
[],
20 const char* expected_output
,
22 int bytes_after_eof
) {
23 HttpChunkedDecoder decoder
;
24 EXPECT_FALSE(decoder
.reached_eof());
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()));
33 result
.append(input
.data(), n
);
36 EXPECT_EQ(expected_output
, result
);
37 EXPECT_EQ(expected_eof
, decoder
.reached_eof());
38 EXPECT_EQ(bytes_after_eof
, decoder
.bytes_after_eof());
41 // Feed the inputs to the decoder, until it returns an error.
42 void RunTestUntilFailure(const char* const inputs
[],
45 HttpChunkedDecoder decoder
;
46 EXPECT_FALSE(decoder
.reached_eof());
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()));
52 EXPECT_EQ(ERR_INVALID_CHUNKED_ENCODING
, n
);
53 EXPECT_EQ(fail_index
, i
);
57 FAIL(); // We should have failed on the |fail_index| iteration of the loop.
60 TEST(HttpChunkedDecoderTest
, Basic
) {
61 const char* const inputs
[] = {
62 "B\r\nhello hello\r\n0\r\n\r\n"
64 RunTest(inputs
, arraysize(inputs
), "hello hello", true, 0);
67 TEST(HttpChunkedDecoderTest
, OneChunk
) {
68 const char* const inputs
[] = {
71 RunTest(inputs
, arraysize(inputs
), "hello", false, 0);
74 TEST(HttpChunkedDecoderTest
, Typical
) {
75 const char* const inputs
[] = {
81 RunTest(inputs
, arraysize(inputs
), "hello world", true, 0);
84 TEST(HttpChunkedDecoderTest
, Incremental
) {
85 const char* const inputs
[] = {
98 RunTest(inputs
, arraysize(inputs
), "hello", true, 0);
101 // Same as above, but group carriage returns with previous input.
102 TEST(HttpChunkedDecoderTest
, Incremental2
) {
103 const char* const inputs
[] = {
112 RunTest(inputs
, arraysize(inputs
), "hello", true, 0);
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
[] = {
125 RunTest(inputs
, arraysize(inputs
), "hello world", true, 0);
128 TEST(HttpChunkedDecoderTest
, Extensions
) {
129 const char* const inputs
[] = {
130 "5;x=0\r\nhello\r\n",
133 RunTest(inputs
, arraysize(inputs
), "hello", true, 0);
136 TEST(HttpChunkedDecoderTest
, Trailers
) {
137 const char* const inputs
[] = {
144 RunTest(inputs
, arraysize(inputs
), "hello", true, 0);
147 TEST(HttpChunkedDecoderTest
, TrailersUnfinished
) {
148 const char* const inputs
[] = {
153 RunTest(inputs
, arraysize(inputs
), "hello", false, 0);
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",
164 RunTestUntilFailure(inputs
, arraysize(inputs
), 0);
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
175 RunTestUntilFailure(inputs
, arraysize(inputs
), 0);
178 TEST(HttpChunkedDecoderTest
, ChunkSize_TrailingSpace
) {
179 const char* const inputs
[] = {
180 // Compatibility [RFC 2616 - Invalid]:
181 // {IE7, Safari3.1, Firefox3, Opera 9.51} - Valid
183 // At least yahoo.com depends on this being valid.
187 RunTest(inputs
, arraysize(inputs
), "hello", true, 0);
190 TEST(HttpChunkedDecoderTest
, InvalidChunkSize_TrailingTab
) {
191 const char* const inputs
[] = {
192 // Compatibility [RFC 2616 - Invalid]:
193 // {IE7, Safari3.1, Firefox3, Opera 9.51} - Valid
197 RunTestUntilFailure(inputs
, arraysize(inputs
), 0);
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
208 RunTestUntilFailure(inputs
, arraysize(inputs
), 0);
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
219 RunTestUntilFailure(inputs
, arraysize(inputs
), 0);
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
230 RunTestUntilFailure(inputs
, arraysize(inputs
), 0);
233 TEST(HttpChunkedDecoderTest
, InvalidChunkSize_LeadingSpace
) {
234 const char* const inputs
[] = {
235 // Compatibility [RFC 2616 - Invalid]:
237 // {Safari 3.1, Firefox3, Opera 9.51} - Valid
241 RunTestUntilFailure(inputs
, arraysize(inputs
), 0);
244 TEST(HttpChunkedDecoderTest
, InvalidLeadingSeparator
) {
245 const char* const inputs
[] = {
246 "\r\n5\r\nhello\r\n",
249 RunTestUntilFailure(inputs
, arraysize(inputs
), 0);
252 TEST(HttpChunkedDecoderTest
, InvalidChunkSize_NoSeparator
) {
253 const char* const inputs
[] = {
258 RunTestUntilFailure(inputs
, arraysize(inputs
), 1);
261 TEST(HttpChunkedDecoderTest
, InvalidChunkSize_Negative
) {
262 const char* const inputs
[] = {
263 "8\r\n12345678\r\n-5\r\nhello\r\n",
266 RunTestUntilFailure(inputs
, arraysize(inputs
), 0);
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
277 RunTestUntilFailure(inputs
, arraysize(inputs
), 0);
280 TEST(HttpChunkedDecoderTest
, InvalidConsecutiveCRLFs
) {
281 const char* const inputs
[] = {
286 RunTestUntilFailure(inputs
, arraysize(inputs
), 1);
289 TEST(HttpChunkedDecoderTest
, ExcessiveChunkLen
) {
290 const char* const inputs
[] = {
291 "c0000000\r\nhello\r\n"
293 RunTestUntilFailure(inputs
, arraysize(inputs
), 0);
296 TEST(HttpChunkedDecoderTest
, BasicExtraData
) {
297 const char* const inputs
[] = {
298 "5\r\nhello\r\n0\r\n\r\nextra bytes"
300 RunTest(inputs
, arraysize(inputs
), "hello", true, 11);
303 TEST(HttpChunkedDecoderTest
, IncrementalExtraData
) {
304 const char* const inputs
[] = {
317 RunTest(inputs
, arraysize(inputs
), "hello", true, 11);
320 TEST(HttpChunkedDecoderTest
, MultipleExtraDataBlocks
) {
321 const char* const inputs
[] = {
322 "5\r\nhello\r\n0\r\n\r\nextra",
325 RunTest(inputs
, arraysize(inputs
), "hello", true, 11);
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
[] = {
338 RunTestUntilFailure(inputs
, arraysize(inputs
), 1);
341 // Test when the extension portion of the line with the chunk length is too
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
[] = {
352 RunTestUntilFailure(inputs
, arraysize(inputs
), 1);