blob: 78805a679e2d6bf0035c1633341b7bb2439c47a1 [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
268 adaptor.set_verify_callback(
269 [this](bool preverified, boost::asio::ssl::verify_context& ctx) {
270 // We always return true to allow full auth flow
271 if (!preverified)
272 {
273 return true;
274 }
275
276 X509_STORE_CTX* cts = ctx.native_handle();
277 if (cts == nullptr)
278 {
279 return true;
280 }
281
282 // Get certificate
283 X509* peerCert =
284 X509_STORE_CTX_get_current_cert(ctx.native_handle());
285 if (peerCert == nullptr)
286 {
287 return true;
288 }
289
290 // Check if certificate is OK
291 int error = X509_STORE_CTX_get_error(cts);
292 if (error != X509_V_OK)
293 {
294 return true;
295 }
296 // Check that we have reached final certificate in chain
297 int32_t depth = X509_STORE_CTX_get_error_depth(cts);
298 if (depth != 0)
299
300 {
301 BMCWEB_LOG_DEBUG
302 << "Certificate verification in progress (depth "
303 << depth << "), waiting to reach final depth";
304 return true;
305 }
306
307 BMCWEB_LOG_DEBUG << "Certificate verification of final depth";
308
309 // Verify KeyUsage
310 bool isKeyUsageDigitalSignature = false;
311 bool isKeyUsageKeyAgreement = false;
312
313 ASN1_BIT_STRING* usage = static_cast<ASN1_BIT_STRING*>(
314 X509_get_ext_d2i(peerCert, NID_key_usage, NULL, NULL));
315
316 if (usage == nullptr)
317 {
318 return true;
319 }
320
321 for (int i = 0; i < usage->length; i++)
322 {
323 if (KU_DIGITAL_SIGNATURE & usage->data[i])
324 {
325 isKeyUsageDigitalSignature = true;
326 }
327 if (KU_KEY_AGREEMENT & usage->data[i])
328 {
329 isKeyUsageKeyAgreement = true;
330 }
331 }
332
333 if (!isKeyUsageDigitalSignature || !isKeyUsageKeyAgreement)
334 {
335 BMCWEB_LOG_DEBUG << "Certificate ExtendedKeyUsage does "
336 "not allow provided certificate to "
337 "be used for user authentication";
338 return true;
339 }
340
341 // Determine that ExtendedKeyUsage includes Client Auth
342
343 stack_st_ASN1_OBJECT* extUsage =
344 static_cast<stack_st_ASN1_OBJECT*>(X509_get_ext_d2i(
345 peerCert, NID_ext_key_usage, NULL, NULL));
346
347 if (extUsage == nullptr)
348 {
349 return true;
350 }
351
352 bool isExKeyUsageClientAuth = false;
353 for (int i = 0; i < sk_ASN1_OBJECT_num(extUsage); i++)
354 {
355 if (NID_client_auth ==
356 OBJ_obj2nid(sk_ASN1_OBJECT_value(extUsage, i)))
357 {
358 isExKeyUsageClientAuth = true;
359 break;
360 }
361 }
362
363 // Certificate has to have proper key usages set
364 if (!isExKeyUsageClientAuth)
365 {
366 BMCWEB_LOG_DEBUG << "Certificate ExtendedKeyUsage does "
367 "not allow provided certificate to "
368 "be used for user authentication";
369 return true;
370 }
371 std::string sslUser;
372 // Extract username contained in CommonName
373 sslUser.resize(256, '\0');
374
375 int status = X509_NAME_get_text_by_NID(
376 X509_get_subject_name(peerCert), NID_commonName,
377 sslUser.data(), static_cast<int>(sslUser.size()));
378
379 if (status == -1)
380 {
381 return true;
382 }
383
384 size_t lastChar = sslUser.find('\0');
385 if (lastChar == std::string::npos || lastChar == 0)
386 {
387 return true;
388 }
389 sslUser.resize(lastChar - 1);
390
391 session =
392 persistent_data::SessionStore::getInstance()
393 .generateUserSession(
394 sslUser,
395 crow::persistent_data::PersistenceType::TIMEOUT);
396
397 return true;
398 });
399#endif // BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
400
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700401#ifdef BMCWEB_ENABLE_DEBUG
Ed Tanous1abe55e2018-09-05 08:30:59 -0700402 connectionCount++;
403 BMCWEB_LOG_DEBUG << this << " Connection open, total "
404 << connectionCount;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700405#endif
Ed Tanous1abe55e2018-09-05 08:30:59 -0700406 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700407
Ed Tanous1abe55e2018-09-05 08:30:59 -0700408 ~Connection()
409 {
410 res.completeRequestHandler = nullptr;
411 cancelDeadlineTimer();
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 closed, total "
415 << connectionCount;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700416#endif
Ed Tanous7045c8d2017-04-03 10:04:37 -0700417 }
418
Ed Tanousceac6f72018-12-02 11:58:47 -0800419 Adaptor& socket()
Ed Tanous1abe55e2018-09-05 08:30:59 -0700420 {
Ed Tanousceac6f72018-12-02 11:58:47 -0800421 return adaptor;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700422 }
423
Ed Tanous1abe55e2018-09-05 08:30:59 -0700424 void start()
425 {
Ed Tanous7045c8d2017-04-03 10:04:37 -0700426
Ed Tanousceac6f72018-12-02 11:58:47 -0800427 startDeadline();
428 // TODO(ed) Abstract this to a more clever class with the idea of an
429 // asynchronous "start"
430 if constexpr (std::is_same_v<Adaptor,
431 boost::beast::ssl_stream<
432 boost::asio::ip::tcp::socket>>)
433 {
434 adaptor.async_handshake(
435 boost::asio::ssl::stream_base::server,
436 [this](const boost::system::error_code& ec) {
437 if (ec)
438 {
439 checkDestroy();
440 return;
441 }
442 doReadHeaders();
443 });
444 }
445 else
446 {
447 doReadHeaders();
448 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700449 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700450
Ed Tanous1abe55e2018-09-05 08:30:59 -0700451 void handle()
452 {
453 cancelDeadlineTimer();
454 bool isInvalidRequest = false;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700455
Ed Tanous1abe55e2018-09-05 08:30:59 -0700456 // Check for HTTP version 1.1.
457 if (req->version() == 11)
458 {
459 if (req->getHeaderValue(boost::beast::http::field::host).empty())
460 {
461 isInvalidRequest = true;
Ed Tanousde5c9f32019-03-26 09:17:55 -0700462 res.result(boost::beast::http::status::bad_request);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700463 }
464 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700465
Ed Tanouse278c182019-03-13 16:23:37 -0700466 BMCWEB_LOG_INFO << "Request: "
467 << " " << this << " HTTP/" << req->version() / 10 << "."
468 << req->version() % 10 << ' ' << req->methodString()
469 << " " << req->target();
Ed Tanous7045c8d2017-04-03 10:04:37 -0700470
Ed Tanous1abe55e2018-09-05 08:30:59 -0700471 needToCallAfterHandlers = false;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700472
Ed Tanous1abe55e2018-09-05 08:30:59 -0700473 if (!isInvalidRequest)
474 {
475 res.completeRequestHandler = [] {};
Ed Tanouse278c182019-03-13 16:23:37 -0700476 res.isAliveHelper = [this]() -> bool { return isAlive(); };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700477
Ed Tanous1abe55e2018-09-05 08:30:59 -0700478 ctx = detail::Context<Middlewares...>();
Ed Tanouse278c182019-03-13 16:23:37 -0700479 req->middlewareContext = static_cast<void*>(&ctx);
480 req->ioService = static_cast<decltype(req->ioService)>(
481 &adaptor.get_executor().context());
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200482
483#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
484 if (auto sp = session.lock())
485 {
486 BMCWEB_LOG_DEBUG << "TLS session: " << sp->uniqueId
487 << " will be used for this request.";
488 req->session = sp;
489 }
490#endif // BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
491
Ed Tanous1abe55e2018-09-05 08:30:59 -0700492 detail::middlewareCallHelper<
Ed Tanous271584a2019-07-09 16:24:22 -0700493 0U, decltype(ctx), decltype(*middlewares), Middlewares...>(
Ed Tanous1abe55e2018-09-05 08:30:59 -0700494 *middlewares, *req, res, ctx);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700495
Ed Tanous1abe55e2018-09-05 08:30:59 -0700496 if (!res.completed)
497 {
498 if (req->isUpgrade() &&
499 boost::iequals(
500 req->getHeaderValue(boost::beast::http::field::upgrade),
501 "websocket"))
502 {
503 handler->handleUpgrade(*req, res, std::move(adaptor));
504 return;
505 }
506 res.completeRequestHandler = [this] {
507 this->completeRequest();
508 };
509 needToCallAfterHandlers = true;
510 handler->handle(*req, res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700511 }
512 else
513 {
514 completeRequest();
515 }
516 }
517 else
518 {
519 completeRequest();
520 }
521 }
Ed Tanouse0d918b2018-03-27 17:41:04 -0700522
Ed Tanouse278c182019-03-13 16:23:37 -0700523 bool isAlive()
524 {
525
526 if constexpr (std::is_same_v<Adaptor,
527 boost::beast::ssl_stream<
528 boost::asio::ip::tcp::socket>>)
529 {
530 return adaptor.next_layer().is_open();
531 }
532 else
533 {
534 return adaptor.is_open();
535 }
536 }
537 void close()
538 {
Ed Tanouse278c182019-03-13 16:23:37 -0700539 if constexpr (std::is_same_v<Adaptor,
540 boost::beast::ssl_stream<
541 boost::asio::ip::tcp::socket>>)
542 {
543 adaptor.next_layer().close();
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200544#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
545 if (auto sp = session.lock())
546 {
547 BMCWEB_LOG_DEBUG << "Removing TLS session: " << sp->uniqueId;
548 persistent_data::SessionStore::getInstance().removeSession(sp);
549 }
550#endif // BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
Ed Tanouse278c182019-03-13 16:23:37 -0700551 }
552 else
553 {
554 adaptor.close();
555 }
556 }
557
Ed Tanous1abe55e2018-09-05 08:30:59 -0700558 void completeRequest()
559 {
560 BMCWEB_LOG_INFO << "Response: " << this << ' ' << req->url << ' '
561 << res.resultInt() << " keepalive=" << req->keepAlive();
Ed Tanous7045c8d2017-04-03 10:04:37 -0700562
Ed Tanous1abe55e2018-09-05 08:30:59 -0700563 if (needToCallAfterHandlers)
564 {
565 needToCallAfterHandlers = false;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700566
Ed Tanous1abe55e2018-09-05 08:30:59 -0700567 // call all afterHandler of middlewares
Ed Tanous271584a2019-07-09 16:24:22 -0700568 detail::afterHandlersCallHelper<sizeof...(Middlewares) - 1,
Ed Tanousb01bf292019-03-25 19:25:26 +0000569 decltype(ctx),
570 decltype(*middlewares)>(
571 *middlewares, ctx, *req, res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700572 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700573
Ed Tanous1abe55e2018-09-05 08:30:59 -0700574 // auto self = this->shared_from_this();
Ed Tanousb01bf292019-03-25 19:25:26 +0000575 res.completeRequestHandler = res.completeRequestHandler = [] {};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700576
Ed Tanouse278c182019-03-13 16:23:37 -0700577 if (!isAlive())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700578 {
579 // BMCWEB_LOG_DEBUG << this << " delete (socket is closed) " <<
580 // isReading
581 // << ' ' << isWriting;
582 // delete this;
583 return;
584 }
585 if (res.body().empty() && !res.jsonValue.empty())
586 {
587 if (http_helpers::requestPrefersHtml(*req))
588 {
589 prettyPrintJson(res);
590 }
591 else
592 {
593 res.jsonMode();
Jason M. Bills193ad2f2018-09-26 15:08:52 -0700594 res.body() = res.jsonValue.dump(2, ' ', true);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700595 }
596 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700597
Ed Tanous1abe55e2018-09-05 08:30:59 -0700598 if (res.resultInt() >= 400 && res.body().empty())
599 {
600 res.body() = std::string(res.reason());
601 }
Ed Tanous6295bec2019-09-03 10:11:01 -0700602
603 if (res.result() == boost::beast::http::status::no_content)
604 {
605 // Boost beast throws if content is provided on a no-content
606 // response. Ideally, this would never happen, but in the case that
607 // it does, we don't want to throw.
608 BMCWEB_LOG_CRITICAL
609 << "Response content provided but code was no-content";
610 res.body().clear();
611 }
612
Ed Tanous1abe55e2018-09-05 08:30:59 -0700613 res.addHeader(boost::beast::http::field::server, serverName);
614 res.addHeader(boost::beast::http::field::date, getCachedDateStr());
615
616 res.keepAlive(req->keepAlive());
617
618 doWrite();
619 }
620
621 private:
622 void doReadHeaders()
623 {
624 // auto self = this->shared_from_this();
625 isReading = true;
626 BMCWEB_LOG_DEBUG << this << " doReadHeaders";
627
628 // Clean up any previous Connection.
629 boost::beast::http::async_read_header(
Ed Tanousceac6f72018-12-02 11:58:47 -0800630 adaptor, buffer, *parser,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700631 [this](const boost::system::error_code& ec,
632 std::size_t bytes_transferred) {
633 isReading = false;
634 BMCWEB_LOG_ERROR << this << " async_read_header "
635 << bytes_transferred << " Bytes";
636 bool errorWhileReading = false;
637 if (ec)
638 {
639 errorWhileReading = true;
640 BMCWEB_LOG_ERROR
641 << this << " Error while reading: " << ec.message();
642 }
643 else
644 {
645 // if the adaptor isn't open anymore, and wasn't handed to a
646 // websocket, treat as an error
Ed Tanouse278c182019-03-13 16:23:37 -0700647 if (!isAlive() && !req->isUpgrade())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700648 {
649 errorWhileReading = true;
650 }
651 }
652
653 if (errorWhileReading)
654 {
655 cancelDeadlineTimer();
Ed Tanouse278c182019-03-13 16:23:37 -0700656 close();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700657 BMCWEB_LOG_DEBUG << this << " from read(1)";
658 checkDestroy();
659 return;
660 }
661
662 // Compute the url parameters for the request
663 req->url = req->target();
664 std::size_t index = req->url.find("?");
Ed Tanous39e77502019-03-04 17:35:53 -0800665 if (index != std::string_view::npos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700666 {
Jason M. Bills43fcbe52018-10-16 15:19:20 -0700667 req->url = req->url.substr(0, index);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700668 }
669 req->urlParams = QueryString(std::string(req->target()));
670 doRead();
671 });
672 }
673
674 void doRead()
675 {
676 // auto self = this->shared_from_this();
677 isReading = true;
678 BMCWEB_LOG_DEBUG << this << " doRead";
679
680 boost::beast::http::async_read(
Ed Tanousceac6f72018-12-02 11:58:47 -0800681 adaptor, buffer, *parser,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700682 [this](const boost::system::error_code& ec,
683 std::size_t bytes_transferred) {
684 BMCWEB_LOG_ERROR << this << " async_read " << bytes_transferred
685 << " Bytes";
686 isReading = false;
687
688 bool errorWhileReading = false;
689 if (ec)
690 {
691 BMCWEB_LOG_ERROR << "Error while reading: " << ec.message();
692 errorWhileReading = true;
693 }
694 else
695 {
Ed Tanouse278c182019-03-13 16:23:37 -0700696 if (!isAlive())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700697 {
698 errorWhileReading = true;
699 }
700 }
701 if (errorWhileReading)
702 {
703 cancelDeadlineTimer();
Ed Tanouse278c182019-03-13 16:23:37 -0700704 close();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700705 BMCWEB_LOG_DEBUG << this << " from read(1)";
706 checkDestroy();
707 return;
708 }
709 handle();
710 });
711 }
712
713 void doWrite()
714 {
715 // auto self = this->shared_from_this();
716 isWriting = true;
717 BMCWEB_LOG_DEBUG << "Doing Write";
718 res.preparePayload();
719 serializer.emplace(*res.stringResponse);
720 boost::beast::http::async_write(
Ed Tanousceac6f72018-12-02 11:58:47 -0800721 adaptor, *serializer,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700722 [&](const boost::system::error_code& ec,
723 std::size_t bytes_transferred) {
724 isWriting = false;
725 BMCWEB_LOG_DEBUG << this << " Wrote " << bytes_transferred
726 << " bytes";
727
728 if (ec)
729 {
730 BMCWEB_LOG_DEBUG << this << " from write(2)";
731 checkDestroy();
732 return;
733 }
Ed Tanousceac6f72018-12-02 11:58:47 -0800734 if (!res.keepAlive())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700735 {
Ed Tanouse278c182019-03-13 16:23:37 -0700736 close();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700737 BMCWEB_LOG_DEBUG << this << " from write(1)";
738 checkDestroy();
739 return;
740 }
741
742 serializer.reset();
743 BMCWEB_LOG_DEBUG << this << " Clearing response";
744 res.clear();
745 parser.emplace(std::piecewise_construct, std::make_tuple());
746 parser->body_limit(httpReqBodyLimit); // reset body limit for
747 // newly created parser
748 buffer.consume(buffer.size());
749
750 req.emplace(parser->get());
751 doReadHeaders();
752 });
753 }
754
755 void checkDestroy()
756 {
757 BMCWEB_LOG_DEBUG << this << " isReading " << isReading << " isWriting "
758 << isWriting;
759 if (!isReading && !isWriting)
760 {
761 BMCWEB_LOG_DEBUG << this << " delete (idle) ";
762 delete this;
763 }
764 }
765
766 void cancelDeadlineTimer()
767 {
768 BMCWEB_LOG_DEBUG << this << " timer cancelled: " << &timerQueue << ' '
769 << timerCancelKey;
770 timerQueue.cancel(timerCancelKey);
771 }
772
773 void startDeadline()
774 {
775 cancelDeadlineTimer();
776
777 timerCancelKey = timerQueue.add([this] {
Ed Tanouse278c182019-03-13 16:23:37 -0700778 if (!isAlive())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700779 {
780 return;
781 }
Ed Tanouse278c182019-03-13 16:23:37 -0700782 close();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700783 });
784 BMCWEB_LOG_DEBUG << this << " timer added: " << &timerQueue << ' '
785 << timerCancelKey;
786 }
787
788 private:
789 Adaptor adaptor;
790 Handler* handler;
791
Ed Tanousa24526d2018-12-10 15:17:59 -0800792 // Making this a std::optional allows it to be efficiently destroyed and
Ed Tanous1abe55e2018-09-05 08:30:59 -0700793 // re-created on Connection reset
Ed Tanousa24526d2018-12-10 15:17:59 -0800794 std::optional<
Ed Tanous1abe55e2018-09-05 08:30:59 -0700795 boost::beast::http::request_parser<boost::beast::http::string_body>>
796 parser;
797
Ed Tanous3112a142018-11-29 15:45:10 -0800798 boost::beast::flat_static_buffer<8192> buffer;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700799
Ed Tanousa24526d2018-12-10 15:17:59 -0800800 std::optional<boost::beast::http::response_serializer<
Ed Tanous1abe55e2018-09-05 08:30:59 -0700801 boost::beast::http::string_body>>
802 serializer;
803
Ed Tanousa24526d2018-12-10 15:17:59 -0800804 std::optional<crow::Request> req;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700805 crow::Response res;
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200806#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
807 std::weak_ptr<crow::persistent_data::UserSession> session;
808#endif // BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
Ed Tanous1abe55e2018-09-05 08:30:59 -0700809
810 const std::string& serverName;
811
Ed Tanous271584a2019-07-09 16:24:22 -0700812 size_t timerCancelKey = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700813
814 bool isReading{};
815 bool isWriting{};
816 bool needToCallAfterHandlers{};
817 bool needToStartReadAfterComplete{};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700818
819 std::tuple<Middlewares...>* middlewares;
820 detail::Context<Middlewares...> ctx;
821
822 std::function<std::string()>& getCachedDateStr;
823 detail::TimerQueue& timerQueue;
Ed Tanous3112a142018-11-29 15:45:10 -0800824};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700825} // namespace crow