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