MySQL 8.4.11
Source Code Documentation
connection.h
Go to the documentation of this file.
1/*
2 Copyright (c) 2024, 2026, Oracle and/or its affiliates.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License, version 2.0,
6 as published by the Free Software Foundation.
7
8 This program is designed to work with certain software (including
9 but not limited to OpenSSL) that is licensed under separate terms,
10 as designated in a particular file or component or in included license
11 documentation. The authors of MySQL hereby grant you an additional
12 permission to link the program and your derivative works with the
13 separately licensed software that they have either included with
14 the program or referenced in the documentation.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24*/
25
26#ifndef ROUTER_SRC_HTTP_SRC_HTTP_SERVER_CONNECTION_H_
27#define ROUTER_SRC_HTTP_SRC_HTTP_SERVER_CONNECTION_H_
28
29#include <charconv>
30#include <cstdint>
31#include <map>
32#include <optional>
33#include <string>
34#include <string_view>
35#include <utility>
36
38#include "http/base/headers.h"
40#include "http/server/request.h"
43#include "mysqlrouter/uri.h"
44
45namespace http {
46namespace server {
47
48template <typename Socket>
50 public:
52 using SessionId = uint32_t;
53
55 : public Parent::ConnectionStatusCallbacks {
56 public:
57 virtual uint64_t max_request_body_size() const = 0;
58 virtual uint64_t max_response_body_size() const = 0;
60 uint64_t max_request_body_size,
61 std::optional<uint64_t> content_length) = 0;
63 uint64_t max_response_body_size, uint64_t response_body_size) = 0;
64 };
65
66 public:
69 bool has_content_length{false};
71 std::optional<uint64_t> content_length;
72 };
73
76 ConnectionStatusCallbacks *connection_handler)
77 : Parent(std::move(s), allowed_method, connection_handler,
78 CNO_CONNECTION_KIND::CNO_SERVER, CNO_HTTP_VERSION::CNO_HTTP1),
80 connection_handler_{connection_handler} {}
81
82 static std::optional<uint64_t> parse_content_length(
83 std::string_view content_length_value) {
84 uint64_t content_length{};
85 const auto *begin = content_length_value.data();
86 const auto *end = begin + content_length_value.size();
87 const auto [ptr, ec] = std::from_chars(begin, end, content_length);
88 if (ec != std::errc{} || ptr != end) return std::nullopt;
89 return content_length;
90 }
91
92 static bool body_size_exceeds_limit(uint64_t max_size, uint64_t current_size,
93 size_t next_chunk_size) {
94 if (current_size > max_size) return true;
95 return next_chunk_size > (max_size - current_size);
96 }
97
98 static bool body_size_exceeds_limit(uint64_t limit, size_t body_size) {
99 return body_size_exceeds_limit(limit, 0, body_size);
100 }
101
102 static void update_body_related_header(std::string_view name,
103 std::string_view value,
106 "Transfer-Encoding")) {
107 result.has_transfer_encoding = true;
108 return;
109 }
110
112 "Content-Length")) {
113 return;
114 }
115
116 result.has_content_length = true;
117
118 const auto parsed = parse_content_length(value);
119 if (!parsed.has_value()) {
120 result.invalid_content_length = true;
121 return;
122 }
123
124 if (!result.content_length.has_value()) {
125 result.content_length = *parsed;
126 } else if (*result.content_length != *parsed) {
127 result.invalid_content_length = true;
128 }
129 }
130
131 bool send(const uint32_t *stream_id_ptr, const int status_code,
132 const std::string &method, const std::string &path,
133 const typename Parent::Headers &headers,
134 const http::base::IOBuffer &data) override {
135 const auto max_response_body_size =
137 if (body_size_exceeds_limit(max_response_body_size, data.length())) {
139 max_response_body_size, data.length());
140
141 typename Parent::Headers error_headers;
142 static const http::base::IOBuffer k_empty;
143 error_headers.add("Connection", "close");
144 error_headers.add("Content-Length", "0");
145 Parent::keep_alive_ = false;
146 return Parent::send(
149 error_headers, k_empty);
150 }
151
152 return Parent::send(stream_id_ptr, status_code, method, path, headers,
153 data);
154 }
155
156 private:
157 int on_settings() override {
158 // Server doesn't need to synchronize to settings, it receives settings as
159 // part of the request.
160 return 0;
161 }
162
163 int on_cno_message_body(const uint32_t session_id, const char *data,
164 const size_t size) override {
165 // We can blindly use session_id with the map because
166 // the map was already initialized in `on_cno_message_head` call.
167 // The 'cno' executes callbacks in following order:
168 //
169 // * on_cno_message_head
170 // * on_cno_message_body
171 // * on_cno_message_tail
172 // * on_cno_stream_end
173 auto it = sessions_.find(session_id);
174 if (it == sessions_.end()) {
175 return 1;
176 }
177 auto &session = it->second;
178 if (session.payload_limit_exceeded) {
179 return 1;
180 }
181
182 if (body_size_exceeds_limit(session.max_request_body_size,
183 session.input_body_size, size)) {
185 session.max_request_body_size, std::nullopt);
186 session.request.send_reply(base::status_code::PayloadTooLarge);
187 session.payload_limit_exceeded = true;
188 Parent::keep_alive_ = false;
189 return 1;
190 }
191
192 session.input_body_size += size;
193 session.request.get_data().input_body_.get().append(data, size);
194 return 0;
195 }
196
197 int on_cno_message_tail(const uint32_t session_id,
198 [[maybe_unused]] const cno_tail_t *tail) override {
199 auto it = sessions_.find(session_id);
200 if (it == sessions_.end() || it->second.payload_limit_exceeded) {
201 return 1;
202 }
203
204 if (request_handler_) {
205 request_handler_->route(it->second.request);
206 }
207
208 return 0;
209 }
210
211 int on_cno_stream_end(const uint32_t id) override {
212 sessions_.erase(id);
213 return 0;
214 }
215
216 int on_cno_message_head(const uint32_t session_id,
217 const cno_message_t *msg) override {
219 first_request_ = false;
220 const auto method_pos =
222
223 http::base::Headers input_headers;
224 const auto path = cno::to_string(msg->path);
225 cno::Sequence<const cno_header_t> sequence{msg->headers, msg->headers_len};
226
227 BodyHeadersValidationResult body_headers{};
228 // libcno may normalize HTTP/1 request framing headers before exposing the
229 // header list, so use parser-provided presence flags for conflict checks.
230 body_headers.has_transfer_encoding = msg->has_transfer_encoding != 0;
231 body_headers.has_content_length = msg->has_content_length != 0;
232 for (const auto &header : sequence) {
233 auto header_name = cno::to_string(header.name);
234 auto header_value = cno::to_string(header.value);
235 update_body_related_header(header_name, header_value, body_headers);
236 input_headers.add(std::move(header_name), std::move(header_value));
237 }
238 if (body_headers.has_transfer_encoding && body_headers.has_content_length) {
239 connection_handler_->log_invalid_request_body_headers_rejection(
240 "both Content-Length and Transfer-Encoding are present");
241 ServerRequest(this, session_id, (base::method::key_type)(1 << method_pos),
242 path, std::move(input_headers))
244 Parent::keep_alive_ = false;
245 return 1;
246 }
247
248 if (body_headers.invalid_content_length) {
249 connection_handler_->log_invalid_request_body_headers_rejection(
250 "invalid Content-Length");
251 ServerRequest(this, session_id, (base::method::key_type)(1 << method_pos),
252 path, std::move(input_headers))
254 Parent::keep_alive_ = false;
255 return 1;
256 }
257
258 const auto max_request_body_size =
260 if (body_headers.content_length.has_value() &&
261 *body_headers.content_length > max_request_body_size) {
263 max_request_body_size, body_headers.content_length);
264 ServerRequest(this, session_id, (base::method::key_type)(1 << method_pos),
265 path, std::move(input_headers))
267 Parent::keep_alive_ = false;
268 return 1;
269 }
270
271 if (!(*Parent::allowed_method_)[method_pos]) {
272 ServerRequest(this, session_id, (base::method::key_type)(1 << method_pos),
273 "", std::move(input_headers))
275 return 1;
276 }
277
278 sessions_.erase(session_id);
279 try {
280 auto pair = sessions_.try_emplace(
281 session_id, this, session_id,
282 (base::method::key_type)(1 << method_pos), path,
283 std::move(input_headers), max_request_body_size);
284
285 char buffer[90];
287 sizeof(buffer));
288 pair.first->second.request.get_output_headers().add("Date", buffer);
289 pair.first->second.request.get_output_headers().add(
290 "Content-Type", "text/html; charset=ISO-8859-1");
291
292 } catch (...) {
293 ServerRequest(this, session_id, (base::method::key_type)(1 << method_pos),
294 "", std::move(input_headers))
296 return 1;
297 }
298
299 return 0;
300 }
301
302 bool first_request_{true};
303 struct SessionData {
305 const uint32_t session_id, const base::method::key_type method,
306 const std::string &path, http::base::Headers &&headers,
307 uint64_t max_body_size)
308 : request(connection, session_id, method, path, std::move(headers)),
309 max_request_body_size(max_body_size) {}
310
313 uint64_t input_body_size{0};
315 };
316 std::map<SessionId, SessionData> sessions_;
319};
320
321} // namespace server
322} // namespace http
323
324#endif // ROUTER_SRC_HTTP_SRC_HTTP_SERVER_CONNECTION_H_
Definition: connection_interface.h:41
Definition: connection.h:106
headers of a HTTP response/request.
Definition: headers.h:43
virtual void add(const std::string_view &key, std::string &&value)
Definition: headers.cc:48
Definition: io_buffer.h:41
virtual size_t length() const
Definition: io_buffer.h:52
Definition: buffer_sequence.h:39
Definition: request_handler_interface.h:34
virtual void route(http::base::Request &request)=0
virtual void log_max_response_body_size_rejection(uint64_t max_response_body_size, uint64_t response_body_size)=0
virtual void log_max_request_body_size_rejection(uint64_t max_request_body_size, std::optional< uint64_t > content_length)=0
Definition: connection.h:49
int on_cno_message_head(const uint32_t session_id, const cno_message_t *msg) override
Definition: connection.h:216
static bool body_size_exceeds_limit(uint64_t max_size, uint64_t current_size, size_t next_chunk_size)
Definition: connection.h:92
int on_cno_message_body(const uint32_t session_id, const char *data, const size_t size) override
Definition: connection.h:163
ConnectionStatusCallbacks * connection_handler_
Definition: connection.h:318
static bool body_size_exceeds_limit(uint64_t limit, size_t body_size)
Definition: connection.h:98
bool send(const uint32_t *stream_id_ptr, const int status_code, const std::string &method, const std::string &path, const typename Parent::Headers &headers, const http::base::IOBuffer &data) override
Definition: connection.h:131
ServerConnection(Socket s, base::method::Bitset *allowed_method, RequestHandlerInterface *rhi, ConnectionStatusCallbacks *connection_handler)
Definition: connection.h:74
RequestHandlerInterface * request_handler_
Definition: connection.h:317
bool first_request_
Definition: connection.h:302
uint32_t SessionId
Definition: connection.h:52
int on_cno_stream_end(const uint32_t id) override
Definition: connection.h:211
static void update_body_related_header(std::string_view name, std::string_view value, BodyHeadersValidationResult &result)
Definition: connection.h:102
int on_cno_message_tail(const uint32_t session_id, const cno_tail_t *tail) override
Definition: connection.h:197
static std::optional< uint64_t > parse_content_length(std::string_view content_length_value)
Definition: connection.h:82
int on_settings() override
Definition: connection.h:157
std::map< SessionId, SessionData > sessions_
Definition: connection.h:316
a HTTP request and response.
Definition: request.h:46
void send_reply(StatusType status_code) override
Definition: request.cc:99
void send_error(StatusType status_code) override
Definition: request.cc:76
Definition: socket.h:1090
#define HTTP_SERVER_LIB_EXPORT
Definition: http_server_lib_export.h:15
mysql_service_status_t send(const char *tag, const unsigned char *data, const size_t data_length) noexcept
Definition: message_service.cc:33
static char * path
Definition: mysqldump.cc:149
std::bitset< Pos::_LAST+1 > Bitset
Definition: method.h:57
HTTP_COMMON_EXPORT pos_type from_string_to_post(const std::string_view &method)
Definition: method.cc:50
int key_type
Definition: method.h:38
constexpr key_type PayloadTooLarge
Definition: status_code.h:77
constexpr key_type InternalError
Definition: status_code.h:93
constexpr key_type NotImplemented
Definition: status_code.h:94
constexpr key_type BadRequest
Definition: status_code.h:64
HTTP_COMMON_EXPORT name_type to_string(key_type key)
Definition: status_code.cc:35
HTTP_COMMON_EXPORT int time_to_rfc5322_fixdate(time_t ts, char *date_buf, size_t date_buf_len)
convert time_t into a Date: header value.
Definition: http_time.cc:39
HTTP_COMMON_EXPORT bool compare_case_insensitive(const std::string &l, const std::string_view &r)
Definition: headers.cc:37
std::string to_string(const T &str)
Convert CNO buffers to strings.
Definition: string.h:42
HTTP_SERVER_LIB_EXPORT std::atomic< uint64_t > http_connections_reused
Definition: server.cc:45
Definition: connection.h:59
const char * begin(const char *const c)
Definition: base64.h:44
size_t size(const char *const c)
Definition: base64.h:46
mutable_buffer buffer(void *p, size_t n) noexcept
Definition: buffer.h:418
Definition: gcs_xcom_synode.h:64
case opt name
Definition: sslopt-case.h:29
std::optional< uint64_t > content_length
Definition: connection.h:71
SessionData(http::base::ConnectionInterface *connection, const uint32_t session_id, const base::method::key_type method, const std::string &path, http::base::Headers &&headers, uint64_t max_body_size)
Definition: connection.h:304
uint64_t max_request_body_size
Definition: connection.h:312
bool payload_limit_exceeded
Definition: connection.h:314
ServerRequest request
Definition: connection.h:311
uint64_t input_body_size
Definition: connection.h:313
Definition: result.h:30
Definition: server_struct.h:39
static stdx::expected< T, std::error_code > from_chars(const std::string &value, int base=10)
convert a numeric string to a number.
Definition: tcp_address.cc:61
long long sequence(UDF_INIT *initid, UDF_ARGS *args, unsigned char *, unsigned char *)
Definition: udf_example.cc:568