Allow only one bookmark to be added for multiple fast starring
[chromium-blink-merge.git] / net / http / http_chunked_decoder_unittest.cc
blob06dd353ca689957f32969bb7ae9287426dfda52e
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"
12 namespace net {
14 namespace {
16 typedef testing::Test HttpChunkedDecoderTest;
18 void RunTest(const char* const inputs[],
19 size_t num_inputs,
20 const char* expected_output,
21 bool expected_eof,
22 int bytes_after_eof) {
23 HttpChunkedDecoder decoder;
24 EXPECT_FALSE(decoder.reached_eof());
26 std::string result;
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()));
31 EXPECT_GE(n, 0);
32 if (n > 0)
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[],
43 size_t num_inputs,
44 size_t fail_index) {
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()));
51 if (n < 0) {
52 EXPECT_EQ(ERR_INVALID_CHUNKED_ENCODING, n);
53 EXPECT_EQ(fail_index, i);
54 return;
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[] = {
69 "5\r\nhello\r\n"
71 RunTest(inputs, arraysize(inputs), "hello", false, 0);
74 TEST(HttpChunkedDecoderTest, Typical) {
75 const char* const inputs[] = {
76 "5\r\nhello\r\n",
77 "1\r\n \r\n",
78 "5\r\nworld\r\n",
79 "0\r\n\r\n"
81 RunTest(inputs, arraysize(inputs), "hello world", true, 0);
84 TEST(HttpChunkedDecoderTest, Incremental) {
85 const char* const inputs[] = {
86 "5",
87 "\r",
88 "\n",
89 "hello",
90 "\r",
91 "\n",
92 "0",
93 "\r",
94 "\n",
95 "\r",
96 "\n"
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[] = {
104 "5\r",
105 "\n",
106 "hello\r",
107 "\n",
108 "0\r",
109 "\n\r",
110 "\n"
112 RunTest(inputs, arraysize(inputs), "hello", true, 0);
115 TEST(HttpChunkedDecoderTest, LF_InsteadOf_CRLF) {
116 // Compatibility: [RFC 7230 - Invalid]
117 // {Firefox3} - Valid
118 // {IE7, Safari3.1, Opera9.51} - Invalid
119 const char* const inputs[] = {
120 "5\nhello\n",
121 "1\n \n",
122 "5\nworld\n",
123 "0\n\n"
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",
131 "0;y=\"2 \"\r\n\r\n"
133 RunTest(inputs, arraysize(inputs), "hello", true, 0);
136 TEST(HttpChunkedDecoderTest, Trailers) {
137 const char* const inputs[] = {
138 "5\r\nhello\r\n",
139 "0\r\n",
140 "Foo: 1\r\n",
141 "Bar: 2\r\n",
142 "\r\n"
144 RunTest(inputs, arraysize(inputs), "hello", true, 0);
147 TEST(HttpChunkedDecoderTest, TrailersUnfinished) {
148 const char* const inputs[] = {
149 "5\r\nhello\r\n",
150 "0\r\n",
151 "Foo: 1\r\n"
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",
162 "0\r\n\r\n"
164 RunTestUntilFailure(inputs, arraysize(inputs), 0);
167 TEST(HttpChunkedDecoderTest, InvalidChunkSize_0X) {
168 const char* const inputs[] = {
169 // Compatibility [RFC 7230 - Invalid]:
170 // {Safari3.1, IE7} - Invalid
171 // {Firefox3, Opera 9.51} - Valid
172 "0x5\r\nhello\r\n",
173 "0\r\n\r\n"
175 RunTestUntilFailure(inputs, arraysize(inputs), 0);
178 TEST(HttpChunkedDecoderTest, ChunkSize_TrailingSpace) {
179 const char* const inputs[] = {
180 // Compatibility [RFC 7230 - Invalid]:
181 // {IE7, Safari3.1, Firefox3, Opera 9.51} - Valid
183 // At least yahoo.com depends on this being valid.
184 "5 \r\nhello\r\n",
185 "0\r\n\r\n"
187 RunTest(inputs, arraysize(inputs), "hello", true, 0);
190 TEST(HttpChunkedDecoderTest, InvalidChunkSize_TrailingTab) {
191 const char* const inputs[] = {
192 // Compatibility [RFC 7230 - Invalid]:
193 // {IE7, Safari3.1, Firefox3, Opera 9.51} - Valid
194 "5\t\r\nhello\r\n",
195 "0\r\n\r\n"
197 RunTestUntilFailure(inputs, arraysize(inputs), 0);
200 TEST(HttpChunkedDecoderTest, InvalidChunkSize_TrailingFormFeed) {
201 const char* const inputs[] = {
202 // Compatibility [RFC 7230- Invalid]:
203 // {Safari3.1} - Invalid
204 // {IE7, Firefox3, Opera 9.51} - Valid
205 "5\f\r\nhello\r\n",
206 "0\r\n\r\n"
208 RunTestUntilFailure(inputs, arraysize(inputs), 0);
211 TEST(HttpChunkedDecoderTest, InvalidChunkSize_TrailingVerticalTab) {
212 const char* const inputs[] = {
213 // Compatibility [RFC 7230 - Invalid]:
214 // {Safari 3.1} - Invalid
215 // {IE7, Firefox3, Opera 9.51} - Valid
216 "5\v\r\nhello\r\n",
217 "0\r\n\r\n"
219 RunTestUntilFailure(inputs, arraysize(inputs), 0);
222 TEST(HttpChunkedDecoderTest, InvalidChunkSize_TrailingNonHexDigit) {
223 const char* const inputs[] = {
224 // Compatibility [RFC 7230 - Invalid]:
225 // {Safari 3.1} - Invalid
226 // {IE7, Firefox3, Opera 9.51} - Valid
227 "5H\r\nhello\r\n",
228 "0\r\n\r\n"
230 RunTestUntilFailure(inputs, arraysize(inputs), 0);
233 TEST(HttpChunkedDecoderTest, InvalidChunkSize_LeadingSpace) {
234 const char* const inputs[] = {
235 // Compatibility [RFC 7230 - Invalid]:
236 // {IE7} - Invalid
237 // {Safari 3.1, Firefox3, Opera 9.51} - Valid
238 " 5\r\nhello\r\n",
239 "0\r\n\r\n"
241 RunTestUntilFailure(inputs, arraysize(inputs), 0);
244 TEST(HttpChunkedDecoderTest, InvalidLeadingSeparator) {
245 const char* const inputs[] = {
246 "\r\n5\r\nhello\r\n",
247 "0\r\n\r\n"
249 RunTestUntilFailure(inputs, arraysize(inputs), 0);
252 TEST(HttpChunkedDecoderTest, InvalidChunkSize_NoSeparator) {
253 const char* const inputs[] = {
254 "5\r\nhello",
255 "1\r\n \r\n",
256 "0\r\n\r\n"
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",
264 "0\r\n\r\n"
266 RunTestUntilFailure(inputs, arraysize(inputs), 0);
269 TEST(HttpChunkedDecoderTest, InvalidChunkSize_Plus) {
270 const char* const inputs[] = {
271 // Compatibility [RFC 7230 - Invalid]:
272 // {IE7, Safari 3.1} - Invalid
273 // {Firefox3, Opera 9.51} - Valid
274 "+5\r\nhello\r\n",
275 "0\r\n\r\n"
277 RunTestUntilFailure(inputs, arraysize(inputs), 0);
280 TEST(HttpChunkedDecoderTest, InvalidConsecutiveCRLFs) {
281 const char* const inputs[] = {
282 "5\r\nhello\r\n",
283 "\r\n\r\n\r\n\r\n",
284 "0\r\n\r\n"
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[] = {
305 "5",
306 "\r",
307 "\n",
308 "hello",
309 "\r",
310 "\n",
311 "0",
312 "\r",
313 "\n",
314 "\r",
315 "\nextra bytes"
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",
323 " bytes"
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[] = {
335 big_chunk.get(),
338 RunTestUntilFailure(inputs, arraysize(inputs), 1);
341 // Test when the extension portion of the line with the chunk length is too
342 // long.
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[] = {
349 "5;",
350 big_chunk.get()
352 RunTestUntilFailure(inputs, arraysize(inputs), 1);
355 } // namespace
357 } // namespace net