blob: e64e76c7625192790934b88e96b9e3ace7c4d233 [file] [log] [blame]
Ed Tanous7045c8d2017-04-03 10:04:37 -07001#pragma once
Ed Tanous1abe55e2018-09-05 08:30:59 -07002#include "http_utility.hpp"
3
Ed Tanous7045c8d2017-04-03 10:04:37 -07004#include <atomic>
Ed Tanouse0d918b2018-03-27 17:41:04 -07005#include <boost/algorithm/string.hpp>
Ed Tanous257f5792018-03-17 14:40:09 -07006#include <boost/algorithm/string/predicate.hpp>
Ed Tanous8f626352018-12-19 14:51:54 -08007#include <boost/asio/io_context.hpp>
Ed Tanous3112a142018-11-29 15:45:10 -08008#include <boost/asio/ip/tcp.hpp>
Ed Tanous2f1ebcd2019-02-13 19:39:07 -08009#include <boost/asio/ssl.hpp>
Ed Tanous3112a142018-11-29 15:45:10 -080010#include <boost/beast/core/flat_static_buffer.hpp>
Ed Tanouse278c182019-03-13 16:23:37 -070011#if BOOST_VERSION >= 107000
12#include <boost/beast/ssl/ssl_stream.hpp>
13#else
Ed Tanous2f1ebcd2019-02-13 19:39:07 -080014#include <boost/beast/experimental/core/ssl_stream.hpp>
Ed Tanouse278c182019-03-13 16:23:37 -070015#endif
Ed Tanouse0d918b2018-03-27 17:41:04 -070016#include <boost/beast/http.hpp>
17#include <boost/beast/websocket.hpp>
Ed Tanous1abe55e2018-09-05 08:30:59 -070018#include <chrono>
Ed Tanous1abe55e2018-09-05 08:30:59 -070019#include <vector>
20
Ed Tanousc94ad492019-10-10 15:39:33 -070021#include "http_response.h"
22#include "logging.h"
23#include "middleware_context.h"
24#include "timer_queue.h"
25#include "utility.h"
Ed Tanous7045c8d2017-04-03 10:04:37 -070026
Ed Tanous1abe55e2018-09-05 08:30:59 -070027namespace crow
28{
Ed Tanous257f5792018-03-17 14:40:09 -070029
Ed Tanous1abe55e2018-09-05 08:30:59 -070030inline void prettyPrintJson(crow::Response& res)
31{
Jason M. Bills193ad2f2018-09-26 15:08:52 -070032 std::string value = res.jsonValue.dump(4, ' ', true);
Ed Tanousa29c9972018-11-29 15:54:32 -080033 utility::escapeHtml(value);
34 utility::convertToLinks(value);
Ed Tanous1abe55e2018-09-05 08:30:59 -070035 res.body() = "<html>\n"
36 "<head>\n"
37 "<title>Redfish API</title>\n"
38 "<link rel=\"stylesheet\" type=\"text/css\" "
39 "href=\"/styles/default.css\">\n"
40 "<script src=\"/highlight.pack.js\"></script>"
41 "<script>hljs.initHighlightingOnLoad();</script>"
42 "</head>\n"
43 "<body>\n"
44 "<div style=\"max-width: 576px;margin:0 auto;\">\n"
45 "<img src=\"/DMTF_Redfish_logo_2017.svg\" alt=\"redfish\" "
46 "height=\"406px\" "
47 "width=\"576px\">\n"
48 "<br>\n"
49 "<pre>\n"
50 "<code class=\"json\">" +
51 value +
52 "</code>\n"
53 "</pre>\n"
54 "</div>\n"
55 "</body>\n"
56 "</html>\n";
Ed Tanous93ef5802019-01-03 10:15:41 -080057 res.addHeader("Content-Type", "text/html;charset=UTF-8");
Ed Tanous257f5792018-03-17 14:40:09 -070058}
59
Ed Tanous7045c8d2017-04-03 10:04:37 -070060using namespace boost;
61using tcp = asio::ip::tcp;
62
Ed Tanous1abe55e2018-09-05 08:30:59 -070063namespace detail
64{
65template <typename MW> struct CheckBeforeHandleArity3Const
66{
67 template <typename T,
68 void (T::*)(Request&, Response&, typename MW::Context&) const =
69 &T::beforeHandle>
70 struct Get
71 {
72 };
Ed Tanous7045c8d2017-04-03 10:04:37 -070073};
74
Ed Tanous1abe55e2018-09-05 08:30:59 -070075template <typename MW> struct CheckBeforeHandleArity3
76{
77 template <typename T, void (T::*)(Request&, Response&,
78 typename MW::Context&) = &T::beforeHandle>
79 struct Get
80 {
81 };
Ed Tanous7045c8d2017-04-03 10:04:37 -070082};
83
Ed Tanous1abe55e2018-09-05 08:30:59 -070084template <typename MW> struct CheckAfterHandleArity3Const
85{
86 template <typename T,
87 void (T::*)(Request&, Response&, typename MW::Context&) const =
88 &T::afterHandle>
89 struct Get
90 {
91 };
Ed Tanous7045c8d2017-04-03 10:04:37 -070092};
93
Ed Tanous1abe55e2018-09-05 08:30:59 -070094template <typename MW> struct CheckAfterHandleArity3
95{
96 template <typename T, void (T::*)(Request&, Response&,
97 typename MW::Context&) = &T::afterHandle>
98 struct Get
99 {
100 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700101};
102
Ed Tanous1abe55e2018-09-05 08:30:59 -0700103template <typename T> struct IsBeforeHandleArity3Impl
104{
105 template <typename C>
106 static std::true_type
107 f(typename CheckBeforeHandleArity3Const<T>::template Get<C>*);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700108
Ed Tanous1abe55e2018-09-05 08:30:59 -0700109 template <typename C>
110 static std::true_type
111 f(typename CheckBeforeHandleArity3<T>::template Get<C>*);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700112
Ed Tanous1abe55e2018-09-05 08:30:59 -0700113 template <typename C> static std::false_type f(...);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700114
Ed Tanous1abe55e2018-09-05 08:30:59 -0700115 public:
Ed Tanous0c838cf2019-10-24 10:01:46 -0700116 static constexpr bool value = decltype(f<T>(nullptr))::value;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700117};
118
Ed Tanous1abe55e2018-09-05 08:30:59 -0700119template <typename T> struct IsAfterHandleArity3Impl
120{
121 template <typename C>
122 static std::true_type
123 f(typename CheckAfterHandleArity3Const<T>::template Get<C>*);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700124
Ed Tanous1abe55e2018-09-05 08:30:59 -0700125 template <typename C>
126 static std::true_type
127 f(typename CheckAfterHandleArity3<T>::template Get<C>*);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700128
Ed Tanous1abe55e2018-09-05 08:30:59 -0700129 template <typename C> static std::false_type f(...);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700130
Ed Tanous1abe55e2018-09-05 08:30:59 -0700131 public:
Ed Tanous0c838cf2019-10-24 10:01:46 -0700132 static constexpr bool value = decltype(f<T>(nullptr))::value;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700133};
134
135template <typename MW, typename Context, typename ParentContext>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700136typename std::enable_if<!IsBeforeHandleArity3Impl<MW>::value>::type
Ed Tanous1abe55e2018-09-05 08:30:59 -0700137 beforeHandlerCall(MW& mw, Request& req, Response& res, Context& ctx,
138 ParentContext& /*parent_ctx*/)
139{
140 mw.beforeHandle(req, res, ctx.template get<MW>(), ctx);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700141}
142
143template <typename MW, typename Context, typename ParentContext>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700144typename std::enable_if<IsBeforeHandleArity3Impl<MW>::value>::type
Ed Tanous1abe55e2018-09-05 08:30:59 -0700145 beforeHandlerCall(MW& mw, Request& req, Response& res, Context& ctx,
146 ParentContext& /*parent_ctx*/)
147{
148 mw.beforeHandle(req, res, ctx.template get<MW>());
Ed Tanous7045c8d2017-04-03 10:04:37 -0700149}
150
151template <typename MW, typename Context, typename ParentContext>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700152typename std::enable_if<!IsAfterHandleArity3Impl<MW>::value>::type
Ed Tanous1abe55e2018-09-05 08:30:59 -0700153 afterHandlerCall(MW& mw, Request& req, Response& res, Context& ctx,
154 ParentContext& /*parent_ctx*/)
155{
156 mw.afterHandle(req, res, ctx.template get<MW>(), ctx);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700157}
158
159template <typename MW, typename Context, typename ParentContext>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700160typename std::enable_if<IsAfterHandleArity3Impl<MW>::value>::type
Ed Tanous1abe55e2018-09-05 08:30:59 -0700161 afterHandlerCall(MW& mw, Request& req, Response& res, Context& ctx,
162 ParentContext& /*parent_ctx*/)
163{
164 mw.afterHandle(req, res, ctx.template get<MW>());
Ed Tanous7045c8d2017-04-03 10:04:37 -0700165}
166
Ed Tanous271584a2019-07-09 16:24:22 -0700167template <size_t N, typename Context, typename Container, typename CurrentMW,
Ed Tanous7045c8d2017-04-03 10:04:37 -0700168 typename... Middlewares>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700169bool middlewareCallHelper(Container& middlewares, Request& req, Response& res,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700170 Context& ctx)
171{
172 using parent_context_t = typename Context::template partial<N - 1>;
173 beforeHandlerCall<CurrentMW, Context, parent_context_t>(
Ed Tanous7045c8d2017-04-03 10:04:37 -0700174 std::get<N>(middlewares), req, res, ctx,
175 static_cast<parent_context_t&>(ctx));
Ed Tanous7045c8d2017-04-03 10:04:37 -0700176
Ed Tanous1abe55e2018-09-05 08:30:59 -0700177 if (res.isCompleted())
178 {
179 afterHandlerCall<CurrentMW, Context, parent_context_t>(
180 std::get<N>(middlewares), req, res, ctx,
181 static_cast<parent_context_t&>(ctx));
182 return true;
183 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700184
Ed Tanous1abe55e2018-09-05 08:30:59 -0700185 if (middlewareCallHelper<N + 1, Context, Container, Middlewares...>(
186 middlewares, req, res, ctx))
187 {
188 afterHandlerCall<CurrentMW, Context, parent_context_t>(
189 std::get<N>(middlewares), req, res, ctx,
190 static_cast<parent_context_t&>(ctx));
191 return true;
192 }
193
194 return false;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700195}
196
Ed Tanous271584a2019-07-09 16:24:22 -0700197template <size_t N, typename Context, typename Container>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700198bool middlewareCallHelper(Container& /*middlewares*/, Request& /*req*/,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700199 Response& /*res*/, Context& /*ctx*/)
200{
201 return false;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700202}
203
Ed Tanous271584a2019-07-09 16:24:22 -0700204template <size_t N, typename Context, typename Container>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700205typename std::enable_if<(N < 0)>::type
206 afterHandlersCallHelper(Container& /*middlewares*/, Context& /*Context*/,
207 Request& /*req*/, Response& /*res*/)
208{
Ed Tanous7045c8d2017-04-03 10:04:37 -0700209}
210
Ed Tanous271584a2019-07-09 16:24:22 -0700211template <size_t N, typename Context, typename Container>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700212typename std::enable_if<(N == 0)>::type
213 afterHandlersCallHelper(Container& middlewares, Context& ctx, Request& req,
214 Response& res)
215{
216 using parent_context_t = typename Context::template partial<N - 1>;
217 using CurrentMW = typename std::tuple_element<
218 N, typename std::remove_reference<Container>::type>::type;
219 afterHandlerCall<CurrentMW, Context, parent_context_t>(
220 std::get<N>(middlewares), req, res, ctx,
221 static_cast<parent_context_t&>(ctx));
Ed Tanous7045c8d2017-04-03 10:04:37 -0700222}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700223
Ed Tanous271584a2019-07-09 16:24:22 -0700224template <size_t N, typename Context, typename Container>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700225typename std::enable_if<(N > 0)>::type
226 afterHandlersCallHelper(Container& middlewares, Context& ctx, Request& req,
227 Response& res)
228{
229 using parent_context_t = typename Context::template partial<N - 1>;
230 using CurrentMW = typename std::tuple_element<
231 N, typename std::remove_reference<Container>::type>::type;
232 afterHandlerCall<CurrentMW, Context, parent_context_t>(
233 std::get<N>(middlewares), req, res, ctx,
234 static_cast<parent_context_t&>(ctx));
235 afterHandlersCallHelper<N - 1, Context, Container>(middlewares, ctx, req,
236 res);
237}
238} // namespace detail
Ed Tanous7045c8d2017-04-03 10:04:37 -0700239
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700240#ifdef BMCWEB_ENABLE_DEBUG
Ed Tanouse0d918b2018-03-27 17:41:04 -0700241static std::atomic<int> connectionCount;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700242#endif
Jennifer Leeacb7cfb2018-06-07 16:08:15 -0700243
244// request body limit size: 30M
245constexpr unsigned int httpReqBodyLimit = 1024 * 1024 * 30;
246
Ed Tanous7045c8d2017-04-03 10:04:37 -0700247template <typename Adaptor, typename Handler, typename... Middlewares>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700248class Connection
249{
250 public:
Ed Tanous271584a2019-07-09 16:24:22 -0700251 Connection(boost::asio::io_context& ioService, Handler* handlerIn,
252 const std::string& ServerNameIn,
253 std::tuple<Middlewares...>* middlewaresIn,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700254 std::function<std::string()>& get_cached_date_str_f,
Ed Tanous271584a2019-07-09 16:24:22 -0700255 detail::TimerQueue& timerQueueIn, Adaptor adaptorIn) :
Ed Tanousceac6f72018-12-02 11:58:47 -0800256 adaptor(std::move(adaptorIn)),
Ed Tanous271584a2019-07-09 16:24:22 -0700257 handler(handlerIn), serverName(ServerNameIn),
258 middlewares(middlewaresIn), getCachedDateStr(get_cached_date_str_f),
259 timerQueue(timerQueueIn)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700260 {
261 parser.emplace(std::piecewise_construct, std::make_tuple());
262 // Temporarily changed to 30MB; Need to modify uploading/authentication
263 // mechanism
264 parser->body_limit(httpReqBodyLimit);
265 req.emplace(parser->get());
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200266
267#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100268 adaptor.set_verify_callback([this](
269 bool preverified,
270 boost::asio::ssl::verify_context& ctx) {
271 // do nothing if TLS is disabled
272 if (!crow::persistent_data::SessionStore::getInstance()
273 .getAuthMethodsConfig()
274 .tls)
275 {
276 BMCWEB_LOG_DEBUG << this << " TLS auth_config is disabled";
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200277 return true;
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100278 }
279
280 // We always return true to allow full auth flow
281 if (!preverified)
282 {
283 return true;
284 }
285
286 X509_STORE_CTX* cts = ctx.native_handle();
287 if (cts == nullptr)
288 {
289 return true;
290 }
291
292 // Get certificate
293 X509* peerCert =
294 X509_STORE_CTX_get_current_cert(ctx.native_handle());
295 if (peerCert == nullptr)
296 {
297 return true;
298 }
299
300 // Check if certificate is OK
301 int error = X509_STORE_CTX_get_error(cts);
302 if (error != X509_V_OK)
303 {
304 return true;
305 }
306 // Check that we have reached final certificate in chain
307 int32_t depth = X509_STORE_CTX_get_error_depth(cts);
308 if (depth != 0)
309
310 {
311 BMCWEB_LOG_DEBUG
312 << this << " Certificate verification in progress (depth "
313 << depth << "), waiting to reach final depth";
314 return true;
315 }
316
317 BMCWEB_LOG_DEBUG << this
318 << " Certificate verification of final depth";
319
320 // Verify KeyUsage
321 bool isKeyUsageDigitalSignature = false;
322 bool isKeyUsageKeyAgreement = false;
323
324 ASN1_BIT_STRING* usage = static_cast<ASN1_BIT_STRING*>(
325 X509_get_ext_d2i(peerCert, NID_key_usage, NULL, NULL));
326
327 if (usage == nullptr)
328 {
329 return true;
330 }
331
332 for (int i = 0; i < usage->length; i++)
333 {
334 if (KU_DIGITAL_SIGNATURE & usage->data[i])
335 {
336 isKeyUsageDigitalSignature = true;
337 }
338 if (KU_KEY_AGREEMENT & usage->data[i])
339 {
340 isKeyUsageKeyAgreement = true;
341 }
342 }
343
344 if (!isKeyUsageDigitalSignature || !isKeyUsageKeyAgreement)
345 {
346 BMCWEB_LOG_DEBUG << this
347 << " Certificate ExtendedKeyUsage does "
348 "not allow provided certificate to "
349 "be used for user authentication";
350 return true;
351 }
352
353 // Determine that ExtendedKeyUsage includes Client Auth
354
355 stack_st_ASN1_OBJECT* extUsage = static_cast<stack_st_ASN1_OBJECT*>(
356 X509_get_ext_d2i(peerCert, NID_ext_key_usage, NULL, NULL));
357
358 if (extUsage == nullptr)
359 {
360 return true;
361 }
362
363 bool isExKeyUsageClientAuth = false;
364 for (int i = 0; i < sk_ASN1_OBJECT_num(extUsage); i++)
365 {
366 if (NID_client_auth ==
367 OBJ_obj2nid(sk_ASN1_OBJECT_value(extUsage, i)))
368 {
369 isExKeyUsageClientAuth = true;
370 break;
371 }
372 }
373
374 // Certificate has to have proper key usages set
375 if (!isExKeyUsageClientAuth)
376 {
377 BMCWEB_LOG_DEBUG << this
378 << " Certificate ExtendedKeyUsage does "
379 "not allow provided certificate to "
380 "be used for user authentication";
381 return true;
382 }
383 std::string sslUser;
384 // Extract username contained in CommonName
385 sslUser.resize(256, '\0');
386
387 int status = X509_NAME_get_text_by_NID(
388 X509_get_subject_name(peerCert), NID_commonName, sslUser.data(),
389 static_cast<int>(sslUser.size()));
390
391 if (status == -1)
392 {
393 return true;
394 }
395
396 size_t lastChar = sslUser.find('\0');
397 if (lastChar == std::string::npos || lastChar == 0)
398 {
399 return true;
400 }
401 sslUser.resize(lastChar);
402
403 session = persistent_data::SessionStore::getInstance()
404 .generateUserSession(
405 sslUser,
406 crow::persistent_data::PersistenceType::TIMEOUT);
407
408 return true;
409 });
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200410#endif // BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
411
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700412#ifdef BMCWEB_ENABLE_DEBUG
Ed Tanous1abe55e2018-09-05 08:30:59 -0700413 connectionCount++;
414 BMCWEB_LOG_DEBUG << this << " Connection open, total "
415 << connectionCount;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700416#endif
Ed Tanous1abe55e2018-09-05 08:30:59 -0700417 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700418
Ed Tanous1abe55e2018-09-05 08:30:59 -0700419 ~Connection()
420 {
421 res.completeRequestHandler = nullptr;
422 cancelDeadlineTimer();
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700423#ifdef BMCWEB_ENABLE_DEBUG
Ed Tanous1abe55e2018-09-05 08:30:59 -0700424 connectionCount--;
425 BMCWEB_LOG_DEBUG << this << " Connection closed, total "
426 << connectionCount;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700427#endif
Ed Tanous7045c8d2017-04-03 10:04:37 -0700428 }
429
Ed Tanousceac6f72018-12-02 11:58:47 -0800430 Adaptor& socket()
Ed Tanous1abe55e2018-09-05 08:30:59 -0700431 {
Ed Tanousceac6f72018-12-02 11:58:47 -0800432 return adaptor;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700433 }
434
Ed Tanous1abe55e2018-09-05 08:30:59 -0700435 void start()
436 {
Ed Tanous7045c8d2017-04-03 10:04:37 -0700437
Ed Tanousceac6f72018-12-02 11:58:47 -0800438 startDeadline();
439 // TODO(ed) Abstract this to a more clever class with the idea of an
440 // asynchronous "start"
441 if constexpr (std::is_same_v<Adaptor,
442 boost::beast::ssl_stream<
443 boost::asio::ip::tcp::socket>>)
444 {
445 adaptor.async_handshake(
446 boost::asio::ssl::stream_base::server,
447 [this](const boost::system::error_code& ec) {
448 if (ec)
449 {
450 checkDestroy();
451 return;
452 }
453 doReadHeaders();
454 });
455 }
456 else
457 {
458 doReadHeaders();
459 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700460 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700461
Ed Tanous1abe55e2018-09-05 08:30:59 -0700462 void handle()
463 {
464 cancelDeadlineTimer();
465 bool isInvalidRequest = false;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700466
Ed Tanous1abe55e2018-09-05 08:30:59 -0700467 // Check for HTTP version 1.1.
468 if (req->version() == 11)
469 {
470 if (req->getHeaderValue(boost::beast::http::field::host).empty())
471 {
472 isInvalidRequest = true;
Ed Tanousde5c9f32019-03-26 09:17:55 -0700473 res.result(boost::beast::http::status::bad_request);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700474 }
475 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700476
Ed Tanouse278c182019-03-13 16:23:37 -0700477 BMCWEB_LOG_INFO << "Request: "
478 << " " << this << " HTTP/" << req->version() / 10 << "."
479 << req->version() % 10 << ' ' << req->methodString()
480 << " " << req->target();
Ed Tanous7045c8d2017-04-03 10:04:37 -0700481
Ed Tanous1abe55e2018-09-05 08:30:59 -0700482 needToCallAfterHandlers = false;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700483
Ed Tanous1abe55e2018-09-05 08:30:59 -0700484 if (!isInvalidRequest)
485 {
486 res.completeRequestHandler = [] {};
Ed Tanouse278c182019-03-13 16:23:37 -0700487 res.isAliveHelper = [this]() -> bool { return isAlive(); };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700488
Ed Tanous1abe55e2018-09-05 08:30:59 -0700489 ctx = detail::Context<Middlewares...>();
Ed Tanouse278c182019-03-13 16:23:37 -0700490 req->middlewareContext = static_cast<void*>(&ctx);
491 req->ioService = static_cast<decltype(req->ioService)>(
492 &adaptor.get_executor().context());
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200493
494#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
495 if (auto sp = session.lock())
496 {
497 BMCWEB_LOG_DEBUG << "TLS session: " << sp->uniqueId
498 << " will be used for this request.";
499 req->session = sp;
500 }
501#endif // BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
502
Ed Tanous1abe55e2018-09-05 08:30:59 -0700503 detail::middlewareCallHelper<
Ed Tanous271584a2019-07-09 16:24:22 -0700504 0U, decltype(ctx), decltype(*middlewares), Middlewares...>(
Ed Tanous1abe55e2018-09-05 08:30:59 -0700505 *middlewares, *req, res, ctx);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700506
Ed Tanous1abe55e2018-09-05 08:30:59 -0700507 if (!res.completed)
508 {
509 if (req->isUpgrade() &&
510 boost::iequals(
511 req->getHeaderValue(boost::beast::http::field::upgrade),
512 "websocket"))
513 {
514 handler->handleUpgrade(*req, res, std::move(adaptor));
515 return;
516 }
517 res.completeRequestHandler = [this] {
518 this->completeRequest();
519 };
520 needToCallAfterHandlers = true;
521 handler->handle(*req, res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700522 }
523 else
524 {
525 completeRequest();
526 }
527 }
528 else
529 {
530 completeRequest();
531 }
532 }
Ed Tanouse0d918b2018-03-27 17:41:04 -0700533
Ed Tanouse278c182019-03-13 16:23:37 -0700534 bool isAlive()
535 {
536
537 if constexpr (std::is_same_v<Adaptor,
538 boost::beast::ssl_stream<
539 boost::asio::ip::tcp::socket>>)
540 {
541 return adaptor.next_layer().is_open();
542 }
543 else
544 {
545 return adaptor.is_open();
546 }
547 }
548 void close()
549 {
Ed Tanouse278c182019-03-13 16:23:37 -0700550 if constexpr (std::is_same_v<Adaptor,
551 boost::beast::ssl_stream<
552 boost::asio::ip::tcp::socket>>)
553 {
554 adaptor.next_layer().close();
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200555#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
556 if (auto sp = session.lock())
557 {
558 BMCWEB_LOG_DEBUG << "Removing TLS session: " << sp->uniqueId;
559 persistent_data::SessionStore::getInstance().removeSession(sp);
560 }
561#endif // BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
Ed Tanouse278c182019-03-13 16:23:37 -0700562 }
563 else
564 {
565 adaptor.close();
566 }
567 }
568
Ed Tanous1abe55e2018-09-05 08:30:59 -0700569 void completeRequest()
570 {
571 BMCWEB_LOG_INFO << "Response: " << this << ' ' << req->url << ' '
572 << res.resultInt() << " keepalive=" << req->keepAlive();
Ed Tanous7045c8d2017-04-03 10:04:37 -0700573
Ed Tanous1abe55e2018-09-05 08:30:59 -0700574 if (needToCallAfterHandlers)
575 {
576 needToCallAfterHandlers = false;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700577
Ed Tanous1abe55e2018-09-05 08:30:59 -0700578 // call all afterHandler of middlewares
Ed Tanous271584a2019-07-09 16:24:22 -0700579 detail::afterHandlersCallHelper<sizeof...(Middlewares) - 1,
Ed Tanousb01bf292019-03-25 19:25:26 +0000580 decltype(ctx),
581 decltype(*middlewares)>(
582 *middlewares, ctx, *req, res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700583 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700584
Ed Tanous1abe55e2018-09-05 08:30:59 -0700585 // auto self = this->shared_from_this();
Ed Tanousb01bf292019-03-25 19:25:26 +0000586 res.completeRequestHandler = res.completeRequestHandler = [] {};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700587
Ed Tanouse278c182019-03-13 16:23:37 -0700588 if (!isAlive())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700589 {
590 // BMCWEB_LOG_DEBUG << this << " delete (socket is closed) " <<
591 // isReading
592 // << ' ' << isWriting;
593 // delete this;
594 return;
595 }
596 if (res.body().empty() && !res.jsonValue.empty())
597 {
598 if (http_helpers::requestPrefersHtml(*req))
599 {
600 prettyPrintJson(res);
601 }
602 else
603 {
604 res.jsonMode();
Jason M. Bills193ad2f2018-09-26 15:08:52 -0700605 res.body() = res.jsonValue.dump(2, ' ', true);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700606 }
607 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700608
Ed Tanous1abe55e2018-09-05 08:30:59 -0700609 if (res.resultInt() >= 400 && res.body().empty())
610 {
611 res.body() = std::string(res.reason());
612 }
Ed Tanous6295bec2019-09-03 10:11:01 -0700613
614 if (res.result() == boost::beast::http::status::no_content)
615 {
616 // Boost beast throws if content is provided on a no-content
617 // response. Ideally, this would never happen, but in the case that
618 // it does, we don't want to throw.
619 BMCWEB_LOG_CRITICAL
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100620 << this << " Response content provided but code was no-content";
Ed Tanous6295bec2019-09-03 10:11:01 -0700621 res.body().clear();
622 }
623
Ed Tanous1abe55e2018-09-05 08:30:59 -0700624 res.addHeader(boost::beast::http::field::server, serverName);
625 res.addHeader(boost::beast::http::field::date, getCachedDateStr());
626
627 res.keepAlive(req->keepAlive());
628
629 doWrite();
630 }
631
632 private:
633 void doReadHeaders()
634 {
635 // auto self = this->shared_from_this();
636 isReading = true;
637 BMCWEB_LOG_DEBUG << this << " doReadHeaders";
638
639 // Clean up any previous Connection.
640 boost::beast::http::async_read_header(
Ed Tanousceac6f72018-12-02 11:58:47 -0800641 adaptor, buffer, *parser,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700642 [this](const boost::system::error_code& ec,
643 std::size_t bytes_transferred) {
644 isReading = false;
645 BMCWEB_LOG_ERROR << this << " async_read_header "
646 << bytes_transferred << " Bytes";
647 bool errorWhileReading = false;
648 if (ec)
649 {
650 errorWhileReading = true;
651 BMCWEB_LOG_ERROR
652 << this << " Error while reading: " << ec.message();
653 }
654 else
655 {
656 // if the adaptor isn't open anymore, and wasn't handed to a
657 // websocket, treat as an error
Ed Tanouse278c182019-03-13 16:23:37 -0700658 if (!isAlive() && !req->isUpgrade())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700659 {
660 errorWhileReading = true;
661 }
662 }
663
664 if (errorWhileReading)
665 {
666 cancelDeadlineTimer();
Ed Tanouse278c182019-03-13 16:23:37 -0700667 close();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700668 BMCWEB_LOG_DEBUG << this << " from read(1)";
669 checkDestroy();
670 return;
671 }
672
673 // Compute the url parameters for the request
674 req->url = req->target();
675 std::size_t index = req->url.find("?");
Ed Tanous39e77502019-03-04 17:35:53 -0800676 if (index != std::string_view::npos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700677 {
Jason M. Bills43fcbe52018-10-16 15:19:20 -0700678 req->url = req->url.substr(0, index);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700679 }
680 req->urlParams = QueryString(std::string(req->target()));
681 doRead();
682 });
683 }
684
685 void doRead()
686 {
687 // auto self = this->shared_from_this();
688 isReading = true;
689 BMCWEB_LOG_DEBUG << this << " doRead";
690
691 boost::beast::http::async_read(
Ed Tanousceac6f72018-12-02 11:58:47 -0800692 adaptor, buffer, *parser,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700693 [this](const boost::system::error_code& ec,
694 std::size_t bytes_transferred) {
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100695 BMCWEB_LOG_DEBUG << this << " async_read " << bytes_transferred
Ed Tanous1abe55e2018-09-05 08:30:59 -0700696 << " Bytes";
697 isReading = false;
698
699 bool errorWhileReading = false;
700 if (ec)
701 {
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100702 BMCWEB_LOG_ERROR
703 << this << " Error while reading: " << ec.message();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700704 errorWhileReading = true;
705 }
706 else
707 {
Ed Tanouse278c182019-03-13 16:23:37 -0700708 if (!isAlive())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700709 {
710 errorWhileReading = true;
711 }
712 }
713 if (errorWhileReading)
714 {
715 cancelDeadlineTimer();
Ed Tanouse278c182019-03-13 16:23:37 -0700716 close();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700717 BMCWEB_LOG_DEBUG << this << " from read(1)";
718 checkDestroy();
719 return;
720 }
721 handle();
722 });
723 }
724
725 void doWrite()
726 {
727 // auto self = this->shared_from_this();
728 isWriting = true;
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100729 BMCWEB_LOG_DEBUG << this << " doWrite";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700730 res.preparePayload();
731 serializer.emplace(*res.stringResponse);
732 boost::beast::http::async_write(
Ed Tanousceac6f72018-12-02 11:58:47 -0800733 adaptor, *serializer,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700734 [&](const boost::system::error_code& ec,
735 std::size_t bytes_transferred) {
736 isWriting = false;
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100737 BMCWEB_LOG_DEBUG << this << " async_write " << bytes_transferred
Ed Tanous1abe55e2018-09-05 08:30:59 -0700738 << " bytes";
739
740 if (ec)
741 {
742 BMCWEB_LOG_DEBUG << this << " from write(2)";
743 checkDestroy();
744 return;
745 }
Ed Tanousceac6f72018-12-02 11:58:47 -0800746 if (!res.keepAlive())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700747 {
Ed Tanouse278c182019-03-13 16:23:37 -0700748 close();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700749 BMCWEB_LOG_DEBUG << this << " from write(1)";
750 checkDestroy();
751 return;
752 }
753
754 serializer.reset();
755 BMCWEB_LOG_DEBUG << this << " Clearing response";
756 res.clear();
757 parser.emplace(std::piecewise_construct, std::make_tuple());
758 parser->body_limit(httpReqBodyLimit); // reset body limit for
759 // newly created parser
760 buffer.consume(buffer.size());
761
762 req.emplace(parser->get());
763 doReadHeaders();
764 });
765 }
766
767 void checkDestroy()
768 {
769 BMCWEB_LOG_DEBUG << this << " isReading " << isReading << " isWriting "
770 << isWriting;
771 if (!isReading && !isWriting)
772 {
773 BMCWEB_LOG_DEBUG << this << " delete (idle) ";
774 delete this;
775 }
776 }
777
778 void cancelDeadlineTimer()
779 {
780 BMCWEB_LOG_DEBUG << this << " timer cancelled: " << &timerQueue << ' '
781 << timerCancelKey;
782 timerQueue.cancel(timerCancelKey);
783 }
784
785 void startDeadline()
786 {
787 cancelDeadlineTimer();
788
789 timerCancelKey = timerQueue.add([this] {
Ed Tanouse278c182019-03-13 16:23:37 -0700790 if (!isAlive())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700791 {
792 return;
793 }
Ed Tanouse278c182019-03-13 16:23:37 -0700794 close();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700795 });
796 BMCWEB_LOG_DEBUG << this << " timer added: " << &timerQueue << ' '
797 << timerCancelKey;
798 }
799
800 private:
801 Adaptor adaptor;
802 Handler* handler;
803
Ed Tanousa24526d2018-12-10 15:17:59 -0800804 // Making this a std::optional allows it to be efficiently destroyed and
Ed Tanous1abe55e2018-09-05 08:30:59 -0700805 // re-created on Connection reset
Ed Tanousa24526d2018-12-10 15:17:59 -0800806 std::optional<
Ed Tanous1abe55e2018-09-05 08:30:59 -0700807 boost::beast::http::request_parser<boost::beast::http::string_body>>
808 parser;
809
Ed Tanous3112a142018-11-29 15:45:10 -0800810 boost::beast::flat_static_buffer<8192> buffer;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700811
Ed Tanousa24526d2018-12-10 15:17:59 -0800812 std::optional<boost::beast::http::response_serializer<
Ed Tanous1abe55e2018-09-05 08:30:59 -0700813 boost::beast::http::string_body>>
814 serializer;
815
Ed Tanousa24526d2018-12-10 15:17:59 -0800816 std::optional<crow::Request> req;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700817 crow::Response res;
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200818#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
819 std::weak_ptr<crow::persistent_data::UserSession> session;
820#endif // BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
Ed Tanous1abe55e2018-09-05 08:30:59 -0700821
822 const std::string& serverName;
823
Ed Tanous271584a2019-07-09 16:24:22 -0700824 size_t timerCancelKey = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700825
826 bool isReading{};
827 bool isWriting{};
828 bool needToCallAfterHandlers{};
829 bool needToStartReadAfterComplete{};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700830
831 std::tuple<Middlewares...>* middlewares;
832 detail::Context<Middlewares...> ctx;
833
834 std::function<std::string()>& getCachedDateStr;
835 detail::TimerQueue& timerQueue;
Ed Tanous3112a142018-11-29 15:45:10 -0800836};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700837} // namespace crow