Home | History | Annotate | Download | only in http
      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 
     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.
     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 
     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 
     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 
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    272 TEST(HttpChunkedDecoderTest, ExcessiveChunkLen) {
    273   const char* inputs[] = {
    274     "c0000000\r\nhello\r\n"
    275   };
    276   RunTestUntilFailure(inputs, arraysize(inputs), 0);
    277 }
    278 
    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 
    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 
    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