blob: e02c518ebb9002f94a43b8776858447c12eaa987 [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>
33#include <vector>
34
Ed Tanous1abe55e2018-09-05 08:30:59 -070035namespace crow
36{
Ed Tanous257f5792018-03-17 14:40:09 -070037
Ed Tanouscf9e4172022-12-21 09:30:16 -080038// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Ed Tanous6fbdbca2021-12-06 14:36:06 -080039static int connectionCount = 0;
Jennifer Leeacb7cfb2018-06-07 16:08:15 -070040
Ed Tanous25b54db2024-04-17 15:40:31 -070041// request body limit size set by the BMCWEB_HTTP_BODY_LIMIT option
42constexpr uint64_t httpReqBodyLimit = 1024UL * 1024UL * BMCWEB_HTTP_BODY_LIMIT;
Jennifer Leeacb7cfb2018-06-07 16:08:15 -070043
Ed Tanous1d1d7782024-04-09 12:54:08 -070044constexpr uint64_t loggedOutPostBodyLimit = 4096U;
James Feist3909dc82020-04-03 10:58:55 -070045
Ed Tanous1d1d7782024-04-09 12:54:08 -070046constexpr uint32_t httpHeaderLimit = 8192U;
James Feist3909dc82020-04-03 10:58:55 -070047
Ed Tanous4fa45df2023-09-01 14:20:50 -070048template <typename>
49struct IsTls : std::false_type
50{};
51
52template <typename T>
Ed Tanous003301a2024-04-16 09:59:19 -070053struct IsTls<boost::asio::ssl::stream<T>> : std::true_type
Ed Tanous4fa45df2023-09-01 14:20:50 -070054{};
55
Ed Tanous52cc1122020-07-18 13:51:21 -070056template <typename Adaptor, typename Handler>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050057class Connection :
Ed Tanous52cc1122020-07-18 13:51:21 -070058 public std::enable_shared_from_this<Connection<Adaptor, Handler>>
Ed Tanous1abe55e2018-09-05 08:30:59 -070059{
Ed Tanous7c8e0642022-02-21 12:11:14 -080060 using self_type = Connection<Adaptor, Handler>;
61
Ed Tanous1abe55e2018-09-05 08:30:59 -070062 public:
Ed Tanous5dfb5b22021-12-03 11:24:53 -080063 Connection(Handler* handlerIn, boost::asio::steady_timer&& timerIn,
Ed Tanous81ce6092020-12-17 16:54:55 +000064 std::function<std::string()>& getCachedDateStrF,
Ed Tanous5dfb5b22021-12-03 11:24:53 -080065 Adaptor adaptorIn) :
Ed Tanousceac6f72018-12-02 11:58:47 -080066 adaptor(std::move(adaptorIn)),
Ed Tanous5dfb5b22021-12-03 11:24:53 -080067 handler(handlerIn), timer(std::move(timerIn)),
68 getCachedDateStr(getCachedDateStrF)
Ed Tanous1abe55e2018-09-05 08:30:59 -070069 {
Ed Tanous1d1d7782024-04-09 12:54:08 -070070 initParser();
Kowalski, Kamil55e43f62019-07-10 13:12:57 +020071
Ed Tanous25b54db2024-04-17 15:40:31 -070072 if constexpr (BMCWEB_MUTUAL_TLS_AUTH)
73 {
74 prepareMutualTls();
75 }
Ed Tanous40aa0582021-07-14 13:24:40 -070076
Ed Tanous40aa0582021-07-14 13:24:40 -070077 connectionCount++;
Ed Tanous6fbdbca2021-12-06 14:36:06 -080078
Ed Tanous1d1d7782024-04-09 12:54:08 -070079 BMCWEB_LOG_DEBUG("{} Connection created, total {}", logPtr(this),
Ed Tanous62598e32023-07-17 17:06:25 -070080 connectionCount);
Ed Tanous40aa0582021-07-14 13:24:40 -070081 }
82
83 ~Connection()
84 {
Ed Tanous1d1d7782024-04-09 12:54:08 -070085 res.releaseCompleteRequestHandler();
Ed Tanous40aa0582021-07-14 13:24:40 -070086 cancelDeadlineTimer();
Ed Tanous6fbdbca2021-12-06 14:36:06 -080087
Ed Tanous40aa0582021-07-14 13:24:40 -070088 connectionCount--;
Ed Tanous62598e32023-07-17 17:06:25 -070089 BMCWEB_LOG_DEBUG("{} Connection closed, total {}", logPtr(this),
90 connectionCount);
Ed Tanous40aa0582021-07-14 13:24:40 -070091 }
92
Ed Tanousecd6a3a2022-01-07 09:18:40 -080093 Connection(const Connection&) = delete;
94 Connection(Connection&&) = delete;
95 Connection& operator=(const Connection&) = delete;
96 Connection& operator=(Connection&&) = delete;
97
Ed Tanous7c8e0642022-02-21 12:11:14 -080098 bool tlsVerifyCallback(bool preverified,
99 boost::asio::ssl::verify_context& ctx)
100 {
101 // We always return true to allow full auth flow for resources that
102 // don't require auth
103 if (preverified)
104 {
Ed Tanous1d1d7782024-04-09 12:54:08 -0700105 mtlsSession = verifyMtlsUser(ip, ctx);
Boleslaw Ogonczyk Makowskib4963072023-02-06 09:59:58 +0100106 if (mtlsSession)
Ed Tanous7c8e0642022-02-21 12:11:14 -0800107 {
Ed Tanous62598e32023-07-17 17:06:25 -0700108 BMCWEB_LOG_DEBUG("{} Generating TLS session: {}", logPtr(this),
109 mtlsSession->uniqueId);
Ed Tanous7c8e0642022-02-21 12:11:14 -0800110 }
111 }
112 return true;
113 }
114
Ed Tanous40aa0582021-07-14 13:24:40 -0700115 void prepareMutualTls()
116 {
Ed Tanous4fa45df2023-09-01 14:20:50 -0700117 if constexpr (IsTls<Adaptor>::value)
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100118 {
Ed Tanous4fa45df2023-09-01 14:20:50 -0700119 std::error_code error;
120 std::filesystem::path caPath(ensuressl::trustStorePath);
121 auto caAvailable = !std::filesystem::is_empty(caPath, error);
122 caAvailable = caAvailable && !error;
123 if (caAvailable && persistent_data::SessionStore::getInstance()
124 .getAuthMethodsConfig()
125 .tls)
Ed Tanouse7d1a1c2020-09-28 09:36:35 -0700126 {
Ed Tanous4fa45df2023-09-01 14:20:50 -0700127 adaptor.set_verify_mode(boost::asio::ssl::verify_peer);
128 std::string id = "bmcweb";
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100129
Ed Tanous4fa45df2023-09-01 14:20:50 -0700130 const char* cStr = id.c_str();
131 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
132 const auto* idC = reinterpret_cast<const unsigned char*>(cStr);
133 int ret = SSL_set_session_id_context(
134 adaptor.native_handle(), idC,
135 static_cast<unsigned int>(id.length()));
136 if (ret == 0)
137 {
138 BMCWEB_LOG_ERROR("{} failed to set SSL id", logPtr(this));
139 }
140 }
141
142 adaptor.set_verify_callback(
143 std::bind_front(&self_type::tlsVerifyCallback, this));
144 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700145 }
146
Ed Tanousceac6f72018-12-02 11:58:47 -0800147 Adaptor& socket()
Ed Tanous1abe55e2018-09-05 08:30:59 -0700148 {
Ed Tanousceac6f72018-12-02 11:58:47 -0800149 return adaptor;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700150 }
151
Ed Tanous1abe55e2018-09-05 08:30:59 -0700152 void start()
153 {
Ed Tanous1d1d7782024-04-09 12:54:08 -0700154 BMCWEB_LOG_DEBUG("{} Connection started, total {}", logPtr(this),
155 connectionCount);
Gunnar Mills4f63be02023-10-25 09:14:07 -0500156 if (connectionCount >= 200)
Ed Tanous6fbdbca2021-12-06 14:36:06 -0800157 {
Ed Tanous1d1d7782024-04-09 12:54:08 -0700158 BMCWEB_LOG_CRITICAL("{} Max connection count exceeded.",
Ed Tanous62598e32023-07-17 17:06:25 -0700159 logPtr(this));
Ed Tanous6fbdbca2021-12-06 14:36:06 -0800160 return;
161 }
162
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800163 startDeadline();
Sunitha Harishc0ea7ae2020-10-30 02:37:30 -0500164
Ed Tanous1d1d7782024-04-09 12:54:08 -0700165 readClientIp();
166
Ed Tanousceac6f72018-12-02 11:58:47 -0800167 // TODO(ed) Abstract this to a more clever class with the idea of an
168 // asynchronous "start"
Ed Tanous4fa45df2023-09-01 14:20:50 -0700169 if constexpr (IsTls<Adaptor>::value)
Ed Tanousceac6f72018-12-02 11:58:47 -0800170 {
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000171 adaptor.async_handshake(boost::asio::ssl::stream_base::server,
172 [this, self(shared_from_this())](
173 const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700174 if (ec)
175 {
176 return;
177 }
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800178 afterSslHandshake();
Ed Tanous002d39b2022-05-31 08:59:27 -0700179 });
Ed Tanousceac6f72018-12-02 11:58:47 -0800180 }
181 else
182 {
183 doReadHeaders();
184 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700185 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700186
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800187 void afterSslHandshake()
188 {
189 // If http2 is enabled, negotiate the protocol
Ed Tanous25b54db2024-04-17 15:40:31 -0700190 if constexpr (BMCWEB_EXPERIMENTAL_HTTP2)
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800191 {
192 const unsigned char* alpn = nullptr;
193 unsigned int alpnlen = 0;
194 SSL_get0_alpn_selected(adaptor.native_handle(), &alpn, &alpnlen);
195 if (alpn != nullptr)
196 {
197 std::string_view selectedProtocol(
198 std::bit_cast<const char*>(alpn), alpnlen);
Ed Tanous62598e32023-07-17 17:06:25 -0700199 BMCWEB_LOG_DEBUG("ALPN selected protocol \"{}\" len: {}",
200 selectedProtocol, alpnlen);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800201 if (selectedProtocol == "h2")
202 {
203 auto http2 =
204 std::make_shared<HTTP2Connection<Adaptor, Handler>>(
205 std::move(adaptor), handler, getCachedDateStr);
206 http2->start();
207 return;
208 }
209 }
210 }
211
212 doReadHeaders();
213 }
214
Ed Tanous1d1d7782024-04-09 12:54:08 -0700215 void initParser()
216 {
217 boost::beast::http::request_parser<bmcweb::HttpBody>& instance =
218 parser.emplace(std::piecewise_construct, std::make_tuple());
219
220 // reset header limit for newly created parser
221 instance.header_limit(httpHeaderLimit);
222
223 // Initially set no body limit. We don't yet know if the user is
224 // authenticated.
225 instance.body_limit(boost::none);
226 }
227
Ed Tanous1abe55e2018-09-05 08:30:59 -0700228 void handle()
229 {
Ed Tanousf79b7a52021-09-22 19:04:29 -0700230 std::error_code reqEc;
Ed Tanouse01d0c32023-06-30 13:21:32 -0700231 if (!parser)
232 {
233 return;
234 }
Ed Tanous52e31622024-01-23 16:31:11 -0800235 req = crow::Request(parser->release(), reqEc);
Ed Tanousf79b7a52021-09-22 19:04:29 -0700236 if (reqEc)
237 {
Ed Tanous62598e32023-07-17 17:06:25 -0700238 BMCWEB_LOG_DEBUG("Request failed to construct{}", reqEc.message());
Gunnar Mills262f1152022-12-20 15:18:47 -0600239 res.result(boost::beast::http::status::bad_request);
240 completeRequest(res);
Ed Tanousf79b7a52021-09-22 19:04:29 -0700241 return;
242 }
Ed Tanous52e31622024-01-23 16:31:11 -0800243 req.session = userSession;
Ed Tanous596b2032021-09-13 10:32:22 -0700244
Ivan Mikhaylovf65b0be2021-04-19 10:05:30 +0000245 // Fetch the client IP address
Ed Tanous1d1d7782024-04-09 12:54:08 -0700246 req.ipAddress = ip;
Ivan Mikhaylovf65b0be2021-04-19 10:05:30 +0000247
Ed Tanous1abe55e2018-09-05 08:30:59 -0700248 // Check for HTTP version 1.1.
Ed Tanous52e31622024-01-23 16:31:11 -0800249 if (req.version() == 11)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700250 {
Ed Tanous52e31622024-01-23 16:31:11 -0800251 if (req.getHeaderValue(boost::beast::http::field::host).empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700252 {
Ed Tanousde5c9f32019-03-26 09:17:55 -0700253 res.result(boost::beast::http::status::bad_request);
Nan Zhou72374eb2022-01-27 17:06:51 -0800254 completeRequest(res);
Ed Tanous6c7f01d2021-08-25 13:42:35 -0700255 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700256 }
257 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700258
Ed Tanous62598e32023-07-17 17:06:25 -0700259 BMCWEB_LOG_INFO("Request: {} HTTP/{}.{} {} {} {}", logPtr(this),
Ed Tanous52e31622024-01-23 16:31:11 -0800260 req.version() / 10, req.version() % 10,
261 req.methodString(), req.target(),
262 req.ipAddress.to_string());
Ed Tanousd32c4fa2021-09-14 13:16:51 -0700263
Ed Tanous52e31622024-01-23 16:31:11 -0800264 req.ioService = static_cast<decltype(req.ioService)>(
Ed Tanous6c7f01d2021-08-25 13:42:35 -0700265 &adaptor.get_executor().context());
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200266
Ed Tanous6c7f01d2021-08-25 13:42:35 -0700267 if (res.completed)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700268 {
Nan Zhou72374eb2022-01-27 17:06:51 -0800269 completeRequest(res);
Ed Tanous6c7f01d2021-08-25 13:42:35 -0700270 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700271 }
Ed Tanous52e31622024-01-23 16:31:11 -0800272 keepAlive = req.keepAlive();
Ed Tanous4fa45df2023-09-01 14:20:50 -0700273 if constexpr (!std::is_same_v<Adaptor, boost::beast::test::stream>)
Ed Tanous1d3c14a2021-09-22 18:54:40 -0700274 {
Ed Tanous4fa45df2023-09-01 14:20:50 -0700275#ifndef BMCWEB_INSECURE_DISABLE_AUTHX
Ed Tanous52e31622024-01-23 16:31:11 -0800276 if (!crow::authentication::isOnAllowlist(req.url().path(),
277 req.method()) &&
278 req.session == nullptr)
Ed Tanous4fa45df2023-09-01 14:20:50 -0700279 {
280 BMCWEB_LOG_WARNING("Authentication failed");
281 forward_unauthorized::sendUnauthorized(
Ed Tanous52e31622024-01-23 16:31:11 -0800282 req.url().encoded_path(),
283 req.getHeaderValue("X-Requested-With"),
284 req.getHeaderValue("Accept"), res);
Ed Tanous4fa45df2023-09-01 14:20:50 -0700285 completeRequest(res);
286 return;
287 }
Nan Zhoua43ea822022-05-27 00:42:44 +0000288#endif // BMCWEB_INSECURE_DISABLE_AUTHX
Ed Tanous4fa45df2023-09-01 14:20:50 -0700289 }
Nan Zhou72374eb2022-01-27 17:06:51 -0800290 auto asyncResp = std::make_shared<bmcweb::AsyncResp>();
Ed Tanous62598e32023-07-17 17:06:25 -0700291 BMCWEB_LOG_DEBUG("Setting completion handler");
Nan Zhou72374eb2022-01-27 17:06:51 -0800292 asyncResp->res.setCompleteRequestHandler(
293 [self(shared_from_this())](crow::Response& thisRes) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700294 self->completeRequest(thisRes);
295 });
Ed Tanous6fde95f2023-06-01 07:33:34 -0700296 bool isSse =
Ed Tanous52e31622024-01-23 16:31:11 -0800297 isContentTypeAllowed(req.getHeaderValue("Accept"),
Ed Tanous6fde95f2023-06-01 07:33:34 -0700298 http_helpers::ContentType::EventStream, false);
Ed Tanous18f8f602023-07-18 10:07:23 -0700299 std::string_view upgradeType(
Ed Tanous52e31622024-01-23 16:31:11 -0800300 req.getHeaderValue(boost::beast::http::field::upgrade));
301 if ((req.isUpgrade() &&
Ed Tanous18f8f602023-07-18 10:07:23 -0700302 bmcweb::asciiIEquals(upgradeType, "websocket")) ||
Ed Tanous6fde95f2023-06-01 07:33:34 -0700303 isSse)
Ed Tanous6c7f01d2021-08-25 13:42:35 -0700304 {
P Dheeraj Srujan Kumara9f076e2021-10-18 22:45:37 +0530305 asyncResp->res.setCompleteRequestHandler(
306 [self(shared_from_this())](crow::Response& thisRes) {
307 if (thisRes.result() != boost::beast::http::status::ok)
308 {
309 // When any error occurs before handle upgradation,
310 // the result in response will be set to respective
311 // error. By default the Result will be OK (200),
312 // which implies successful handle upgrade. Response
313 // needs to be sent over this connection only on
314 // failure.
315 self->completeRequest(thisRes);
316 return;
317 }
318 });
Ed Tanous52e31622024-01-23 16:31:11 -0800319 handler->handleUpgrade(req, asyncResp, std::move(adaptor));
Ed Tanous6c7f01d2021-08-25 13:42:35 -0700320 return;
321 }
Ed Tanous291d7092022-04-13 12:34:57 -0700322 std::string_view expected =
Ed Tanous52e31622024-01-23 16:31:11 -0800323 req.getHeaderValue(boost::beast::http::field::if_none_match);
Ed Tanous291d7092022-04-13 12:34:57 -0700324 if (!expected.empty())
325 {
326 res.setExpectedHash(expected);
327 }
Ed Tanous52e31622024-01-23 16:31:11 -0800328 handler->handle(req, asyncResp);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700329 }
Ed Tanouse0d918b2018-03-27 17:41:04 -0700330
Ed Tanous1d1d7782024-04-09 12:54:08 -0700331 void hardClose()
Ed Tanouse278c182019-03-13 16:23:37 -0700332 {
Ed Tanous1d1d7782024-04-09 12:54:08 -0700333 BMCWEB_LOG_DEBUG("{} Closing socket", logPtr(this));
334 boost::beast::get_lowest_layer(adaptor).close();
335 }
336
337 void tlsShutdownComplete(const std::shared_ptr<self_type>& self,
338 const boost::system::error_code& ec)
339 {
340 if (ec)
341 {
342 BMCWEB_LOG_WARNING("{} Failed to shut down TLS cleanly {}",
343 logPtr(self.get()), ec);
344 }
345 self->hardClose();
346 }
347
348 void gracefulClose()
349 {
350 BMCWEB_LOG_DEBUG("{} Socket close requested", logPtr(this));
351 if (mtlsSession != nullptr)
352 {
353 BMCWEB_LOG_DEBUG("{} Removing TLS session: {}", logPtr(this),
354 mtlsSession->uniqueId);
355 persistent_data::SessionStore::getInstance().removeSession(
356 mtlsSession);
357 }
Ed Tanous4fa45df2023-09-01 14:20:50 -0700358 if constexpr (IsTls<Adaptor>::value)
Ed Tanouse278c182019-03-13 16:23:37 -0700359 {
Ed Tanous1d1d7782024-04-09 12:54:08 -0700360 adaptor.async_shutdown(std::bind_front(
361 &self_type::tlsShutdownComplete, this, shared_from_this()));
Ed Tanouse278c182019-03-13 16:23:37 -0700362 }
363 else
364 {
Ed Tanous1d1d7782024-04-09 12:54:08 -0700365 hardClose();
Ed Tanouse278c182019-03-13 16:23:37 -0700366 }
367 }
368
Nan Zhou72374eb2022-01-27 17:06:51 -0800369 void completeRequest(crow::Response& thisRes)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700370 {
Nan Zhou72374eb2022-01-27 17:06:51 -0800371 res = std::move(thisRes);
Ed Tanous4cdc2e82023-01-13 10:03:22 -0800372 res.keepAlive(keepAlive);
Ed Tanous5ae6f922023-01-09 10:45:53 -0800373
Ed Tanous52e31622024-01-23 16:31:11 -0800374 completeResponseFields(req, res);
Ed Tanous998e0cb2023-09-06 13:57:30 -0700375 res.addHeader(boost::beast::http::field::date, getCachedDateStr());
Ed Tanous291d7092022-04-13 12:34:57 -0700376
Ed Tanous52e31622024-01-23 16:31:11 -0800377 doWrite();
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000378
379 // delete lambda with self shared_ptr
380 // to enable connection destruction
John Edward Broadbent4147b8a2021-07-19 16:52:24 -0700381 res.setCompleteRequestHandler(nullptr);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700382 }
383
Sunitha Harishc0ea7ae2020-10-30 02:37:30 -0500384 void readClientIp()
385 {
386 boost::system::error_code ec;
Sunitha Harishc0ea7ae2020-10-30 02:37:30 -0500387
Ed Tanous4fa45df2023-09-01 14:20:50 -0700388 if constexpr (!std::is_same_v<Adaptor, boost::beast::test::stream>)
Sunitha Harishc0ea7ae2020-10-30 02:37:30 -0500389 {
Ed Tanous4fa45df2023-09-01 14:20:50 -0700390 boost::asio::ip::tcp::endpoint endpoint =
391 boost::beast::get_lowest_layer(adaptor).remote_endpoint(ec);
392
393 if (ec)
394 {
395 // If remote endpoint fails keep going. "ClientOriginIPAddress"
396 // will be empty.
397 BMCWEB_LOG_ERROR(
398 "Failed to get the client's IP Address. ec : {}", ec);
Ed Tanous1d1d7782024-04-09 12:54:08 -0700399 return;
Ed Tanous4fa45df2023-09-01 14:20:50 -0700400 }
401 ip = endpoint.address();
Sunitha Harishc0ea7ae2020-10-30 02:37:30 -0500402 }
Sunitha Harishc0ea7ae2020-10-30 02:37:30 -0500403 }
404
Ed Tanous1abe55e2018-09-05 08:30:59 -0700405 private:
Ed Tanous1d1d7782024-04-09 12:54:08 -0700406 uint64_t getContentLengthLimit()
407 {
408#ifndef BMCWEB_INSECURE_DISABLE_AUTHX
409 if (userSession == nullptr)
410 {
411 return loggedOutPostBodyLimit;
412 }
413#endif
414
415 return httpReqBodyLimit;
416 }
417
418 // Returns true if content length was within limits
419 // Returns false if content length error has been returned
420 bool handleContentLengthError()
421 {
422 if (!parser)
423 {
424 BMCWEB_LOG_CRITICAL("Paser was null");
425 return false;
426 }
427 const boost::optional<uint64_t> contentLength =
428 parser->content_length();
429 if (!contentLength)
430 {
431 BMCWEB_LOG_DEBUG("{} No content length available", logPtr(this));
432 return true;
433 }
434
435 uint64_t maxAllowedContentLength = getContentLengthLimit();
436
437 if (*contentLength > maxAllowedContentLength)
438 {
439 // If the users content limit is between the logged in
440 // and logged out limits They probably just didn't log
441 // in
442 if (*contentLength > loggedOutPostBodyLimit &&
443 *contentLength < httpReqBodyLimit)
444 {
445 BMCWEB_LOG_DEBUG(
446 "{} Content length {} valid, but greater than logged out"
447 " limit of {}. Setting unauthorized",
448 logPtr(this), *contentLength, loggedOutPostBodyLimit);
449 res.result(boost::beast::http::status::unauthorized);
450 }
451 else
452 {
453 // Otherwise they're over both limits, so inform
454 // them
455 BMCWEB_LOG_DEBUG(
456 "{} Content length {} was greater than global limit {}."
457 " Setting payload too large",
458 logPtr(this), *contentLength, httpReqBodyLimit);
459 res.result(boost::beast::http::status::payload_too_large);
460 }
461
462 keepAlive = false;
463 doWrite();
464 return false;
465 }
466
467 return true;
468 }
469
Ed Tanous1abe55e2018-09-05 08:30:59 -0700470 void doReadHeaders()
471 {
Ed Tanous62598e32023-07-17 17:06:25 -0700472 BMCWEB_LOG_DEBUG("{} doReadHeaders", logPtr(this));
Ed Tanouse01d0c32023-06-30 13:21:32 -0700473 if (!parser)
474 {
Ed Tanous1d1d7782024-04-09 12:54:08 -0700475 BMCWEB_LOG_CRITICAL("Parser was not initialized.");
Ed Tanouse01d0c32023-06-30 13:21:32 -0700476 return;
477 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700478 // Clean up any previous Connection.
479 boost::beast::http::async_read_header(
Ed Tanousceac6f72018-12-02 11:58:47 -0800480 adaptor, buffer, *parser,
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000481 [this,
482 self(shared_from_this())](const boost::system::error_code& ec,
Ed Tanous81ce6092020-12-17 16:54:55 +0000483 std::size_t bytesTransferred) {
Ed Tanous62598e32023-07-17 17:06:25 -0700484 BMCWEB_LOG_DEBUG("{} async_read_header {} Bytes", logPtr(this),
485 bytesTransferred);
Ed Tanous52e31622024-01-23 16:31:11 -0800486
Ed Tanous002d39b2022-05-31 08:59:27 -0700487 if (ec)
488 {
Ed Tanous52e31622024-01-23 16:31:11 -0800489 cancelDeadlineTimer();
490
Ed Tanous1d1d7782024-04-09 12:54:08 -0700491 if (ec == boost::beast::http::error::header_limit)
492 {
493 BMCWEB_LOG_ERROR("{} Header field too large, closing",
494 logPtr(this), ec.message());
495
496 res.result(boost::beast::http::status::
497 request_header_fields_too_large);
498 keepAlive = false;
499 doWrite();
500 return;
501 }
Myung Baea4326fe2023-01-10 14:29:24 -0600502 if (ec == boost::beast::http::error::end_of_stream)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700503 {
Ed Tanous1d1d7782024-04-09 12:54:08 -0700504 BMCWEB_LOG_WARNING("{} End of stream, closing {}",
505 logPtr(this), ec);
506 hardClose();
507 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700508 }
Ed Tanous1d1d7782024-04-09 12:54:08 -0700509
510 BMCWEB_LOG_DEBUG("{} Closing socket due to read error {}",
511 logPtr(this), ec.message());
512 gracefulClose();
513
Ed Tanous002d39b2022-05-31 08:59:27 -0700514 return;
515 }
516
Ed Tanous1d1d7782024-04-09 12:54:08 -0700517 std::string_view expect =
518 req.getHeaderValue(boost::beast::http::field::expect);
519 if (bmcweb::asciiIEquals(expect, "100-continue"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700520 {
Ed Tanous1d1d7782024-04-09 12:54:08 -0700521 res.result(boost::beast::http::status::continue_);
522 doWrite();
523 return;
Ed Tanous002d39b2022-05-31 08:59:27 -0700524 }
Ed Tanous1d1d7782024-04-09 12:54:08 -0700525
Ed Tanous4fa45df2023-09-01 14:20:50 -0700526 if constexpr (!std::is_same_v<Adaptor, boost::beast::test::stream>)
Ed Tanous002d39b2022-05-31 08:59:27 -0700527 {
Ed Tanous4fa45df2023-09-01 14:20:50 -0700528#ifndef BMCWEB_INSECURE_DISABLE_AUTHX
529 boost::beast::http::verb method = parser->get().method();
530 userSession = crow::authentication::authenticate(
531 ip, res, method, parser->get().base(), mtlsSession);
Nan Zhoua43ea822022-05-27 00:42:44 +0000532#endif // BMCWEB_INSECURE_DISABLE_AUTHX
Ed Tanous4fa45df2023-09-01 14:20:50 -0700533 }
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800534
Ed Tanous1d1d7782024-04-09 12:54:08 -0700535 if (!handleContentLengthError())
536 {
537 return;
538 }
539
540 parser->body_limit(getContentLengthLimit());
541
Ed Tanous7d243eb2023-01-23 15:57:41 -0800542 if (parser->is_done())
543 {
544 handle();
545 return;
546 }
547
Ed Tanous002d39b2022-05-31 08:59:27 -0700548 doRead();
Patrick Williams5a39f772023-10-20 11:20:21 -0500549 });
Ed Tanous1abe55e2018-09-05 08:30:59 -0700550 }
551
552 void doRead()
553 {
Ed Tanous62598e32023-07-17 17:06:25 -0700554 BMCWEB_LOG_DEBUG("{} doRead", logPtr(this));
Ed Tanouse01d0c32023-06-30 13:21:32 -0700555 if (!parser)
556 {
557 return;
558 }
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800559 startDeadline();
Ed Tanous7d243eb2023-01-23 15:57:41 -0800560 boost::beast::http::async_read_some(
561 adaptor, buffer, *parser,
562 [this,
563 self(shared_from_this())](const boost::system::error_code& ec,
564 std::size_t bytesTransferred) {
Ed Tanous62598e32023-07-17 17:06:25 -0700565 BMCWEB_LOG_DEBUG("{} async_read_some {} Bytes", logPtr(this),
566 bytesTransferred);
Ed Tanous7d243eb2023-01-23 15:57:41 -0800567
Ed Tanous002d39b2022-05-31 08:59:27 -0700568 if (ec)
569 {
Ed Tanous62598e32023-07-17 17:06:25 -0700570 BMCWEB_LOG_ERROR("{} Error while reading: {}", logPtr(this),
571 ec.message());
Ed Tanous1d1d7782024-04-09 12:54:08 -0700572 if (ec == boost::beast::http::error::body_limit)
573 {
574 if (handleContentLengthError())
575 {
576 BMCWEB_LOG_CRITICAL("Body length limit reached, "
577 "but no content-length "
578 "available? Should never happen");
579 res.result(
580 boost::beast::http::status::internal_server_error);
581 keepAlive = false;
582 doWrite();
583 }
584 return;
585 }
586
587 gracefulClose();
Ed Tanous002d39b2022-05-31 08:59:27 -0700588 return;
589 }
Ed Tanous7d243eb2023-01-23 15:57:41 -0800590
591 // If the user is logged in, allow them to send files incrementally
592 // one piece at a time. If authentication is disabled then there is
593 // no user session hence always allow to send one piece at a time.
594 if (userSession != nullptr)
595 {
596 cancelDeadlineTimer();
597 }
598 if (!parser->is_done())
599 {
600 doRead();
601 return;
602 }
603
604 cancelDeadlineTimer();
Ed Tanous002d39b2022-05-31 08:59:27 -0700605 handle();
Patrick Williams5a39f772023-10-20 11:20:21 -0500606 });
Ed Tanous1abe55e2018-09-05 08:30:59 -0700607 }
608
Ed Tanous27b0cf92023-08-07 12:02:40 -0700609 void afterDoWrite(const std::shared_ptr<self_type>& /*self*/,
610 const boost::system::error_code& ec,
611 std::size_t bytesTransferred)
612 {
Ed Tanous1d1d7782024-04-09 12:54:08 -0700613 BMCWEB_LOG_DEBUG("{} async_write wrote {} bytes, ec=", logPtr(this),
614 bytesTransferred, ec);
Ed Tanous27b0cf92023-08-07 12:02:40 -0700615
616 cancelDeadlineTimer();
617
618 if (ec)
619 {
620 BMCWEB_LOG_DEBUG("{} from write(2)", logPtr(this));
621 return;
622 }
Ed Tanous1d1d7782024-04-09 12:54:08 -0700623
624 if (res.result() == boost::beast::http::status::continue_)
625 {
626 // Reset the result to ok
627 res.result(boost::beast::http::status::ok);
628 doRead();
629 return;
630 }
631
Ed Tanous27b0cf92023-08-07 12:02:40 -0700632 if (!keepAlive)
633 {
Ed Tanous1d1d7782024-04-09 12:54:08 -0700634 BMCWEB_LOG_DEBUG("{} keepalive not set. Closing socket",
635 logPtr(this));
636
637 gracefulClose();
Ed Tanous27b0cf92023-08-07 12:02:40 -0700638 return;
639 }
640
641 BMCWEB_LOG_DEBUG("{} Clearing response", logPtr(this));
642 res.clear();
Ed Tanous1d1d7782024-04-09 12:54:08 -0700643 initParser();
Ed Tanous27b0cf92023-08-07 12:02:40 -0700644
645 userSession = nullptr;
646
647 // Destroy the Request via the std::optional
Ed Tanous52e31622024-01-23 16:31:11 -0800648 req.clear();
Ed Tanous27b0cf92023-08-07 12:02:40 -0700649 doReadHeaders();
650 }
651
Ed Tanous52e31622024-01-23 16:31:11 -0800652 void doWrite()
Ed Tanous1abe55e2018-09-05 08:30:59 -0700653 {
Ed Tanous62598e32023-07-17 17:06:25 -0700654 BMCWEB_LOG_DEBUG("{} doWrite", logPtr(this));
Ed Tanous52e31622024-01-23 16:31:11 -0800655 res.preparePayload();
Ed Tanous27b0cf92023-08-07 12:02:40 -0700656
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800657 startDeadline();
Ed Tanous4d698612024-02-06 14:57:24 -0800658 boost::beast::async_write(
659 adaptor,
660 boost::beast::http::message_generator(std::move(res.response)),
Ed Tanous52e31622024-01-23 16:31:11 -0800661 std::bind_front(&self_type::afterDoWrite, this,
662 shared_from_this()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700663 }
664
Ed Tanous1abe55e2018-09-05 08:30:59 -0700665 void cancelDeadlineTimer()
666 {
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800667 timer.cancel();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700668 }
669
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800670 void startDeadline()
Ed Tanous1abe55e2018-09-05 08:30:59 -0700671 {
Ed Tanous7d243eb2023-01-23 15:57:41 -0800672 // Timer is already started so no further action is required.
673 if (timerStarted)
674 {
675 return;
676 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700677
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800678 std::chrono::seconds timeout(15);
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800679
680 std::weak_ptr<Connection<Adaptor, Handler>> weakSelf = weak_from_this();
681 timer.expires_after(timeout);
Ed Tanous81c4e332023-05-18 10:30:34 -0700682 timer.async_wait([weakSelf](const boost::system::error_code& ec) {
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800683 // Note, we are ignoring other types of errors here; If the timer
684 // failed for any reason, we should still close the connection
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800685 std::shared_ptr<Connection<Adaptor, Handler>> self =
686 weakSelf.lock();
687 if (!self)
688 {
Ed Tanous1d1d7782024-04-09 12:54:08 -0700689 if (ec == boost::asio::error::operation_aborted)
690 {
691 BMCWEB_LOG_DEBUG(
692 "{} Timer canceled on connection being destroyed",
693 logPtr(self.get()));
694 return;
695 }
Ed Tanous62598e32023-07-17 17:06:25 -0700696 BMCWEB_LOG_CRITICAL("{} Failed to capture connection",
697 logPtr(self.get()));
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800698 return;
699 }
Ed Tanous7d243eb2023-01-23 15:57:41 -0800700
701 self->timerStarted = false;
702
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800703 if (ec)
704 {
Ed Tanous1d1d7782024-04-09 12:54:08 -0700705 if (ec == boost::asio::error::operation_aborted)
706 {
707 BMCWEB_LOG_DEBUG("{} Timer canceled", logPtr(self.get()));
708 return;
709 }
710 BMCWEB_LOG_CRITICAL("{} Timer failed {}", logPtr(self.get()),
Ed Tanous62598e32023-07-17 17:06:25 -0700711 ec);
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800712 }
713
Ed Tanous1d1d7782024-04-09 12:54:08 -0700714 BMCWEB_LOG_WARNING("{} Connection timed out, hard closing",
Ed Tanous62598e32023-07-17 17:06:25 -0700715 logPtr(self.get()));
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800716
Ed Tanous1d1d7782024-04-09 12:54:08 -0700717 self->hardClose();
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800718 });
719
Ed Tanous7d243eb2023-01-23 15:57:41 -0800720 timerStarted = true;
Ed Tanous62598e32023-07-17 17:06:25 -0700721 BMCWEB_LOG_DEBUG("{} timer started", logPtr(this));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700722 }
723
Ed Tanous1abe55e2018-09-05 08:30:59 -0700724 Adaptor adaptor;
725 Handler* handler;
Ed Tanous1d1d7782024-04-09 12:54:08 -0700726
727 boost::asio::ip::address ip;
728
Ed Tanousa24526d2018-12-10 15:17:59 -0800729 // Making this a std::optional allows it to be efficiently destroyed and
Ed Tanous1abe55e2018-09-05 08:30:59 -0700730 // re-created on Connection reset
Ed Tanousb2896142024-01-31 15:25:47 -0800731 std::optional<boost::beast::http::request_parser<bmcweb::HttpBody>> parser;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700732
Ed Tanous3112a142018-11-29 15:45:10 -0800733 boost::beast::flat_static_buffer<8192> buffer;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700734
Ed Tanous52e31622024-01-23 16:31:11 -0800735 crow::Request req;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700736 crow::Response res;
Ed Tanous52cc1122020-07-18 13:51:21 -0700737
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700738 std::shared_ptr<persistent_data::UserSession> userSession;
Boleslaw Ogonczyk Makowskib4963072023-02-06 09:59:58 +0100739 std::shared_ptr<persistent_data::UserSession> mtlsSession;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700740
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800741 boost::asio::steady_timer timer;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700742
Ed Tanous4cdc2e82023-01-13 10:03:22 -0800743 bool keepAlive = true;
744
Ed Tanous7d243eb2023-01-23 15:57:41 -0800745 bool timerStarted = false;
746
Ed Tanous1abe55e2018-09-05 08:30:59 -0700747 std::function<std::string()>& getCachedDateStr;
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000748
749 using std::enable_shared_from_this<
Ed Tanous52cc1122020-07-18 13:51:21 -0700750 Connection<Adaptor, Handler>>::shared_from_this;
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800751
752 using std::enable_shared_from_this<
753 Connection<Adaptor, Handler>>::weak_from_this;
Ed Tanous3112a142018-11-29 15:45:10 -0800754};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700755} // namespace crow