blob: 7d24fe7485007275306a3ee1bd720a3ff266081d [file] [log] [blame]
Ed Tanous7045c8d2017-04-03 10:04:37 -07001#pragma once
Adriana Kobylak0e1cf262019-12-05 13:57:57 -06002#include "config.h"
3
Ed Tanous1abe55e2018-09-05 08:30:59 -07004#include "http_utility.hpp"
5
Ed Tanous7045c8d2017-04-03 10:04:37 -07006#include <atomic>
Ed Tanouse0d918b2018-03-27 17:41:04 -07007#include <boost/algorithm/string.hpp>
Ed Tanous257f5792018-03-17 14:40:09 -07008#include <boost/algorithm/string/predicate.hpp>
Ed Tanous8f626352018-12-19 14:51:54 -08009#include <boost/asio/io_context.hpp>
Ed Tanous3112a142018-11-29 15:45:10 -080010#include <boost/asio/ip/tcp.hpp>
Ed Tanous2f1ebcd2019-02-13 19:39:07 -080011#include <boost/asio/ssl.hpp>
Ed Tanous3112a142018-11-29 15:45:10 -080012#include <boost/beast/core/flat_static_buffer.hpp>
Ed Tanouse278c182019-03-13 16:23:37 -070013#if BOOST_VERSION >= 107000
14#include <boost/beast/ssl/ssl_stream.hpp>
15#else
Ed Tanous2f1ebcd2019-02-13 19:39:07 -080016#include <boost/beast/experimental/core/ssl_stream.hpp>
Ed Tanouse278c182019-03-13 16:23:37 -070017#endif
Ed Tanouse0d918b2018-03-27 17:41:04 -070018#include <boost/beast/http.hpp>
19#include <boost/beast/websocket.hpp>
Ed Tanous1abe55e2018-09-05 08:30:59 -070020#include <chrono>
Ed Tanous1abe55e2018-09-05 08:30:59 -070021#include <vector>
22
Ed Tanousc94ad492019-10-10 15:39:33 -070023#include "http_response.h"
24#include "logging.h"
25#include "middleware_context.h"
26#include "timer_queue.h"
27#include "utility.h"
Ed Tanous7045c8d2017-04-03 10:04:37 -070028
Ed Tanous1abe55e2018-09-05 08:30:59 -070029namespace crow
30{
Ed Tanous257f5792018-03-17 14:40:09 -070031
Ed Tanous1abe55e2018-09-05 08:30:59 -070032inline void prettyPrintJson(crow::Response& res)
33{
Jason M. Bills193ad2f2018-09-26 15:08:52 -070034 std::string value = res.jsonValue.dump(4, ' ', true);
Ed Tanousa29c9972018-11-29 15:54:32 -080035 utility::escapeHtml(value);
36 utility::convertToLinks(value);
Ed Tanous1abe55e2018-09-05 08:30:59 -070037 res.body() = "<html>\n"
38 "<head>\n"
39 "<title>Redfish API</title>\n"
40 "<link rel=\"stylesheet\" type=\"text/css\" "
41 "href=\"/styles/default.css\">\n"
42 "<script src=\"/highlight.pack.js\"></script>"
43 "<script>hljs.initHighlightingOnLoad();</script>"
44 "</head>\n"
45 "<body>\n"
46 "<div style=\"max-width: 576px;margin:0 auto;\">\n"
47 "<img src=\"/DMTF_Redfish_logo_2017.svg\" alt=\"redfish\" "
48 "height=\"406px\" "
49 "width=\"576px\">\n"
50 "<br>\n"
51 "<pre>\n"
52 "<code class=\"json\">" +
53 value +
54 "</code>\n"
55 "</pre>\n"
56 "</div>\n"
57 "</body>\n"
58 "</html>\n";
Ed Tanous93ef5802019-01-03 10:15:41 -080059 res.addHeader("Content-Type", "text/html;charset=UTF-8");
Ed Tanous257f5792018-03-17 14:40:09 -070060}
61
Ed Tanous7045c8d2017-04-03 10:04:37 -070062using namespace boost;
63using tcp = asio::ip::tcp;
64
Ed Tanous1abe55e2018-09-05 08:30:59 -070065namespace detail
66{
67template <typename MW> struct CheckBeforeHandleArity3Const
68{
69 template <typename T,
70 void (T::*)(Request&, Response&, typename MW::Context&) const =
71 &T::beforeHandle>
72 struct Get
73 {
74 };
Ed Tanous7045c8d2017-04-03 10:04:37 -070075};
76
Ed Tanous1abe55e2018-09-05 08:30:59 -070077template <typename MW> struct CheckBeforeHandleArity3
78{
79 template <typename T, void (T::*)(Request&, Response&,
80 typename MW::Context&) = &T::beforeHandle>
81 struct Get
82 {
83 };
Ed Tanous7045c8d2017-04-03 10:04:37 -070084};
85
Ed Tanous1abe55e2018-09-05 08:30:59 -070086template <typename MW> struct CheckAfterHandleArity3Const
87{
88 template <typename T,
89 void (T::*)(Request&, Response&, typename MW::Context&) const =
90 &T::afterHandle>
91 struct Get
92 {
93 };
Ed Tanous7045c8d2017-04-03 10:04:37 -070094};
95
Ed Tanous1abe55e2018-09-05 08:30:59 -070096template <typename MW> struct CheckAfterHandleArity3
97{
98 template <typename T, void (T::*)(Request&, Response&,
99 typename MW::Context&) = &T::afterHandle>
100 struct Get
101 {
102 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700103};
104
Ed Tanous1abe55e2018-09-05 08:30:59 -0700105template <typename T> struct IsBeforeHandleArity3Impl
106{
107 template <typename C>
108 static std::true_type
109 f(typename CheckBeforeHandleArity3Const<T>::template Get<C>*);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700110
Ed Tanous1abe55e2018-09-05 08:30:59 -0700111 template <typename C>
112 static std::true_type
113 f(typename CheckBeforeHandleArity3<T>::template Get<C>*);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700114
Ed Tanous1abe55e2018-09-05 08:30:59 -0700115 template <typename C> static std::false_type f(...);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700116
Ed Tanous1abe55e2018-09-05 08:30:59 -0700117 public:
Ed Tanous0c838cf2019-10-24 10:01:46 -0700118 static constexpr bool value = decltype(f<T>(nullptr))::value;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700119};
120
Ed Tanous1abe55e2018-09-05 08:30:59 -0700121template <typename T> struct IsAfterHandleArity3Impl
122{
123 template <typename C>
124 static std::true_type
125 f(typename CheckAfterHandleArity3Const<T>::template Get<C>*);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700126
Ed Tanous1abe55e2018-09-05 08:30:59 -0700127 template <typename C>
128 static std::true_type
129 f(typename CheckAfterHandleArity3<T>::template Get<C>*);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700130
Ed Tanous1abe55e2018-09-05 08:30:59 -0700131 template <typename C> static std::false_type f(...);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700132
Ed Tanous1abe55e2018-09-05 08:30:59 -0700133 public:
Ed Tanous0c838cf2019-10-24 10:01:46 -0700134 static constexpr bool value = decltype(f<T>(nullptr))::value;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700135};
136
137template <typename MW, typename Context, typename ParentContext>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700138typename std::enable_if<!IsBeforeHandleArity3Impl<MW>::value>::type
Ed Tanous1abe55e2018-09-05 08:30:59 -0700139 beforeHandlerCall(MW& mw, Request& req, Response& res, Context& ctx,
140 ParentContext& /*parent_ctx*/)
141{
142 mw.beforeHandle(req, res, ctx.template get<MW>(), ctx);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700143}
144
145template <typename MW, typename Context, typename ParentContext>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700146typename std::enable_if<IsBeforeHandleArity3Impl<MW>::value>::type
Ed Tanous1abe55e2018-09-05 08:30:59 -0700147 beforeHandlerCall(MW& mw, Request& req, Response& res, Context& ctx,
148 ParentContext& /*parent_ctx*/)
149{
150 mw.beforeHandle(req, res, ctx.template get<MW>());
Ed Tanous7045c8d2017-04-03 10:04:37 -0700151}
152
153template <typename MW, typename Context, typename ParentContext>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700154typename std::enable_if<!IsAfterHandleArity3Impl<MW>::value>::type
Ed Tanous1abe55e2018-09-05 08:30:59 -0700155 afterHandlerCall(MW& mw, Request& req, Response& res, Context& ctx,
156 ParentContext& /*parent_ctx*/)
157{
158 mw.afterHandle(req, res, ctx.template get<MW>(), ctx);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700159}
160
161template <typename MW, typename Context, typename ParentContext>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700162typename std::enable_if<IsAfterHandleArity3Impl<MW>::value>::type
Ed Tanous1abe55e2018-09-05 08:30:59 -0700163 afterHandlerCall(MW& mw, Request& req, Response& res, Context& ctx,
164 ParentContext& /*parent_ctx*/)
165{
166 mw.afterHandle(req, res, ctx.template get<MW>());
Ed Tanous7045c8d2017-04-03 10:04:37 -0700167}
168
Ed Tanous271584a2019-07-09 16:24:22 -0700169template <size_t N, typename Context, typename Container, typename CurrentMW,
Ed Tanous7045c8d2017-04-03 10:04:37 -0700170 typename... Middlewares>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700171bool middlewareCallHelper(Container& middlewares, Request& req, Response& res,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700172 Context& ctx)
173{
174 using parent_context_t = typename Context::template partial<N - 1>;
175 beforeHandlerCall<CurrentMW, Context, parent_context_t>(
Ed Tanous7045c8d2017-04-03 10:04:37 -0700176 std::get<N>(middlewares), req, res, ctx,
177 static_cast<parent_context_t&>(ctx));
Ed Tanous7045c8d2017-04-03 10:04:37 -0700178
Ed Tanous1abe55e2018-09-05 08:30:59 -0700179 if (res.isCompleted())
180 {
181 afterHandlerCall<CurrentMW, Context, parent_context_t>(
182 std::get<N>(middlewares), req, res, ctx,
183 static_cast<parent_context_t&>(ctx));
184 return true;
185 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700186
Ed Tanous1abe55e2018-09-05 08:30:59 -0700187 if (middlewareCallHelper<N + 1, Context, Container, Middlewares...>(
188 middlewares, req, res, ctx))
189 {
190 afterHandlerCall<CurrentMW, Context, parent_context_t>(
191 std::get<N>(middlewares), req, res, ctx,
192 static_cast<parent_context_t&>(ctx));
193 return true;
194 }
195
196 return false;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700197}
198
Ed Tanous271584a2019-07-09 16:24:22 -0700199template <size_t N, typename Context, typename Container>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700200bool middlewareCallHelper(Container& /*middlewares*/, Request& /*req*/,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700201 Response& /*res*/, Context& /*ctx*/)
202{
203 return false;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700204}
205
Ed Tanous271584a2019-07-09 16:24:22 -0700206template <size_t N, typename Context, typename Container>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700207typename std::enable_if<(N < 0)>::type
208 afterHandlersCallHelper(Container& /*middlewares*/, Context& /*Context*/,
209 Request& /*req*/, Response& /*res*/)
210{
Ed Tanous7045c8d2017-04-03 10:04:37 -0700211}
212
Ed Tanous271584a2019-07-09 16:24:22 -0700213template <size_t N, typename Context, typename Container>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700214typename std::enable_if<(N == 0)>::type
215 afterHandlersCallHelper(Container& middlewares, Context& ctx, Request& req,
216 Response& res)
217{
218 using parent_context_t = typename Context::template partial<N - 1>;
219 using CurrentMW = typename std::tuple_element<
220 N, typename std::remove_reference<Container>::type>::type;
221 afterHandlerCall<CurrentMW, Context, parent_context_t>(
222 std::get<N>(middlewares), req, res, ctx,
223 static_cast<parent_context_t&>(ctx));
Ed Tanous7045c8d2017-04-03 10:04:37 -0700224}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700225
Ed Tanous271584a2019-07-09 16:24:22 -0700226template <size_t N, typename Context, typename Container>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700227typename std::enable_if<(N > 0)>::type
228 afterHandlersCallHelper(Container& middlewares, Context& ctx, Request& req,
229 Response& res)
230{
231 using parent_context_t = typename Context::template partial<N - 1>;
232 using CurrentMW = typename std::tuple_element<
233 N, typename std::remove_reference<Container>::type>::type;
234 afterHandlerCall<CurrentMW, Context, parent_context_t>(
235 std::get<N>(middlewares), req, res, ctx,
236 static_cast<parent_context_t&>(ctx));
237 afterHandlersCallHelper<N - 1, Context, Container>(middlewares, ctx, req,
238 res);
239}
240} // namespace detail
Ed Tanous7045c8d2017-04-03 10:04:37 -0700241
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700242#ifdef BMCWEB_ENABLE_DEBUG
Ed Tanouse0d918b2018-03-27 17:41:04 -0700243static std::atomic<int> connectionCount;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700244#endif
Jennifer Leeacb7cfb2018-06-07 16:08:15 -0700245
Adriana Kobylak0e1cf262019-12-05 13:57:57 -0600246// request body limit size set by the BMCWEB_HTTP_REQ_BODY_LIMIT_MB option
247constexpr unsigned int httpReqBodyLimit =
248 1024 * 1024 * BMCWEB_HTTP_REQ_BODY_LIMIT_MB;
Jennifer Leeacb7cfb2018-06-07 16:08:15 -0700249
Ed Tanous7045c8d2017-04-03 10:04:37 -0700250template <typename Adaptor, typename Handler, typename... Middlewares>
James Feista8086642020-01-07 09:53:20 -0800251class Connection
Ed Tanous1abe55e2018-09-05 08:30:59 -0700252{
253 public:
Ed Tanous271584a2019-07-09 16:24:22 -0700254 Connection(boost::asio::io_context& ioService, Handler* handlerIn,
255 const std::string& ServerNameIn,
256 std::tuple<Middlewares...>* middlewaresIn,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700257 std::function<std::string()>& get_cached_date_str_f,
Ed Tanous271584a2019-07-09 16:24:22 -0700258 detail::TimerQueue& timerQueueIn, Adaptor adaptorIn) :
Ed Tanousceac6f72018-12-02 11:58:47 -0800259 adaptor(std::move(adaptorIn)),
Ed Tanous271584a2019-07-09 16:24:22 -0700260 handler(handlerIn), serverName(ServerNameIn),
261 middlewares(middlewaresIn), getCachedDateStr(get_cached_date_str_f),
262 timerQueue(timerQueueIn)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700263 {
264 parser.emplace(std::piecewise_construct, std::make_tuple());
Adriana Kobylak0e1cf262019-12-05 13:57:57 -0600265 // Temporarily set by the BMCWEB_HTTP_REQ_BODY_LIMIT_MB variable; Need
266 // to modify uploading/authentication mechanism to a better method that
267 // disallows a DOS attack based on a large file size.
Ed Tanous1abe55e2018-09-05 08:30:59 -0700268 parser->body_limit(httpReqBodyLimit);
269 req.emplace(parser->get());
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200270
271#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100272 auto ca_available = !std::filesystem::is_empty(
273 std::filesystem::path(ensuressl::trustStorePath));
274 if (ca_available && crow::persistent_data::SessionStore::getInstance()
275 .getAuthMethodsConfig()
276 .tls)
277 {
278 adaptor.set_verify_mode(boost::asio::ssl::verify_peer);
279 SSL_set_session_id_context(
280 adaptor.native_handle(),
281 reinterpret_cast<const unsigned char*>(serverName.c_str()),
Zbigniew Kurzynskicac94c52019-11-07 12:55:04 +0100282 static_cast<unsigned int>(serverName.length()));
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100283 BMCWEB_LOG_DEBUG << this << " TLS is enabled on this connection.";
284 }
285
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100286 adaptor.set_verify_callback([this](
287 bool preverified,
288 boost::asio::ssl::verify_context& ctx) {
289 // do nothing if TLS is disabled
290 if (!crow::persistent_data::SessionStore::getInstance()
291 .getAuthMethodsConfig()
292 .tls)
293 {
294 BMCWEB_LOG_DEBUG << this << " TLS auth_config is disabled";
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200295 return true;
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100296 }
297
298 // We always return true to allow full auth flow
299 if (!preverified)
300 {
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100301 BMCWEB_LOG_DEBUG << this << " TLS preverification failed.";
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100302 return true;
303 }
304
305 X509_STORE_CTX* cts = ctx.native_handle();
306 if (cts == nullptr)
307 {
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100308 BMCWEB_LOG_DEBUG << this << " Cannot get native TLS handle.";
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100309 return true;
310 }
311
312 // Get certificate
313 X509* peerCert =
314 X509_STORE_CTX_get_current_cert(ctx.native_handle());
315 if (peerCert == nullptr)
316 {
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100317 BMCWEB_LOG_DEBUG << this
318 << " Cannot get current TLS certificate.";
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100319 return true;
320 }
321
322 // Check if certificate is OK
323 int error = X509_STORE_CTX_get_error(cts);
324 if (error != X509_V_OK)
325 {
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100326 BMCWEB_LOG_INFO << this << " Last TLS error is: " << error;
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100327 return true;
328 }
329 // Check that we have reached final certificate in chain
330 int32_t depth = X509_STORE_CTX_get_error_depth(cts);
331 if (depth != 0)
332
333 {
334 BMCWEB_LOG_DEBUG
335 << this << " Certificate verification in progress (depth "
336 << depth << "), waiting to reach final depth";
337 return true;
338 }
339
340 BMCWEB_LOG_DEBUG << this
341 << " Certificate verification of final depth";
342
343 // Verify KeyUsage
344 bool isKeyUsageDigitalSignature = false;
345 bool isKeyUsageKeyAgreement = false;
346
347 ASN1_BIT_STRING* usage = static_cast<ASN1_BIT_STRING*>(
348 X509_get_ext_d2i(peerCert, NID_key_usage, NULL, NULL));
349
350 if (usage == nullptr)
351 {
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100352 BMCWEB_LOG_DEBUG << this << " TLS usage is null";
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100353 return true;
354 }
355
356 for (int i = 0; i < usage->length; i++)
357 {
358 if (KU_DIGITAL_SIGNATURE & usage->data[i])
359 {
360 isKeyUsageDigitalSignature = true;
361 }
362 if (KU_KEY_AGREEMENT & usage->data[i])
363 {
364 isKeyUsageKeyAgreement = true;
365 }
366 }
367
368 if (!isKeyUsageDigitalSignature || !isKeyUsageKeyAgreement)
369 {
370 BMCWEB_LOG_DEBUG << this
371 << " Certificate ExtendedKeyUsage does "
372 "not allow provided certificate to "
373 "be used for user authentication";
374 return true;
375 }
376
377 // Determine that ExtendedKeyUsage includes Client Auth
378
379 stack_st_ASN1_OBJECT* extUsage = static_cast<stack_st_ASN1_OBJECT*>(
380 X509_get_ext_d2i(peerCert, NID_ext_key_usage, NULL, NULL));
381
382 if (extUsage == nullptr)
383 {
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100384 BMCWEB_LOG_DEBUG << this << " TLS extUsage is null";
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100385 return true;
386 }
387
388 bool isExKeyUsageClientAuth = false;
389 for (int i = 0; i < sk_ASN1_OBJECT_num(extUsage); i++)
390 {
391 if (NID_client_auth ==
392 OBJ_obj2nid(sk_ASN1_OBJECT_value(extUsage, i)))
393 {
394 isExKeyUsageClientAuth = true;
395 break;
396 }
397 }
398
399 // Certificate has to have proper key usages set
400 if (!isExKeyUsageClientAuth)
401 {
402 BMCWEB_LOG_DEBUG << this
403 << " Certificate ExtendedKeyUsage does "
404 "not allow provided certificate to "
405 "be used for user authentication";
406 return true;
407 }
408 std::string sslUser;
409 // Extract username contained in CommonName
410 sslUser.resize(256, '\0');
411
412 int status = X509_NAME_get_text_by_NID(
413 X509_get_subject_name(peerCert), NID_commonName, sslUser.data(),
414 static_cast<int>(sslUser.size()));
415
416 if (status == -1)
417 {
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100418 BMCWEB_LOG_DEBUG
419 << this << " TLS cannot get username to create session";
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100420 return true;
421 }
422
423 size_t lastChar = sslUser.find('\0');
424 if (lastChar == std::string::npos || lastChar == 0)
425 {
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100426 BMCWEB_LOG_DEBUG << this << " Invalid TLS user name";
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100427 return true;
428 }
429 sslUser.resize(lastChar);
430
431 session = persistent_data::SessionStore::getInstance()
432 .generateUserSession(
433 sslUser,
434 crow::persistent_data::PersistenceType::TIMEOUT);
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100435 if (auto sp = session.lock())
436 {
437 BMCWEB_LOG_DEBUG << this
438 << " Generating TLS session: " << sp->uniqueId;
439 }
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100440 return true;
441 });
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200442#endif // BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
443
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700444#ifdef BMCWEB_ENABLE_DEBUG
Ed Tanous1abe55e2018-09-05 08:30:59 -0700445 connectionCount++;
446 BMCWEB_LOG_DEBUG << this << " Connection open, total "
447 << connectionCount;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700448#endif
Ed Tanous1abe55e2018-09-05 08:30:59 -0700449 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700450
Ed Tanous1abe55e2018-09-05 08:30:59 -0700451 ~Connection()
452 {
453 res.completeRequestHandler = nullptr;
454 cancelDeadlineTimer();
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700455#ifdef BMCWEB_ENABLE_DEBUG
Ed Tanous1abe55e2018-09-05 08:30:59 -0700456 connectionCount--;
457 BMCWEB_LOG_DEBUG << this << " Connection closed, total "
458 << connectionCount;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700459#endif
Ed Tanous7045c8d2017-04-03 10:04:37 -0700460 }
461
Ed Tanousceac6f72018-12-02 11:58:47 -0800462 Adaptor& socket()
Ed Tanous1abe55e2018-09-05 08:30:59 -0700463 {
Ed Tanousceac6f72018-12-02 11:58:47 -0800464 return adaptor;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700465 }
466
Ed Tanous1abe55e2018-09-05 08:30:59 -0700467 void start()
468 {
Ed Tanous7045c8d2017-04-03 10:04:37 -0700469
Ed Tanousceac6f72018-12-02 11:58:47 -0800470 startDeadline();
471 // TODO(ed) Abstract this to a more clever class with the idea of an
472 // asynchronous "start"
473 if constexpr (std::is_same_v<Adaptor,
474 boost::beast::ssl_stream<
475 boost::asio::ip::tcp::socket>>)
476 {
James Feista8086642020-01-07 09:53:20 -0800477 adaptor.async_handshake(
478 boost::asio::ssl::stream_base::server,
479 [this](const boost::system::error_code& ec) {
480 if (ec)
481 {
482 checkDestroy();
483 return;
484 }
485 doReadHeaders();
486 });
Ed Tanousceac6f72018-12-02 11:58:47 -0800487 }
488 else
489 {
490 doReadHeaders();
491 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700492 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700493
Ed Tanous1abe55e2018-09-05 08:30:59 -0700494 void handle()
495 {
496 cancelDeadlineTimer();
497 bool isInvalidRequest = false;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700498
Ed Tanous1abe55e2018-09-05 08:30:59 -0700499 // Check for HTTP version 1.1.
500 if (req->version() == 11)
501 {
502 if (req->getHeaderValue(boost::beast::http::field::host).empty())
503 {
504 isInvalidRequest = true;
Ed Tanousde5c9f32019-03-26 09:17:55 -0700505 res.result(boost::beast::http::status::bad_request);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700506 }
507 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700508
Ed Tanouse278c182019-03-13 16:23:37 -0700509 BMCWEB_LOG_INFO << "Request: "
510 << " " << this << " HTTP/" << req->version() / 10 << "."
511 << req->version() % 10 << ' ' << req->methodString()
512 << " " << req->target();
Ed Tanous7045c8d2017-04-03 10:04:37 -0700513
Ed Tanous1abe55e2018-09-05 08:30:59 -0700514 needToCallAfterHandlers = false;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700515
Ed Tanous1abe55e2018-09-05 08:30:59 -0700516 if (!isInvalidRequest)
517 {
518 res.completeRequestHandler = [] {};
Ed Tanouse278c182019-03-13 16:23:37 -0700519 res.isAliveHelper = [this]() -> bool { return isAlive(); };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700520
Ed Tanous1abe55e2018-09-05 08:30:59 -0700521 ctx = detail::Context<Middlewares...>();
Ed Tanouse278c182019-03-13 16:23:37 -0700522 req->middlewareContext = static_cast<void*>(&ctx);
523 req->ioService = static_cast<decltype(req->ioService)>(
524 &adaptor.get_executor().context());
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200525
526#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
527 if (auto sp = session.lock())
528 {
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100529 // set cookie only if this is req from the browser.
530 if (req->getHeaderValue("User-Agent").empty())
531 {
532 BMCWEB_LOG_DEBUG << this << " TLS session: " << sp->uniqueId
533 << " will be used for this request.";
534 req->session = sp;
535 }
536 else
537 {
538 std::string_view cookieValue =
539 req->getHeaderValue("Cookie");
540 if (cookieValue.empty() ||
541 cookieValue.find("SESSION=") == std::string::npos)
542 {
543 res.addHeader("Set-Cookie",
544 "XSRF-TOKEN=" + sp->csrfToken +
545 "; Secure\r\nSet-Cookie: SESSION=" +
546 sp->sessionToken +
Zbigniew Kurzynski26139a52019-12-11 19:11:18 +0100547 "; Secure; HttpOnly\r\nSet-Cookie: "
548 "IsAuthenticated=true; Secure");
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100549 BMCWEB_LOG_DEBUG
550 << this << " TLS session: " << sp->uniqueId
551 << " with cookie will be used for this request.";
552 req->session = sp;
553 }
554 }
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200555 }
556#endif // BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
557
Ed Tanous1abe55e2018-09-05 08:30:59 -0700558 detail::middlewareCallHelper<
Ed Tanous271584a2019-07-09 16:24:22 -0700559 0U, decltype(ctx), decltype(*middlewares), Middlewares...>(
Ed Tanous1abe55e2018-09-05 08:30:59 -0700560 *middlewares, *req, res, ctx);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700561
Ed Tanous1abe55e2018-09-05 08:30:59 -0700562 if (!res.completed)
563 {
564 if (req->isUpgrade() &&
565 boost::iequals(
566 req->getHeaderValue(boost::beast::http::field::upgrade),
567 "websocket"))
568 {
569 handler->handleUpgrade(*req, res, std::move(adaptor));
570 return;
571 }
James Feista8086642020-01-07 09:53:20 -0800572 res.completeRequestHandler = [this] {
573 this->completeRequest();
574 };
575 needToCallAfterHandlers = true;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700576 handler->handle(*req, res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700577 }
578 else
579 {
580 completeRequest();
581 }
582 }
583 else
584 {
585 completeRequest();
586 }
587 }
Ed Tanouse0d918b2018-03-27 17:41:04 -0700588
Ed Tanouse278c182019-03-13 16:23:37 -0700589 bool isAlive()
590 {
591
592 if constexpr (std::is_same_v<Adaptor,
593 boost::beast::ssl_stream<
594 boost::asio::ip::tcp::socket>>)
595 {
596 return adaptor.next_layer().is_open();
597 }
598 else
599 {
600 return adaptor.is_open();
601 }
602 }
603 void close()
604 {
Ed Tanouse278c182019-03-13 16:23:37 -0700605 if constexpr (std::is_same_v<Adaptor,
606 boost::beast::ssl_stream<
607 boost::asio::ip::tcp::socket>>)
608 {
609 adaptor.next_layer().close();
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200610#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
611 if (auto sp = session.lock())
612 {
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100613 BMCWEB_LOG_DEBUG << this
614 << " Removing TLS session: " << sp->uniqueId;
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200615 persistent_data::SessionStore::getInstance().removeSession(sp);
616 }
617#endif // BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
Ed Tanouse278c182019-03-13 16:23:37 -0700618 }
619 else
620 {
621 adaptor.close();
622 }
623 }
624
Ed Tanous1abe55e2018-09-05 08:30:59 -0700625 void completeRequest()
626 {
627 BMCWEB_LOG_INFO << "Response: " << this << ' ' << req->url << ' '
628 << res.resultInt() << " keepalive=" << req->keepAlive();
Ed Tanous7045c8d2017-04-03 10:04:37 -0700629
Ed Tanous1abe55e2018-09-05 08:30:59 -0700630 if (needToCallAfterHandlers)
631 {
632 needToCallAfterHandlers = false;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700633
Ed Tanous1abe55e2018-09-05 08:30:59 -0700634 // call all afterHandler of middlewares
Ed Tanous271584a2019-07-09 16:24:22 -0700635 detail::afterHandlersCallHelper<sizeof...(Middlewares) - 1,
Ed Tanousb01bf292019-03-25 19:25:26 +0000636 decltype(ctx),
637 decltype(*middlewares)>(
638 *middlewares, ctx, *req, res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700639 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700640
James Feista8086642020-01-07 09:53:20 -0800641 // auto self = this->shared_from_this();
642 res.completeRequestHandler = res.completeRequestHandler = [] {};
643
Ed Tanouse278c182019-03-13 16:23:37 -0700644 if (!isAlive())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700645 {
646 // BMCWEB_LOG_DEBUG << this << " delete (socket is closed) " <<
647 // isReading
648 // << ' ' << isWriting;
649 // delete this;
650 return;
651 }
652 if (res.body().empty() && !res.jsonValue.empty())
653 {
654 if (http_helpers::requestPrefersHtml(*req))
655 {
656 prettyPrintJson(res);
657 }
658 else
659 {
660 res.jsonMode();
Jason M. Bills193ad2f2018-09-26 15:08:52 -0700661 res.body() = res.jsonValue.dump(2, ' ', true);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700662 }
663 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700664
Ed Tanous1abe55e2018-09-05 08:30:59 -0700665 if (res.resultInt() >= 400 && res.body().empty())
666 {
667 res.body() = std::string(res.reason());
668 }
Ed Tanous6295bec2019-09-03 10:11:01 -0700669
670 if (res.result() == boost::beast::http::status::no_content)
671 {
672 // Boost beast throws if content is provided on a no-content
673 // response. Ideally, this would never happen, but in the case that
674 // it does, we don't want to throw.
675 BMCWEB_LOG_CRITICAL
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100676 << this << " Response content provided but code was no-content";
Ed Tanous6295bec2019-09-03 10:11:01 -0700677 res.body().clear();
678 }
679
Ed Tanous1abe55e2018-09-05 08:30:59 -0700680 res.addHeader(boost::beast::http::field::server, serverName);
681 res.addHeader(boost::beast::http::field::date, getCachedDateStr());
682
683 res.keepAlive(req->keepAlive());
684
685 doWrite();
686 }
687
688 private:
689 void doReadHeaders()
690 {
James Feista8086642020-01-07 09:53:20 -0800691 // auto self = this->shared_from_this();
692 isReading = true;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700693 BMCWEB_LOG_DEBUG << this << " doReadHeaders";
694
695 // Clean up any previous Connection.
696 boost::beast::http::async_read_header(
Ed Tanousceac6f72018-12-02 11:58:47 -0800697 adaptor, buffer, *parser,
James Feista8086642020-01-07 09:53:20 -0800698 [this](const boost::system::error_code& ec,
699 std::size_t bytes_transferred) {
700 isReading = false;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700701 BMCWEB_LOG_ERROR << this << " async_read_header "
702 << bytes_transferred << " Bytes";
703 bool errorWhileReading = false;
704 if (ec)
705 {
706 errorWhileReading = true;
707 BMCWEB_LOG_ERROR
708 << this << " Error while reading: " << ec.message();
709 }
710 else
711 {
712 // if the adaptor isn't open anymore, and wasn't handed to a
713 // websocket, treat as an error
Ed Tanouse278c182019-03-13 16:23:37 -0700714 if (!isAlive() && !req->isUpgrade())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700715 {
716 errorWhileReading = true;
717 }
718 }
719
720 if (errorWhileReading)
721 {
722 cancelDeadlineTimer();
Ed Tanouse278c182019-03-13 16:23:37 -0700723 close();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700724 BMCWEB_LOG_DEBUG << this << " from read(1)";
James Feista8086642020-01-07 09:53:20 -0800725 checkDestroy();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700726 return;
727 }
728
729 // Compute the url parameters for the request
730 req->url = req->target();
731 std::size_t index = req->url.find("?");
Ed Tanous39e77502019-03-04 17:35:53 -0800732 if (index != std::string_view::npos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700733 {
Jason M. Bills43fcbe52018-10-16 15:19:20 -0700734 req->url = req->url.substr(0, index);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700735 }
736 req->urlParams = QueryString(std::string(req->target()));
737 doRead();
738 });
739 }
740
741 void doRead()
742 {
James Feista8086642020-01-07 09:53:20 -0800743 // auto self = this->shared_from_this();
744 isReading = true;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700745 BMCWEB_LOG_DEBUG << this << " doRead";
746
747 boost::beast::http::async_read(
Ed Tanousceac6f72018-12-02 11:58:47 -0800748 adaptor, buffer, *parser,
James Feista8086642020-01-07 09:53:20 -0800749 [this](const boost::system::error_code& ec,
750 std::size_t bytes_transferred) {
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100751 BMCWEB_LOG_DEBUG << this << " async_read " << bytes_transferred
Ed Tanous1abe55e2018-09-05 08:30:59 -0700752 << " Bytes";
James Feista8086642020-01-07 09:53:20 -0800753 isReading = false;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700754
755 bool errorWhileReading = false;
756 if (ec)
757 {
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100758 BMCWEB_LOG_ERROR
759 << this << " Error while reading: " << ec.message();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700760 errorWhileReading = true;
761 }
762 else
763 {
Ed Tanouse278c182019-03-13 16:23:37 -0700764 if (!isAlive())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700765 {
766 errorWhileReading = true;
767 }
768 }
769 if (errorWhileReading)
770 {
771 cancelDeadlineTimer();
Ed Tanouse278c182019-03-13 16:23:37 -0700772 close();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700773 BMCWEB_LOG_DEBUG << this << " from read(1)";
James Feista8086642020-01-07 09:53:20 -0800774 checkDestroy();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700775 return;
776 }
777 handle();
778 });
779 }
780
781 void doWrite()
782 {
James Feista8086642020-01-07 09:53:20 -0800783 // auto self = this->shared_from_this();
784 isWriting = true;
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100785 BMCWEB_LOG_DEBUG << this << " doWrite";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700786 res.preparePayload();
787 serializer.emplace(*res.stringResponse);
788 boost::beast::http::async_write(
Ed Tanousceac6f72018-12-02 11:58:47 -0800789 adaptor, *serializer,
James Feista8086642020-01-07 09:53:20 -0800790 [&](const boost::system::error_code& ec,
791 std::size_t bytes_transferred) {
792 isWriting = false;
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100793 BMCWEB_LOG_DEBUG << this << " async_write " << bytes_transferred
Ed Tanous1abe55e2018-09-05 08:30:59 -0700794 << " bytes";
795
796 if (ec)
797 {
798 BMCWEB_LOG_DEBUG << this << " from write(2)";
James Feista8086642020-01-07 09:53:20 -0800799 checkDestroy();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700800 return;
801 }
Ed Tanousceac6f72018-12-02 11:58:47 -0800802 if (!res.keepAlive())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700803 {
Ed Tanouse278c182019-03-13 16:23:37 -0700804 close();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700805 BMCWEB_LOG_DEBUG << this << " from write(1)";
James Feista8086642020-01-07 09:53:20 -0800806 checkDestroy();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700807 return;
808 }
809
810 serializer.reset();
811 BMCWEB_LOG_DEBUG << this << " Clearing response";
812 res.clear();
813 parser.emplace(std::piecewise_construct, std::make_tuple());
814 parser->body_limit(httpReqBodyLimit); // reset body limit for
815 // newly created parser
816 buffer.consume(buffer.size());
817
818 req.emplace(parser->get());
819 doReadHeaders();
820 });
821 }
822
James Feista8086642020-01-07 09:53:20 -0800823 void checkDestroy()
824 {
825 BMCWEB_LOG_DEBUG << this << " isReading " << isReading << " isWriting "
826 << isWriting;
827 if (!isReading && !isWriting)
828 {
829 BMCWEB_LOG_DEBUG << this << " delete (idle) ";
830 delete this;
831 }
832 }
833
Ed Tanous1abe55e2018-09-05 08:30:59 -0700834 void cancelDeadlineTimer()
835 {
James Feista8086642020-01-07 09:53:20 -0800836 BMCWEB_LOG_DEBUG << this << " timer cancelled: " << &timerQueue << ' '
837 << timerCancelKey;
838 timerQueue.cancel(timerCancelKey);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700839 }
840
841 void startDeadline()
842 {
843 cancelDeadlineTimer();
844
James Feista8086642020-01-07 09:53:20 -0800845 timerCancelKey = timerQueue.add([this] {
Ed Tanouse278c182019-03-13 16:23:37 -0700846 if (!isAlive())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700847 {
848 return;
849 }
Ed Tanouse278c182019-03-13 16:23:37 -0700850 close();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700851 });
852 BMCWEB_LOG_DEBUG << this << " timer added: " << &timerQueue << ' '
James Feista8086642020-01-07 09:53:20 -0800853 << timerCancelKey;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700854 }
855
856 private:
857 Adaptor adaptor;
858 Handler* handler;
859
Ed Tanousa24526d2018-12-10 15:17:59 -0800860 // Making this a std::optional allows it to be efficiently destroyed and
Ed Tanous1abe55e2018-09-05 08:30:59 -0700861 // re-created on Connection reset
Ed Tanousa24526d2018-12-10 15:17:59 -0800862 std::optional<
Ed Tanous1abe55e2018-09-05 08:30:59 -0700863 boost::beast::http::request_parser<boost::beast::http::string_body>>
864 parser;
865
Ed Tanous3112a142018-11-29 15:45:10 -0800866 boost::beast::flat_static_buffer<8192> buffer;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700867
Ed Tanousa24526d2018-12-10 15:17:59 -0800868 std::optional<boost::beast::http::response_serializer<
Ed Tanous1abe55e2018-09-05 08:30:59 -0700869 boost::beast::http::string_body>>
870 serializer;
871
Ed Tanousa24526d2018-12-10 15:17:59 -0800872 std::optional<crow::Request> req;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700873 crow::Response res;
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200874#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
875 std::weak_ptr<crow::persistent_data::UserSession> session;
876#endif // BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
Ed Tanous1abe55e2018-09-05 08:30:59 -0700877
878 const std::string& serverName;
879
James Feista8086642020-01-07 09:53:20 -0800880 size_t timerCancelKey = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700881
James Feista8086642020-01-07 09:53:20 -0800882 bool isReading{};
883 bool isWriting{};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700884 bool needToCallAfterHandlers{};
885 bool needToStartReadAfterComplete{};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700886
887 std::tuple<Middlewares...>* middlewares;
888 detail::Context<Middlewares...> ctx;
889
890 std::function<std::string()>& getCachedDateStr;
891 detail::TimerQueue& timerQueue;
Ed Tanous3112a142018-11-29 15:45:10 -0800892};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700893} // namespace crow