-
Notifications
You must be signed in to change notification settings - Fork 9k
Expand file tree
/
Copy pathHTTPChunkedReadBuffer.cpp
More file actions
128 lines (101 loc) · 3.29 KB
/
Copy pathHTTPChunkedReadBuffer.cpp
File metadata and controls
128 lines (101 loc) · 3.29 KB
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#include <IO/HTTPChunkedReadBuffer.h>
#include <IO/ReadHelpers.h>
#include <Common/StringUtils.h>
#include <base/hex.h>
#include <base/arithmeticOverflow.h>
namespace DB
{
namespace
{
constexpr size_t chunk_footer_size = 2;
}
namespace ErrorCodes
{
extern const int ARGUMENT_OUT_OF_BOUND;
extern const int UNEXPECTED_END_OF_FILE;
extern const int BAD_REQUEST_PARAMETER;
}
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::BAD_REQUEST_PARAMETER, "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;
if (need_read_chunk_footer)
{
readChunkFooter();
need_read_chunk_footer = false;
}
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);
}
/// Avoid leaving only the current chunk footer buffered. Otherwise a readiness check would see the footer
/// and the following read could still block while waiting for the next chunk header.
if (in->available() >= chunk_footer_size)
readChunkFooter();
else
need_read_chunk_footer = true;
return true;
}
bool HTTPChunkedReadBuffer::poll(size_t timeout_microseconds)
{
if (hasPendingData())
return true;
if (!in)
return true;
if (need_read_chunk_footer)
{
if (in->available() < chunk_footer_size)
{
if (in->available() == 0 && !in->poll(timeout_microseconds))
return false;
if (in->available() < chunk_footer_size)
return false;
}
readChunkFooter();
need_read_chunk_footer = false;
}
return in->poll(timeout_microseconds);
}
}