Line data Source code
1 : #include "response_factory.hpp"
2 : #include "../common/http_response.hpp"
3 : #include "../common/syntax.hpp"
4 : #include "../../util/logger.hpp"
5 : #include <algorithm>
6 : #include <cstring>
7 : #include <utility>
8 :
9 : namespace thinger::http {
10 :
11 : namespace {
12 :
13 : // Start of every status line: HTTP-version = "HTTP/1." DIGIT, then SP (RFC 9112,
14 : // section 2.3). Only major version 1 uses this message syntax; a higher minor version
15 : // is handled as 1.1 (RFC 9110, section 2.5)
16 : constexpr std::string_view version_prefix = "HTTP/1.";
17 : constexpr size_t status_prefix_size = 9; // "HTTP/1.1 "
18 :
19 42346 : bool is_digit(char c) {
20 42346 : return c >= '0' && c <= '9';
21 : }
22 :
23 : // Whether the byte at `position` of a status line can be there, as far as the
24 : // version and the space after it are concerned
25 76860 : bool valid_status_prefix_char(size_t position, char c) {
26 76860 : if (position < version_prefix.size()) return c == version_prefix[position];
27 17002 : if (position == version_prefix.size()) return is_digit(c);
28 8498 : return position != status_prefix_size - 1 || c == ' ';
29 : }
30 :
31 : // Body bytes reserved at once for a Content-Length body (it grows past it as read)
32 : constexpr size_t max_reserve = 1024 * 1024;
33 :
34 : }
35 :
36 190983 : boost::tribool response_factory::parse_buffer(const char* data, size_t size, size_t& consumed) {
37 190983 : consumed = 0;
38 : while (true) {
39 454933 : if (state_ == state::done) return true;
40 447116 : if (state_ == state::error) return false;
41 446483 : if (consumed == size) return boost::indeterminate;
42 :
43 263950 : const char* begin = data + consumed;
44 263950 : size_t available = size - consumed;
45 263950 : switch (state_) {
46 212740 : case state::status_line:
47 : case state::header_lines:
48 212740 : consumed += read_head(begin, available);
49 212740 : break;
50 9061 : case state::body_length:
51 9061 : consumed += read_length(begin, available);
52 9061 : break;
53 42134 : case state::body_chunked:
54 42134 : consumed += read_chunked(begin, available);
55 42134 : break;
56 15 : case state::body_until_close:
57 15 : if (add_body(begin, available)) consumed += available;
58 15 : break;
59 0 : default:
60 0 : break;
61 : }
62 263950 : }
63 : }
64 :
65 212740 : size_t response_factory::read_head(const char* data, size_t size) {
66 : // Up to the end of the line (LF), if it is there
67 212740 : auto newline = static_cast<const char*>(std::memchr(data, '\n', size));
68 212740 : size_t bytes = newline ? static_cast<size_t>(newline - data) + 1 : size;
69 :
70 : // Not a response from the first bytes (e.g. another protocol): fail right away
71 : // instead of waiting for the end of a line that may never come
72 212740 : if (state_ == state::status_line) {
73 91007 : for (size_t position = line_.size(); position < status_prefix_size && position - line_.size() < bytes; ++position) {
74 76860 : if (!valid_status_prefix_char(position, data[position - line_.size()])) {
75 81 : fail(client_error::invalid_status_line);
76 81 : return 0;
77 : }
78 : }
79 : }
80 :
81 : // The head is buffered whole: bound it
82 212659 : if (bytes > max_header_section_size - header_section_size_) {
83 12 : fail(client_error::header_section_too_large);
84 12 : return 0;
85 : }
86 212647 : header_section_size_ += bytes;
87 :
88 212647 : if (!newline) {
89 177113 : line_.append(data, bytes);
90 177113 : return bytes;
91 : }
92 35534 : if (line_.empty()) {
93 33105 : process_line({data, bytes});
94 : } else {
95 2429 : line_.append(data, bytes);
96 2429 : process_line(line_);
97 : }
98 35534 : line_.clear();
99 35534 : return bytes;
100 : }
101 :
102 35534 : void response_factory::process_line(std::string_view line) {
103 : // Every line ends with CRLF: a bare LF is not a line terminator (and a bare CR, a
104 : // control character, is never valid within a line)
105 35534 : if (line.size() < 2 || line[line.size() - 2] != '\r') {
106 27 : fail(state_ == state::status_line ? client_error::invalid_status_line : client_error::invalid_header);
107 27 : return;
108 : }
109 35507 : line.remove_suffix(2);
110 :
111 35507 : if (state_ == state::status_line) {
112 8471 : process_status_line(line);
113 27036 : } else if (line.empty()) {
114 8267 : process_head();
115 : } else {
116 18769 : process_header_line(line);
117 : }
118 : }
119 :
120 8471 : void response_factory::process_status_line(std::string_view line) {
121 : // status-line = HTTP-version SP status-code SP [ reason-phrase ] (RFC 9112, section
122 : // 4), with a three-digit status code from 100 to 599 (RFC 9110, section 15). As in
123 : // llhttp, the space after the status code may be missing when there is no reason
124 16942 : if (line.size() < status_prefix_size + 3 || !line.starts_with(version_prefix) || !is_digit(line[7]) || line[8] != ' ' ||
125 16942 : !is_digit(line[9]) || !is_digit(line[10]) || !is_digit(line[11])) {
126 33 : fail(client_error::invalid_status_line);
127 93 : return;
128 : }
129 8438 : int status = (line[9] - '0') * 100 + (line[10] - '0') * 10 + (line[11] - '0');
130 8438 : std::string_view reason;
131 8438 : if (line.size() > 12) {
132 8429 : if (line[12] != ' ') {
133 24 : fail(client_error::invalid_status_line);
134 24 : return;
135 : }
136 8405 : reason = line.substr(13);
137 : }
138 8414 : if (status < 100 || status > 599 || !syntax::is_field_value(reason)) {
139 36 : fail(client_error::invalid_status_line);
140 36 : return;
141 : }
142 :
143 8378 : resp_ = std::make_shared<http_response>();
144 8378 : resp_->set_http_version_major(1);
145 8378 : resp_->set_http_version_minor(static_cast<uint8_t>(line[7] - '0'));
146 8378 : resp_->set_status(static_cast<uint16_t>(status));
147 16756 : resp_->set_reason_phrase(std::string(reason));
148 8378 : state_ = state::header_lines;
149 : }
150 :
151 18769 : void response_factory::process_header_line(std::string_view line) {
152 : // field-line = field-name ":" OWS field-value OWS (RFC 9112, section 5). A line
153 : // starting with whitespace (obsolete line folding, section 5.2) has no valid name
154 18769 : auto colon = line.find(':');
155 18769 : if (colon == std::string_view::npos || !syntax::is_token(line.substr(0, colon))) {
156 57 : fail(client_error::invalid_header);
157 81 : return;
158 : }
159 18712 : auto value = syntax::trim(line.substr(colon + 1));
160 18712 : if (!syntax::is_field_value(value)) {
161 18 : fail(client_error::invalid_header);
162 18 : return;
163 : }
164 18694 : if (++header_lines_ > max_header_lines) {
165 6 : fail(client_error::header_section_too_large);
166 6 : return;
167 : }
168 74752 : resp_->process_header(std::string(line.substr(0, colon)), std::string(value));
169 : }
170 :
171 8267 : void response_factory::process_head() {
172 : // Ambiguous framing fails whatever the status: the server is not doing what it says
173 8267 : if (!resp_->has_valid_framing()) {
174 225 : fail(client_error::invalid_framing);
175 225 : return;
176 : }
177 :
178 8042 : int status = resp_->get_status_code();
179 8042 : if (status < 200 && status != 101 && !headers_only_) {
180 : // 100 Continue, 103 Early Hints...: the final response follows
181 42 : start_response();
182 42 : return;
183 : }
184 :
185 8000 : framing_ = body_framing_of(status);
186 8000 : keep_alive_ = !switches_protocols(status) && framing_ != body_framing::until_close && resp_->keep_alive();
187 8000 : if (headers_only_) {
188 567 : state_ = state::done;
189 : } else {
190 7433 : start_body();
191 : }
192 : }
193 :
194 15654 : bool response_factory::switches_protocols(int status) const {
195 15654 : return status == 101 || (request_method_ == method::CONNECT && status >= 200 && status < 300);
196 : }
197 :
198 : // RFC 9112, section 6.3
199 8000 : response_factory::body_framing response_factory::body_framing_of(int status) const {
200 8000 : if (status < 200 || switches_protocols(status) || request_method_ == method::HEAD || status == 204 || status == 304) {
201 477 : return body_framing::none;
202 : }
203 7523 : if (resp_->is_chunked_transfer()) return body_framing::chunked;
204 7297 : if (resp_->has_content_length_header()) return body_framing::content_length;
205 42 : return body_framing::until_close;
206 : }
207 :
208 7433 : void response_factory::start_body() {
209 7433 : switch (framing_) {
210 399 : case body_framing::none:
211 399 : state_ = state::done;
212 399 : break;
213 6853 : case body_framing::content_length:
214 6853 : remaining_ = resp_->get_content_length();
215 6853 : if (remaining_ == 0) {
216 396 : state_ = state::done;
217 6457 : } else if (!on_streaming_ && remaining_ > max_content_size_) {
218 3 : LOG_ERROR("the response size exceeds the maximum allowed size: {} ({} max)", remaining_, max_content_size_);
219 3 : fail(client_error::body_too_large);
220 : } else {
221 6454 : if (!on_streaming_) resp_->get_content().reserve(std::min(remaining_, max_reserve));
222 6454 : state_ = state::body_length;
223 : }
224 6853 : break;
225 163 : case body_framing::chunked:
226 163 : decoder_ = {};
227 163 : if (on_chunked_ && !on_streaming_) on_chunked_(0, std::string());
228 163 : state_ = state::body_chunked;
229 163 : break;
230 18 : case body_framing::until_close:
231 18 : state_ = state::body_until_close;
232 18 : break;
233 : }
234 7433 : }
235 :
236 9061 : size_t response_factory::read_length(const char* data, size_t size) {
237 9061 : size_t bytes = std::min(size, remaining_);
238 9061 : if (!add_body(data, bytes)) return 0;
239 9046 : remaining_ -= bytes;
240 9046 : if (remaining_ == 0) state_ = state::done;
241 9046 : return bytes;
242 : }
243 :
244 42134 : size_t response_factory::read_chunked(const char* data, size_t size) {
245 : // Chunk data is taken straight from the input, the framing goes through the decoder
246 42134 : if (size_t pending = decoder_.data_pending()) {
247 20867 : size_t bytes = std::min(size, pending);
248 41722 : if (!add_body(data, bytes)) return 0;
249 20855 : decoder_.data_consumed(bytes);
250 20855 : if (on_chunked_ && !on_streaming_ && decoder_.data_pending() == 0) {
251 24 : on_chunked_(1, resp_->get_content());
252 24 : resp_->get_content().clear();
253 : }
254 20855 : return bytes;
255 : }
256 :
257 21267 : size_t produced = 0;
258 21267 : size_t bytes = decoder_.decode(reinterpret_cast<const uint8_t*>(data), size, nullptr, 0, produced);
259 21267 : if (decoder_.failed()) {
260 81 : fail(client_error::malformed_body);
261 21186 : } else if (decoder_.done()) {
262 58 : if (on_chunked_ && !on_streaming_) on_chunked_(2, std::string());
263 58 : state_ = state::done;
264 : }
265 21267 : return bytes;
266 : }
267 :
268 29943 : bool response_factory::add_body(const char* data, size_t size) {
269 29943 : if (on_streaming_) {
270 20852 : streaming_downloaded_ += size;
271 20852 : size_t total = framing_ == body_framing::content_length ? resp_->get_content_length() : 0;
272 20852 : if (!on_streaming_(std::string_view(data, size), streaming_downloaded_, total)) {
273 24 : fail(client_error::aborted);
274 24 : return false;
275 : }
276 20828 : return true;
277 : }
278 9091 : auto& content = resp_->get_content();
279 9091 : if (size > max_content_size_ - std::min(content.size(), max_content_size_)) {
280 6 : LOG_ERROR("the response size exceeds the maximum allowed size: {} max", max_content_size_);
281 6 : fail(client_error::body_too_large);
282 6 : return false;
283 : }
284 9085 : content.append(data, size);
285 9085 : return true;
286 : }
287 :
288 123 : bool response_factory::end_of_stream() {
289 123 : if (state_ == state::body_until_close) state_ = state::done;
290 123 : if (state_ == state::done) return true;
291 108 : if (state_ != state::error) fail(client_error::incomplete_response);
292 108 : return false;
293 : }
294 :
295 7679 : bool response_factory::keep_alive() const {
296 7679 : return state_ == state::done && keep_alive_;
297 : }
298 :
299 741 : void response_factory::fail(client_error error) {
300 741 : error_ = error;
301 741 : state_ = state::error;
302 741 : }
303 :
304 19064 : void response_factory::start_response() {
305 19064 : resp_.reset();
306 19064 : state_ = state::status_line;
307 19064 : framing_ = body_framing::none;
308 19064 : keep_alive_ = false;
309 19064 : line_.clear();
310 19064 : header_section_size_ = 0;
311 19064 : header_lines_ = 0;
312 19064 : remaining_ = 0;
313 19064 : }
314 :
315 7832 : std::shared_ptr<http_response> response_factory::consume_response() {
316 7832 : auto response = std::exchange(resp_, nullptr);
317 7832 : if (response) response->log("HTTP CLIENT RESPONSE", body_logging_);
318 7832 : return response;
319 0 : }
320 :
321 19022 : void response_factory::reset() {
322 19022 : start_response();
323 19022 : request_method_ = method::GET;
324 19022 : error_ = client_error::none;
325 19022 : on_chunked_ = nullptr;
326 19022 : on_streaming_ = nullptr;
327 19022 : streaming_downloaded_ = 0;
328 19022 : }
329 :
330 86 : int response_factory::get_status_code() const {
331 86 : return resp_ ? resp_->get_status_code() : 0;
332 : }
333 :
334 8409 : void response_factory::setOnChunked(const std::function<void(int, const std::string&)>& onChunked) {
335 8409 : on_chunked_ = onChunked;
336 8409 : }
337 :
338 122 : void response_factory::setOnStreaming(std::function<bool(const std::string_view&, size_t, size_t)> callback) {
339 122 : on_streaming_ = std::move(callback);
340 122 : streaming_downloaded_ = 0;
341 122 : }
342 :
343 : }
|