blob: 2050afd5875774699306640cb20a3dfc979aee0d [file] [log] [blame]
Ed Tanous7045c8d2017-04-03 10:04:37 -07001#pragma once
Ed Tanous75312982021-02-11 14:26:02 -08002#include "bmcweb_config.h"
Adriana Kobylak0e1cf262019-12-05 13:57:57 -06003
Ed Tanousd093c992023-01-19 19:01:49 -08004#include "async_resp.hpp"
Nan Zhoud055a342022-05-25 01:15:34 +00005#include "authentication.hpp"
Ed Tanoused5f8952023-06-22 14:06:22 -07006#include "complete_response_fields.hpp"
Ed Tanousfca2cbe2021-01-28 14:49:59 -08007#include "http2_connection.hpp"
Ed Tanousb2896142024-01-31 15:25:47 -08008#include "http_body.hpp"
Ed Tanous04e438c2020-10-03 08:06:26 -07009#include "http_response.hpp"
Ed Tanous1abe55e2018-09-05 08:30:59 -070010#include "http_utility.hpp"
Ed Tanous04e438c2020-10-03 08:06:26 -070011#include "logging.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080012#include "mutual_tls.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080013#include "ssl_key_handler.hpp"
Ed Tanous18f8f602023-07-18 10:07:23 -070014#include "str_utility.hpp"
Ed Tanous04e438c2020-10-03 08:06:26 -070015#include "utility.hpp"
Ed Tanous1abe55e2018-09-05 08:30:59 -070016
Ed Tanous8f626352018-12-19 14:51:54 -080017#include <boost/asio/io_context.hpp>
Ed Tanous3112a142018-11-29 15:45:10 -080018#include <boost/asio/ip/tcp.hpp>
Ed Tanousd43cd0c2020-09-30 20:46:53 -070019#include <boost/asio/ssl/stream.hpp>
Ed Tanous5dfb5b22021-12-03 11:24:53 -080020#include <boost/asio/steady_timer.hpp>
Ed Tanous4fa45df2023-09-01 14:20:50 -070021#include <boost/beast/_experimental/test/stream.hpp>
Ed Tanous4d698612024-02-06 14:57:24 -080022#include <boost/beast/core/buffers_generator.hpp>
Ed Tanous3112a142018-11-29 15:45:10 -080023#include <boost/beast/core/flat_static_buffer.hpp>
Myung Baea4326fe2023-01-10 14:29:24 -060024#include <boost/beast/http/error.hpp>
Ed Tanous4d698612024-02-06 14:57:24 -080025#include <boost/beast/http/message_generator.hpp>
Ed Tanous918ef252022-05-25 10:40:41 -070026#include <boost/beast/http/parser.hpp>
27#include <boost/beast/http/read.hpp>
Ed Tanous918ef252022-05-25 10:40:41 -070028#include <boost/beast/http/write.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050029#include <boost/beast/websocket.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050030
Manojkiran Eda44250442020-06-16 12:51:38 +053031#include <atomic>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050032#include <chrono>
Jonathan Doman102a4cd2024-04-15 16:56:23 -070033#include <memory>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050034#include <vector>
35
Ed Tanous1abe55e2018-09-05 08:30:59 -070036namespace crow
37{
Ed Tanous257f5792018-03-17 14:40:09 -070038
Ed Tanouscf9e4172022-12-21 09:30:16 -080039// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Ed Tanous6fbdbca2021-12-06 14:36:06 -080040static int connectionCount = 0;
Jennifer Leeacb7cfb2018-06-07 16:08:15 -070041
Ed Tanous25b54db2024-04-17 15:40:31 -070042// request body limit size set by the BMCWEB_HTTP_BODY_LIMIT option
43constexpr uint64_t httpReqBodyLimit = 1024UL * 1024UL * BMCWEB_HTTP_BODY_LIMIT;
Jennifer Leeacb7cfb2018-06-07 16:08:15 -070044
Ed Tanous1d1d7782024-04-09 12:54:08 -070045constexpr uint64_t loggedOutPostBodyLimit = 4096U;
James Feist3909dc82020-04-03 10:58:55 -070046
Ed Tanous1d1d7782024-04-09 12:54:08 -070047constexpr uint32_t httpHeaderLimit = 8192U;
James Feist3909dc82020-04-03 10:58:55 -070048
Ed Tanous4fa45df2023-09-01 14:20:50 -070049template <typename>
50struct IsTls : std::false_type
51{};
52
53template <typename T>
Ed Tanous003301a2024-04-16 09:59:19 -070054struct IsTls<boost::asio::ssl::stream<T>> : std::true_type
Ed Tanous4fa45df2023-09-01 14:20:50 -070055{};
56
Ed Tanous52cc1122020-07-18 13:51:21 -070057template <typename Adaptor, typename Handler>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050058class Connection :
Ed Tanous52cc1122020-07-18 13:51:21 -070059 public std::enable_shared_from_this<Connection<Adaptor, Handler>>
Ed Tanous1abe55e2018-09-05 08:30:59 -070060{
Ed Tanous7c8e0642022-02-21 12:11:14 -080061 using self_type = Connection<Adaptor, Handler>;
62
Ed Tanous1abe55e2018-09-05 08:30:59 -070063 public:
Ed Tanous5dfb5b22021-12-03 11:24:53 -080064 Connection(Handler* handlerIn, boost::asio::steady_timer&& timerIn,
Ed Tanous81ce6092020-12-17 16:54:55 +000065 std::function<std::string()>& getCachedDateStrF,
Ed Tanous5dfb5b22021-12-03 11:24:53 -080066 Adaptor adaptorIn) :
Ed Tanousceac6f72018-12-02 11:58:47 -080067 adaptor(std::move(adaptorIn)),
Ed Tanous5dfb5b22021-12-03 11:24:53 -080068 handler(handlerIn), timer(std::move(timerIn)),
69 getCachedDateStr(getCachedDateStrF)
Ed Tanous1abe55e2018-09-05 08:30:59 -070070 {
Ed Tanous1d1d7782024-04-09 12:54:08 -070071 initParser();
Kowalski, Kamil55e43f62019-07-10 13:12:57 +020072
Ed Tanous25b54db2024-04-17 15:40:31 -070073 if constexpr (BMCWEB_MUTUAL_TLS_AUTH)
74 {
75 prepareMutualTls();
76 }
Ed Tanous40aa0582021-07-14 13:24:40 -070077
Ed Tanous40aa0582021-07-14 13:24:40 -070078 connectionCount++;
Ed Tanous6fbdbca2021-12-06 14:36:06 -080079
Ed Tanous1d1d7782024-04-09 12:54:08 -070080 BMCWEB_LOG_DEBUG("{} Connection created, total {}", logPtr(this),
Ed Tanous62598e32023-07-17 17:06:25 -070081 connectionCount);
Ed Tanous40aa0582021-07-14 13:24:40 -070082 }
83
84 ~Connection()
85 {
Ed Tanous1d1d7782024-04-09 12:54:08 -070086 res.releaseCompleteRequestHandler();
Ed Tanous40aa0582021-07-14 13:24:40 -070087 cancelDeadlineTimer();
Ed Tanous6fbdbca2021-12-06 14:36:06 -080088
Ed Tanous40aa0582021-07-14 13:24:40 -070089 connectionCount--;
Ed Tanous62598e32023-07-17 17:06:25 -070090 BMCWEB_LOG_DEBUG("{} Connection closed, total {}", logPtr(this),
91 connectionCount);
Ed Tanous40aa0582021-07-14 13:24:40 -070092 }
93
Ed Tanousecd6a3a2022-01-07 09:18:40 -080094 Connection(const Connection&) = delete;
95 Connection(Connection&&) = delete;
96 Connection& operator=(const Connection&) = delete;
97 Connection& operator=(Connection&&) = delete;
98
Ed Tanous7c8e0642022-02-21 12:11:14 -080099 bool tlsVerifyCallback(bool preverified,
100 boost::asio::ssl::verify_context& ctx)
101 {
102 // We always return true to allow full auth flow for resources that
103 // don't require auth
104 if (preverified)
105 {
Ed Tanous1d1d7782024-04-09 12:54:08 -0700106 mtlsSession = verifyMtlsUser(ip, ctx);
Boleslaw Ogonczyk Makowskib4963072023-02-06 09:59:58 +0100107 if (mtlsSession)
Ed Tanous7c8e0642022-02-21 12:11:14 -0800108 {
Ed Tanous62598e32023-07-17 17:06:25 -0700109 BMCWEB_LOG_DEBUG("{} Generating TLS session: {}", logPtr(this),
110 mtlsSession->uniqueId);
Ed Tanous7c8e0642022-02-21 12:11:14 -0800111 }
112 }
113 return true;
114 }
115
Ed Tanous40aa0582021-07-14 13:24:40 -0700116 void prepareMutualTls()
117 {
Ed Tanous4fa45df2023-09-01 14:20:50 -0700118 if constexpr (IsTls<Adaptor>::value)
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100119 {
Ed Tanous4fa45df2023-09-01 14:20:50 -0700120 std::error_code error;
121 std::filesystem::path caPath(ensuressl::trustStorePath);
122 auto caAvailable = !std::filesystem::is_empty(caPath, error);
123 caAvailable = caAvailable && !error;
124 if (caAvailable && persistent_data::SessionStore::getInstance()
125 .getAuthMethodsConfig()
126 .tls)
Ed Tanouse7d1a1c2020-09-28 09:36:35 -0700127 {
Ed Tanous4fa45df2023-09-01 14:20:50 -0700128 adaptor.set_verify_mode(boost::asio::ssl::verify_peer);
129 std::string id = "bmcweb";
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100130
Ed Tanous4fa45df2023-09-01 14:20:50 -0700131 const char* cStr = id.c_str();
132 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
133 const auto* idC = reinterpret_cast<const unsigned char*>(cStr);
134 int ret = SSL_set_session_id_context(
135 adaptor.native_handle(), idC,
136 static_cast<unsigned int>(id.length()));
137 if (ret == 0)
138 {
139 BMCWEB_LOG_ERROR("{} failed to set SSL id", logPtr(this));
140 }
141 }
142
143 adaptor.set_verify_callback(
144 std::bind_front(&self_type::tlsVerifyCallback, this));
145 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700146 }
147
Ed Tanousceac6f72018-12-02 11:58:47 -0800148 Adaptor& socket()
Ed Tanous1abe55e2018-09-05 08:30:59 -0700149 {
Ed Tanousceac6f72018-12-02 11:58:47 -0800150 return adaptor;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700151 }
152
Ed Tanous1abe55e2018-09-05 08:30:59 -0700153 void start()
154 {
Ed Tanous1d1d7782024-04-09 12:54:08 -0700155 BMCWEB_LOG_DEBUG("{} Connection started, total {}", logPtr(this),
156 connectionCount);
Gunnar Mills4f63be02023-10-25 09:14:07 -0500157 if (connectionCount >= 200)
Ed Tanous6fbdbca2021-12-06 14:36:06 -0800158 {
Ed Tanous1d1d7782024-04-09 12:54:08 -0700159 BMCWEB_LOG_CRITICAL("{} Max connection count exceeded.",
Ed Tanous62598e32023-07-17 17:06:25 -0700160 logPtr(this));
Ed Tanous6fbdbca2021-12-06 14:36:06 -0800161 return;
162 }
163
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800164 startDeadline();
Sunitha Harishc0ea7ae2020-10-30 02:37:30 -0500165
Ed Tanous1d1d7782024-04-09 12:54:08 -0700166 readClientIp();
167
Ed Tanousceac6f72018-12-02 11:58:47 -0800168 // TODO(ed) Abstract this to a more clever class with the idea of an
169 // asynchronous "start"
Ed Tanous4fa45df2023-09-01 14:20:50 -0700170 if constexpr (IsTls<Adaptor>::value)
Ed Tanousceac6f72018-12-02 11:58:47 -0800171 {
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000172 adaptor.async_handshake(boost::asio::ssl::stream_base::server,
173 [this, self(shared_from_this())](
174 const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700175 if (ec)
176 {
177 return;
178 }
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800179 afterSslHandshake();
Ed Tanous002d39b2022-05-31 08:59:27 -0700180 });
Ed Tanousceac6f72018-12-02 11:58:47 -0800181 }
182 else
183 {
184 doReadHeaders();
185 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700186 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700187
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800188 void afterSslHandshake()
189 {
190 // If http2 is enabled, negotiate the protocol
Ed Tanous25b54db2024-04-17 15:40:31 -0700191 if constexpr (BMCWEB_EXPERIMENTAL_HTTP2)
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800192 {
193 const unsigned char* alpn = nullptr;
194 unsigned int alpnlen = 0;
195 SSL_get0_alpn_selected(adaptor.native_handle(), &alpn, &alpnlen);
196 if (alpn != nullptr)
197 {
198 std::string_view selectedProtocol(
199 std::bit_cast<const char*>(alpn), alpnlen);
Ed Tanous62598e32023-07-17 17:06:25 -0700200 BMCWEB_LOG_DEBUG("ALPN selected protocol \"{}\" len: {}",
201 selectedProtocol, alpnlen);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800202 if (selectedProtocol == "h2")
203 {
204 auto http2 =
205 std::make_shared<HTTP2Connection<Adaptor, Handler>>(
206 std::move(adaptor), handler, getCachedDateStr);
207 http2->start();
208 return;
209 }
210 }
211 }
212
213 doReadHeaders();
214 }
215
Ed Tanous1d1d7782024-04-09 12:54:08 -0700216 void initParser()
217 {
218 boost::beast::http::request_parser<bmcweb::HttpBody>& instance =
219 parser.emplace(std::piecewise_construct, std::make_tuple());
220
221 // reset header limit for newly created parser
222 instance.header_limit(httpHeaderLimit);
223
224 // Initially set no body limit. We don't yet know if the user is
225 // authenticated.
226 instance.body_limit(boost::none);
227 }
228
Ed Tanous1abe55e2018-09-05 08:30:59 -0700229 void handle()
230 {
Ed Tanousf79b7a52021-09-22 19:04:29 -0700231 std::error_code reqEc;
Ed Tanouse01d0c32023-06-30 13:21:32 -0700232 if (!parser)
233 {
234 return;
235 }
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700236 req = std::make_shared<crow::Request>(parser->release(), reqEc);
Ed Tanousf79b7a52021-09-22 19:04:29 -0700237 if (reqEc)
238 {
Ed Tanous62598e32023-07-17 17:06:25 -0700239 BMCWEB_LOG_DEBUG("Request failed to construct{}", reqEc.message());
Gunnar Mills262f1152022-12-20 15:18:47 -0600240 res.result(boost::beast::http::status::bad_request);
241 completeRequest(res);
Ed Tanousf79b7a52021-09-22 19:04:29 -0700242 return;
243 }
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700244 req->session = userSession;
Ed Tanous89cda632024-04-16 08:45:54 -0700245 accept = req->getHeaderValue("Accept");
Ivan Mikhaylovf65b0be2021-04-19 10:05:30 +0000246 // Fetch the client IP address
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700247 req->ipAddress = ip;
Ivan Mikhaylovf65b0be2021-04-19 10:05:30 +0000248
Ed Tanous1abe55e2018-09-05 08:30:59 -0700249 // Check for HTTP version 1.1.
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700250 if (req->version() == 11)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700251 {
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700252 if (req->getHeaderValue(boost::beast::http::field::host).empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700253 {
Ed Tanousde5c9f32019-03-26 09:17:55 -0700254 res.result(boost::beast::http::status::bad_request);
Nan Zhou72374eb2022-01-27 17:06:51 -0800255 completeRequest(res);
Ed Tanous6c7f01d2021-08-25 13:42:35 -0700256 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700257 }
258 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700259
Ed Tanous62598e32023-07-17 17:06:25 -0700260 BMCWEB_LOG_INFO("Request: {} HTTP/{}.{} {} {} {}", logPtr(this),
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700261 req->version() / 10, req->version() % 10,
262 req->methodString(), req->target(),
263 req->ipAddress.to_string());
Ed Tanousd32c4fa2021-09-14 13:16:51 -0700264
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700265 req->ioService = static_cast<decltype(req->ioService)>(
Ed Tanous6c7f01d2021-08-25 13:42:35 -0700266 &adaptor.get_executor().context());
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200267
Ed Tanous6c7f01d2021-08-25 13:42:35 -0700268 if (res.completed)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700269 {
Nan Zhou72374eb2022-01-27 17:06:51 -0800270 completeRequest(res);
Ed Tanous6c7f01d2021-08-25 13:42:35 -0700271 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700272 }
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700273 keepAlive = req->keepAlive();
Ed Tanous4fa45df2023-09-01 14:20:50 -0700274 if constexpr (!std::is_same_v<Adaptor, boost::beast::test::stream>)
Ed Tanous1d3c14a2021-09-22 18:54:40 -0700275 {
Ed Tanous83328312024-05-09 15:48:09 -0700276 if constexpr (!BMCWEB_INSECURE_DISABLE_AUTH)
Ed Tanous4fa45df2023-09-01 14:20:50 -0700277 {
Ed Tanous83328312024-05-09 15:48:09 -0700278 if (!crow::authentication::isOnAllowlist(req->url().path(),
279 req->method()) &&
280 req->session == nullptr)
281 {
282 BMCWEB_LOG_WARNING("Authentication failed");
283 forward_unauthorized::sendUnauthorized(
284 req->url().encoded_path(),
285 req->getHeaderValue("X-Requested-With"),
286 req->getHeaderValue("Accept"), res);
287 completeRequest(res);
288 return;
289 }
Ed Tanous4fa45df2023-09-01 14:20:50 -0700290 }
Ed Tanous4fa45df2023-09-01 14:20:50 -0700291 }
Nan Zhou72374eb2022-01-27 17:06:51 -0800292 auto asyncResp = std::make_shared<bmcweb::AsyncResp>();
Ed Tanous62598e32023-07-17 17:06:25 -0700293 BMCWEB_LOG_DEBUG("Setting completion handler");
Nan Zhou72374eb2022-01-27 17:06:51 -0800294 asyncResp->res.setCompleteRequestHandler(
295 [self(shared_from_this())](crow::Response& thisRes) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700296 self->completeRequest(thisRes);
297 });
Ed Tanous6fde95f2023-06-01 07:33:34 -0700298 bool isSse =
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700299 isContentTypeAllowed(req->getHeaderValue("Accept"),
Ed Tanous6fde95f2023-06-01 07:33:34 -0700300 http_helpers::ContentType::EventStream, false);
Ed Tanous18f8f602023-07-18 10:07:23 -0700301 std::string_view upgradeType(
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700302 req->getHeaderValue(boost::beast::http::field::upgrade));
303 if ((req->isUpgrade() &&
Ed Tanous18f8f602023-07-18 10:07:23 -0700304 bmcweb::asciiIEquals(upgradeType, "websocket")) ||
Ed Tanous6fde95f2023-06-01 07:33:34 -0700305 isSse)
Ed Tanous6c7f01d2021-08-25 13:42:35 -0700306 {
P Dheeraj Srujan Kumara9f076e2021-10-18 22:45:37 +0530307 asyncResp->res.setCompleteRequestHandler(
308 [self(shared_from_this())](crow::Response& thisRes) {
309 if (thisRes.result() != boost::beast::http::status::ok)
310 {
311 // When any error occurs before handle upgradation,
312 // the result in response will be set to respective
313 // error. By default the Result will be OK (200),
314 // which implies successful handle upgrade. Response
315 // needs to be sent over this connection only on
316 // failure.
317 self->completeRequest(thisRes);
318 return;
319 }
320 });
Ed Tanous52e31622024-01-23 16:31:11 -0800321 handler->handleUpgrade(req, asyncResp, std::move(adaptor));
Ed Tanous6c7f01d2021-08-25 13:42:35 -0700322 return;
323 }
Ed Tanous291d7092022-04-13 12:34:57 -0700324 std::string_view expected =
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700325 req->getHeaderValue(boost::beast::http::field::if_none_match);
Ed Tanous291d7092022-04-13 12:34:57 -0700326 if (!expected.empty())
327 {
328 res.setExpectedHash(expected);
329 }
Ed Tanous52e31622024-01-23 16:31:11 -0800330 handler->handle(req, asyncResp);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700331 }
Ed Tanouse0d918b2018-03-27 17:41:04 -0700332
Ed Tanous1d1d7782024-04-09 12:54:08 -0700333 void hardClose()
Ed Tanouse278c182019-03-13 16:23:37 -0700334 {
Ed Tanous1d1d7782024-04-09 12:54:08 -0700335 BMCWEB_LOG_DEBUG("{} Closing socket", logPtr(this));
336 boost::beast::get_lowest_layer(adaptor).close();
337 }
338
339 void tlsShutdownComplete(const std::shared_ptr<self_type>& self,
340 const boost::system::error_code& ec)
341 {
342 if (ec)
343 {
344 BMCWEB_LOG_WARNING("{} Failed to shut down TLS cleanly {}",
345 logPtr(self.get()), ec);
346 }
347 self->hardClose();
348 }
349
350 void gracefulClose()
351 {
352 BMCWEB_LOG_DEBUG("{} Socket close requested", logPtr(this));
353 if (mtlsSession != nullptr)
354 {
355 BMCWEB_LOG_DEBUG("{} Removing TLS session: {}", logPtr(this),
356 mtlsSession->uniqueId);
357 persistent_data::SessionStore::getInstance().removeSession(
358 mtlsSession);
359 }
Ed Tanous4fa45df2023-09-01 14:20:50 -0700360 if constexpr (IsTls<Adaptor>::value)
Ed Tanouse278c182019-03-13 16:23:37 -0700361 {
Ed Tanous1d1d7782024-04-09 12:54:08 -0700362 adaptor.async_shutdown(std::bind_front(
363 &self_type::tlsShutdownComplete, this, shared_from_this()));
Ed Tanouse278c182019-03-13 16:23:37 -0700364 }
365 else
366 {
Ed Tanous1d1d7782024-04-09 12:54:08 -0700367 hardClose();
Ed Tanouse278c182019-03-13 16:23:37 -0700368 }
369 }
370
Nan Zhou72374eb2022-01-27 17:06:51 -0800371 void completeRequest(crow::Response& thisRes)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700372 {
Nan Zhou72374eb2022-01-27 17:06:51 -0800373 res = std::move(thisRes);
Ed Tanous4cdc2e82023-01-13 10:03:22 -0800374 res.keepAlive(keepAlive);
Ed Tanous5ae6f922023-01-09 10:45:53 -0800375
Ed Tanous89cda632024-04-16 08:45:54 -0700376 completeResponseFields(accept, res);
Ed Tanous998e0cb2023-09-06 13:57:30 -0700377 res.addHeader(boost::beast::http::field::date, getCachedDateStr());
Ed Tanous291d7092022-04-13 12:34:57 -0700378
Ed Tanous52e31622024-01-23 16:31:11 -0800379 doWrite();
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000380
381 // delete lambda with self shared_ptr
382 // to enable connection destruction
John Edward Broadbent4147b8a2021-07-19 16:52:24 -0700383 res.setCompleteRequestHandler(nullptr);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700384 }
385
Sunitha Harishc0ea7ae2020-10-30 02:37:30 -0500386 void readClientIp()
387 {
388 boost::system::error_code ec;
Sunitha Harishc0ea7ae2020-10-30 02:37:30 -0500389
Ed Tanous4fa45df2023-09-01 14:20:50 -0700390 if constexpr (!std::is_same_v<Adaptor, boost::beast::test::stream>)
Sunitha Harishc0ea7ae2020-10-30 02:37:30 -0500391 {
Ed Tanous4fa45df2023-09-01 14:20:50 -0700392 boost::asio::ip::tcp::endpoint endpoint =
393 boost::beast::get_lowest_layer(adaptor).remote_endpoint(ec);
394
395 if (ec)
396 {
397 // If remote endpoint fails keep going. "ClientOriginIPAddress"
398 // will be empty.
399 BMCWEB_LOG_ERROR(
400 "Failed to get the client's IP Address. ec : {}", ec);
Ed Tanous1d1d7782024-04-09 12:54:08 -0700401 return;
Ed Tanous4fa45df2023-09-01 14:20:50 -0700402 }
403 ip = endpoint.address();
Sunitha Harishc0ea7ae2020-10-30 02:37:30 -0500404 }
Sunitha Harishc0ea7ae2020-10-30 02:37:30 -0500405 }
406
Ed Tanous1abe55e2018-09-05 08:30:59 -0700407 private:
Ed Tanous1d1d7782024-04-09 12:54:08 -0700408 uint64_t getContentLengthLimit()
409 {
Ed Tanous83328312024-05-09 15:48:09 -0700410 if constexpr (!BMCWEB_INSECURE_DISABLE_AUTH)
Ed Tanous1d1d7782024-04-09 12:54:08 -0700411 {
Ed Tanous83328312024-05-09 15:48:09 -0700412 if (userSession == nullptr)
413 {
414 return loggedOutPostBodyLimit;
415 }
Ed Tanous1d1d7782024-04-09 12:54:08 -0700416 }
Ed Tanous1d1d7782024-04-09 12:54:08 -0700417
418 return httpReqBodyLimit;
419 }
420
421 // Returns true if content length was within limits
422 // Returns false if content length error has been returned
423 bool handleContentLengthError()
424 {
425 if (!parser)
426 {
Manojkiran Edaefff2b52024-06-18 18:01:46 +0530427 BMCWEB_LOG_CRITICAL("Parser was null");
Ed Tanous1d1d7782024-04-09 12:54:08 -0700428 return false;
429 }
430 const boost::optional<uint64_t> contentLength =
431 parser->content_length();
432 if (!contentLength)
433 {
434 BMCWEB_LOG_DEBUG("{} No content length available", logPtr(this));
435 return true;
436 }
437
438 uint64_t maxAllowedContentLength = getContentLengthLimit();
439
440 if (*contentLength > maxAllowedContentLength)
441 {
442 // If the users content limit is between the logged in
443 // and logged out limits They probably just didn't log
444 // in
445 if (*contentLength > loggedOutPostBodyLimit &&
446 *contentLength < httpReqBodyLimit)
447 {
448 BMCWEB_LOG_DEBUG(
449 "{} Content length {} valid, but greater than logged out"
450 " limit of {}. Setting unauthorized",
451 logPtr(this), *contentLength, loggedOutPostBodyLimit);
452 res.result(boost::beast::http::status::unauthorized);
453 }
454 else
455 {
456 // Otherwise they're over both limits, so inform
457 // them
458 BMCWEB_LOG_DEBUG(
459 "{} Content length {} was greater than global limit {}."
460 " Setting payload too large",
461 logPtr(this), *contentLength, httpReqBodyLimit);
462 res.result(boost::beast::http::status::payload_too_large);
463 }
464
465 keepAlive = false;
466 doWrite();
467 return false;
468 }
469
470 return true;
471 }
472
Ed Tanous1abe55e2018-09-05 08:30:59 -0700473 void doReadHeaders()
474 {
Ed Tanous62598e32023-07-17 17:06:25 -0700475 BMCWEB_LOG_DEBUG("{} doReadHeaders", logPtr(this));
Ed Tanouse01d0c32023-06-30 13:21:32 -0700476 if (!parser)
477 {
Ed Tanous1d1d7782024-04-09 12:54:08 -0700478 BMCWEB_LOG_CRITICAL("Parser was not initialized.");
Ed Tanouse01d0c32023-06-30 13:21:32 -0700479 return;
480 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700481 // Clean up any previous Connection.
482 boost::beast::http::async_read_header(
Ed Tanousceac6f72018-12-02 11:58:47 -0800483 adaptor, buffer, *parser,
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000484 [this,
485 self(shared_from_this())](const boost::system::error_code& ec,
Ed Tanous81ce6092020-12-17 16:54:55 +0000486 std::size_t bytesTransferred) {
Ed Tanous62598e32023-07-17 17:06:25 -0700487 BMCWEB_LOG_DEBUG("{} async_read_header {} Bytes", logPtr(this),
488 bytesTransferred);
Ed Tanous52e31622024-01-23 16:31:11 -0800489
Ed Tanous002d39b2022-05-31 08:59:27 -0700490 if (ec)
491 {
Ed Tanous52e31622024-01-23 16:31:11 -0800492 cancelDeadlineTimer();
493
Ed Tanous1d1d7782024-04-09 12:54:08 -0700494 if (ec == boost::beast::http::error::header_limit)
495 {
496 BMCWEB_LOG_ERROR("{} Header field too large, closing",
497 logPtr(this), ec.message());
498
499 res.result(boost::beast::http::status::
500 request_header_fields_too_large);
501 keepAlive = false;
502 doWrite();
503 return;
504 }
Myung Baea4326fe2023-01-10 14:29:24 -0600505 if (ec == boost::beast::http::error::end_of_stream)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700506 {
Ed Tanous1d1d7782024-04-09 12:54:08 -0700507 BMCWEB_LOG_WARNING("{} End of stream, closing {}",
508 logPtr(this), ec);
509 hardClose();
510 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700511 }
Ed Tanous1d1d7782024-04-09 12:54:08 -0700512
513 BMCWEB_LOG_DEBUG("{} Closing socket due to read error {}",
514 logPtr(this), ec.message());
515 gracefulClose();
516
Ed Tanous002d39b2022-05-31 08:59:27 -0700517 return;
518 }
519
Ed Tanous83328312024-05-09 15:48:09 -0700520 if constexpr (!std::is_same_v<Adaptor, boost::beast::test::stream>)
521 {
522 if constexpr (!BMCWEB_INSECURE_DISABLE_AUTH)
523 {
524 boost::beast::http::verb method = parser->get().method();
525 userSession = crow::authentication::authenticate(
526 ip, res, method, parser->get().base(), mtlsSession);
527 }
528 }
529
Ed Tanous1d1d7782024-04-09 12:54:08 -0700530 std::string_view expect =
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700531 parser->get()[boost::beast::http::field::expect];
Ed Tanous1d1d7782024-04-09 12:54:08 -0700532 if (bmcweb::asciiIEquals(expect, "100-continue"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700533 {
Ed Tanous1d1d7782024-04-09 12:54:08 -0700534 res.result(boost::beast::http::status::continue_);
535 doWrite();
536 return;
Ed Tanous002d39b2022-05-31 08:59:27 -0700537 }
Ed Tanous1d1d7782024-04-09 12:54:08 -0700538
Ed Tanous1d1d7782024-04-09 12:54:08 -0700539 if (!handleContentLengthError())
540 {
541 return;
542 }
543
544 parser->body_limit(getContentLengthLimit());
545
Ed Tanous7d243eb2023-01-23 15:57:41 -0800546 if (parser->is_done())
547 {
548 handle();
549 return;
550 }
551
Ed Tanous002d39b2022-05-31 08:59:27 -0700552 doRead();
Patrick Williams5a39f772023-10-20 11:20:21 -0500553 });
Ed Tanous1abe55e2018-09-05 08:30:59 -0700554 }
555
556 void doRead()
557 {
Ed Tanous62598e32023-07-17 17:06:25 -0700558 BMCWEB_LOG_DEBUG("{} doRead", logPtr(this));
Ed Tanouse01d0c32023-06-30 13:21:32 -0700559 if (!parser)
560 {
561 return;
562 }
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800563 startDeadline();
Ed Tanous7d243eb2023-01-23 15:57:41 -0800564 boost::beast::http::async_read_some(
565 adaptor, buffer, *parser,
566 [this,
567 self(shared_from_this())](const boost::system::error_code& ec,
568 std::size_t bytesTransferred) {
Ed Tanous62598e32023-07-17 17:06:25 -0700569 BMCWEB_LOG_DEBUG("{} async_read_some {} Bytes", logPtr(this),
570 bytesTransferred);
Ed Tanous7d243eb2023-01-23 15:57:41 -0800571
Ed Tanous002d39b2022-05-31 08:59:27 -0700572 if (ec)
573 {
Ed Tanous62598e32023-07-17 17:06:25 -0700574 BMCWEB_LOG_ERROR("{} Error while reading: {}", logPtr(this),
575 ec.message());
Ed Tanous1d1d7782024-04-09 12:54:08 -0700576 if (ec == boost::beast::http::error::body_limit)
577 {
578 if (handleContentLengthError())
579 {
580 BMCWEB_LOG_CRITICAL("Body length limit reached, "
581 "but no content-length "
582 "available? Should never happen");
583 res.result(
584 boost::beast::http::status::internal_server_error);
585 keepAlive = false;
586 doWrite();
587 }
588 return;
589 }
590
591 gracefulClose();
Ed Tanous002d39b2022-05-31 08:59:27 -0700592 return;
593 }
Ed Tanous7d243eb2023-01-23 15:57:41 -0800594
595 // If the user is logged in, allow them to send files incrementally
596 // one piece at a time. If authentication is disabled then there is
597 // no user session hence always allow to send one piece at a time.
598 if (userSession != nullptr)
599 {
600 cancelDeadlineTimer();
601 }
602 if (!parser->is_done())
603 {
604 doRead();
605 return;
606 }
607
608 cancelDeadlineTimer();
Ed Tanous002d39b2022-05-31 08:59:27 -0700609 handle();
Patrick Williams5a39f772023-10-20 11:20:21 -0500610 });
Ed Tanous1abe55e2018-09-05 08:30:59 -0700611 }
612
Ed Tanous27b0cf92023-08-07 12:02:40 -0700613 void afterDoWrite(const std::shared_ptr<self_type>& /*self*/,
614 const boost::system::error_code& ec,
615 std::size_t bytesTransferred)
616 {
Ed Tanous0242baf2024-05-16 19:52:47 -0700617 BMCWEB_LOG_DEBUG("{} async_write wrote {} bytes, ec={}", logPtr(this),
Ed Tanous1d1d7782024-04-09 12:54:08 -0700618 bytesTransferred, ec);
Ed Tanous27b0cf92023-08-07 12:02:40 -0700619
620 cancelDeadlineTimer();
621
Ed Tanous0242baf2024-05-16 19:52:47 -0700622 if (ec == boost::system::errc::operation_would_block ||
623 ec == boost::system::errc::resource_unavailable_try_again)
624 {
625 doWrite();
626 return;
627 }
Ed Tanous27b0cf92023-08-07 12:02:40 -0700628 if (ec)
629 {
630 BMCWEB_LOG_DEBUG("{} from write(2)", logPtr(this));
631 return;
632 }
Ed Tanous1d1d7782024-04-09 12:54:08 -0700633
634 if (res.result() == boost::beast::http::status::continue_)
635 {
636 // Reset the result to ok
637 res.result(boost::beast::http::status::ok);
638 doRead();
639 return;
640 }
641
Ed Tanous27b0cf92023-08-07 12:02:40 -0700642 if (!keepAlive)
643 {
Ed Tanous1d1d7782024-04-09 12:54:08 -0700644 BMCWEB_LOG_DEBUG("{} keepalive not set. Closing socket",
645 logPtr(this));
646
647 gracefulClose();
Ed Tanous27b0cf92023-08-07 12:02:40 -0700648 return;
649 }
650
651 BMCWEB_LOG_DEBUG("{} Clearing response", logPtr(this));
652 res.clear();
Ed Tanous1d1d7782024-04-09 12:54:08 -0700653 initParser();
Ed Tanous27b0cf92023-08-07 12:02:40 -0700654
655 userSession = nullptr;
656
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700657 req->clear();
Ed Tanous27b0cf92023-08-07 12:02:40 -0700658 doReadHeaders();
659 }
660
Ed Tanous52e31622024-01-23 16:31:11 -0800661 void doWrite()
Ed Tanous1abe55e2018-09-05 08:30:59 -0700662 {
Ed Tanous62598e32023-07-17 17:06:25 -0700663 BMCWEB_LOG_DEBUG("{} doWrite", logPtr(this));
Ed Tanous52e31622024-01-23 16:31:11 -0800664 res.preparePayload();
Ed Tanous27b0cf92023-08-07 12:02:40 -0700665
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800666 startDeadline();
Ed Tanous4d698612024-02-06 14:57:24 -0800667 boost::beast::async_write(
668 adaptor,
669 boost::beast::http::message_generator(std::move(res.response)),
Ed Tanous52e31622024-01-23 16:31:11 -0800670 std::bind_front(&self_type::afterDoWrite, this,
671 shared_from_this()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700672 }
673
Ed Tanous1abe55e2018-09-05 08:30:59 -0700674 void cancelDeadlineTimer()
675 {
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800676 timer.cancel();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700677 }
678
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800679 void startDeadline()
Ed Tanous1abe55e2018-09-05 08:30:59 -0700680 {
Ed Tanous7d243eb2023-01-23 15:57:41 -0800681 // Timer is already started so no further action is required.
682 if (timerStarted)
683 {
684 return;
685 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700686
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800687 std::chrono::seconds timeout(15);
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800688
689 std::weak_ptr<Connection<Adaptor, Handler>> weakSelf = weak_from_this();
690 timer.expires_after(timeout);
Ed Tanous81c4e332023-05-18 10:30:34 -0700691 timer.async_wait([weakSelf](const boost::system::error_code& ec) {
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800692 // Note, we are ignoring other types of errors here; If the timer
693 // failed for any reason, we should still close the connection
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800694 std::shared_ptr<Connection<Adaptor, Handler>> self =
695 weakSelf.lock();
696 if (!self)
697 {
Ed Tanous1d1d7782024-04-09 12:54:08 -0700698 if (ec == boost::asio::error::operation_aborted)
699 {
700 BMCWEB_LOG_DEBUG(
701 "{} Timer canceled on connection being destroyed",
702 logPtr(self.get()));
703 return;
704 }
Ed Tanous62598e32023-07-17 17:06:25 -0700705 BMCWEB_LOG_CRITICAL("{} Failed to capture connection",
706 logPtr(self.get()));
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800707 return;
708 }
Ed Tanous7d243eb2023-01-23 15:57:41 -0800709
710 self->timerStarted = false;
711
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800712 if (ec)
713 {
Ed Tanous1d1d7782024-04-09 12:54:08 -0700714 if (ec == boost::asio::error::operation_aborted)
715 {
716 BMCWEB_LOG_DEBUG("{} Timer canceled", logPtr(self.get()));
717 return;
718 }
719 BMCWEB_LOG_CRITICAL("{} Timer failed {}", logPtr(self.get()),
Ed Tanous62598e32023-07-17 17:06:25 -0700720 ec);
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800721 }
722
Ed Tanous1d1d7782024-04-09 12:54:08 -0700723 BMCWEB_LOG_WARNING("{} Connection timed out, hard closing",
Ed Tanous62598e32023-07-17 17:06:25 -0700724 logPtr(self.get()));
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800725
Ed Tanous1d1d7782024-04-09 12:54:08 -0700726 self->hardClose();
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800727 });
728
Ed Tanous7d243eb2023-01-23 15:57:41 -0800729 timerStarted = true;
Ed Tanous62598e32023-07-17 17:06:25 -0700730 BMCWEB_LOG_DEBUG("{} timer started", logPtr(this));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700731 }
732
Ed Tanous1abe55e2018-09-05 08:30:59 -0700733 Adaptor adaptor;
734 Handler* handler;
Ed Tanous1d1d7782024-04-09 12:54:08 -0700735
736 boost::asio::ip::address ip;
737
Ed Tanousa24526d2018-12-10 15:17:59 -0800738 // Making this a std::optional allows it to be efficiently destroyed and
Ed Tanous1abe55e2018-09-05 08:30:59 -0700739 // re-created on Connection reset
Ed Tanousb2896142024-01-31 15:25:47 -0800740 std::optional<boost::beast::http::request_parser<bmcweb::HttpBody>> parser;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700741
Ed Tanous3112a142018-11-29 15:45:10 -0800742 boost::beast::flat_static_buffer<8192> buffer;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700743
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700744 std::shared_ptr<crow::Request> req;
Ed Tanous89cda632024-04-16 08:45:54 -0700745 std::string accept;
746
Ed Tanous1abe55e2018-09-05 08:30:59 -0700747 crow::Response res;
Ed Tanous52cc1122020-07-18 13:51:21 -0700748
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700749 std::shared_ptr<persistent_data::UserSession> userSession;
Boleslaw Ogonczyk Makowskib4963072023-02-06 09:59:58 +0100750 std::shared_ptr<persistent_data::UserSession> mtlsSession;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700751
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800752 boost::asio::steady_timer timer;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700753
Ed Tanous4cdc2e82023-01-13 10:03:22 -0800754 bool keepAlive = true;
755
Ed Tanous7d243eb2023-01-23 15:57:41 -0800756 bool timerStarted = false;
757
Ed Tanous1abe55e2018-09-05 08:30:59 -0700758 std::function<std::string()>& getCachedDateStr;
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000759
760 using std::enable_shared_from_this<
Ed Tanous52cc1122020-07-18 13:51:21 -0700761 Connection<Adaptor, Handler>>::shared_from_this;
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800762
763 using std::enable_shared_from_this<
764 Connection<Adaptor, Handler>>::weak_from_this;
Ed Tanous3112a142018-11-29 15:45:10 -0800765};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700766} // namespace crow