MySQL 26.10.0
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 // The rejected request target may be malformed, so do not parse it just
239 // to send an error response.
240 if (body_headers.has_transfer_encoding && body_headers.has_content_length) {
241 connection_handler_->log_invalid_request_body_headers_rejection(
242 "both Content-Length and Transfer-Encoding are present");
243 ServerRequest(this, session_id, (base::method::key_type)(1 << method_pos),
244 "", std::move(input_headers))
246 Parent::keep_alive_ = false;
247 return 1;
248 }
249
250 if (body_headers.invalid_content_length) {
251 connection_handler_->log_invalid_request_body_headers_rejection(
252 "invalid Content-Length");
253 ServerRequest(this, session_id, (base::method::key_type)(1 << method_pos),
254 "", std::move(input_headers))
256 Parent::keep_alive_ = false;
257 return 1;
258 }
259
260 const auto max_request_body_size =
262 if (body_headers.content_length.has_value() &&
263 *body_headers.content_length > max_request_body_size) {
265 max_request_body_size, body_headers.content_length);
266 ServerRequest(this, session_id, (base::method::key_type)(1 << method_pos),
267 "", std::move(input_headers))
269 Parent::keep_alive_ = false;
270 return 1;
271 }
272
273 if (!(*Parent::allowed_method_)[method_pos]) {
274 ServerRequest(this, session_id, (base::method::key_type)(1 << method_pos),
275 "", std::move(input_headers))
277 return 1;
278 }
279
280 sessions_.erase(session_id);
281 try {
282 auto pair = sessions_.try_emplace(
283 session_id, this, session_id,
284 (base::method::key_type)(1 << method_pos), path,
285 std::move(input_headers), max_request_body_size);
286
287 char buffer[90];
289 sizeof(buffer));
290 pair.first->second.request.get_output_headers().add("Date", buffer);
291 pair.first->second.request.get_output_headers().add(
292 "Content-Type", "text/html; charset=ISO-8859-1");
293
294 } catch (...) {
295 ServerRequest(this, session_id, (base::method::key_type)(1 << method_pos),
296 "", std::move(input_headers))
298 return 1;
299 }
300
301 return 0;
302 }
303
304 bool first_request_{true};
305 struct SessionData {
307 const uint32_t session_id, const base::method::key_type method,
308 const std::string &path, http::base::Headers &&headers,
309 uint64_t max_body_size)
310 : request(connection, session_id, method, path, std::move(headers)),
311 max_request_body_size(max_body_size) {}
312
315 uint64_t input_body_size{0};
317 };
318 std::map<SessionId, SessionData> sessions_;
321};
322
323} // namespace server
324} // namespace http
325
326#endif // ROUTER_SRC_HTTP_SRC_HTTP_SERVER_CONNECTION_H_
Definition: connection_interface.h:41
Definition: connection.h:105
headers of a HTTP response/request.
Definition: headers.h:43
virtual void add(const std::string_view &key, std::string &&value)
Definition: headers.cc:47
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:36
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:320
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:319
bool first_request_
Definition: connection.h:304
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:318
a HTTP request and response.
Definition: request.h:46
void send_reply(StatusType status_code) override
Definition: request.cc:98
void send_error(StatusType status_code) override
Definition: request.cc:75
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:153
stdx::expected< T, std::error_code > from_chars(const std::string &value, int base=10)
convert a numeric string to a number.
Definition: destination.cc:61
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:48
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:33
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:38
HTTP_COMMON_EXPORT bool compare_case_insensitive(const std::string &l, const std::string_view &r)
Definition: headers.cc:36
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:58
ValueType value(const std::optional< ValueType > &v)
Definition: gtid.h:83
constexpr const char k_empty[]
Definition: mrs_component_embedded_mysql_result.cc:50
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
Define std::hash<Gtid>.
Definition: gtid.h:355
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:306
uint64_t max_request_body_size
Definition: connection.h:314
bool payload_limit_exceeded
Definition: connection.h:316
ServerRequest request
Definition: connection.h:313
uint64_t input_body_size
Definition: connection.h:315
Definition: result.h:30
Definition: server_struct.h:39
long long sequence(UDF_INIT *initid, UDF_ARGS *args, unsigned char *, unsigned char *)
Definition: udf_example.cc:568