aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/clickhouse/src/IO/HTTPChunkedReadBuffer.cpp
blob: 29034b35e16e994fba734fb846f51ae6bbf144b1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include <IO/HTTPChunkedReadBuffer.h>

#include <IO/ReadHelpers.h>
#include <Common/StringUtils/StringUtils.h>
#include <base/hex.h>
#include <base/arithmeticOverflow.h>


namespace DB
{

namespace ErrorCodes
{
    extern const int ARGUMENT_OUT_OF_BOUND;
    extern const int UNEXPECTED_END_OF_FILE;
    extern const int CORRUPTED_DATA;
}

size_t HTTPChunkedReadBuffer::readChunkHeader()
{
    if (in->eof())
        throw Exception(ErrorCodes::UNEXPECTED_END_OF_FILE, "Unexpected end of file while reading chunk header of HTTP chunked data");

    if (!isHexDigit(*in->position()))
        throw Exception(ErrorCodes::CORRUPTED_DATA, "Unexpected data instead of HTTP chunk header");

    size_t res = 0;
    do
    {
        if (common::mulOverflow(res, 16ul, res) || common::addOverflow<size_t>(res, unhex(*in->position()), res))
            throw Exception(ErrorCodes::ARGUMENT_OUT_OF_BOUND, "Chunk size is out of bounds");
        ++in->position();
    } while (!in->eof() && isHexDigit(*in->position()));

    if (res > max_chunk_size)
        throw Exception(ErrorCodes::ARGUMENT_OUT_OF_BOUND, "Chunk size exceeded the limit (max size: {})", max_chunk_size);

    /// NOTE: If we want to read any chunk extensions, it should be done here.

    skipToCarriageReturnOrEOF(*in);

    if (in->eof())
        throw Exception(ErrorCodes::UNEXPECTED_END_OF_FILE, "Unexpected end of file while reading chunk header of HTTP chunked data");

    assertString("\n", *in);
    return res;
}

void HTTPChunkedReadBuffer::readChunkFooter()
{
    assertString("\r\n", *in);
}

bool HTTPChunkedReadBuffer::nextImpl()
{
    if (!in)
        return false;

    /// The footer of previous chunk.
    if (count())
        readChunkFooter();

    size_t chunk_size = readChunkHeader();
    if (0 == chunk_size)
    {
        readChunkFooter();
        in.reset();  // prevent double-eof situation.
        return false;
    }

    if (in->available() >= chunk_size)
    {
        /// Zero-copy read from input.
        working_buffer = Buffer(in->position(), in->position() + chunk_size);
        in->position() += chunk_size;
    }
    else
    {
        /// Chunk is not completely in buffer, copy it to scratch space.
        memory.resize(chunk_size);
        in->readStrict(memory.data(), chunk_size);
        working_buffer = Buffer(memory.data(), memory.data() + chunk_size);
    }

    /// NOTE: We postpone reading the footer to the next iteration, because it may not be completely in buffer,
    ///       but we need to keep the current data in buffer available.

    return true;
}

}