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