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);