blob: 5a4ff57c316e317bd2711227976be20b533cbddb [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>
Jan Sowinskic00500b2019-12-03 10:37:06 +0100248class Connection : public std::enable_shared_from_this<
249 Connection<Adaptor, Handler, Middlewares...>>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700250{
251 public:
Ed Tanous271584a2019-07-09 16:24:22 -0700252 Connection(boost::asio::io_context& ioService, Handler* handlerIn,
253 const std::string& ServerNameIn,
254 std::tuple<Middlewares...>* middlewaresIn,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700255 std::function<std::string()>& get_cached_date_str_f,
Ed Tanous271584a2019-07-09 16:24:22 -0700256 detail::TimerQueue& timerQueueIn, Adaptor adaptorIn) :
Ed Tanousceac6f72018-12-02 11:58:47 -0800257 adaptor(std::move(adaptorIn)),
Ed Tanous271584a2019-07-09 16:24:22 -0700258 handler(handlerIn), serverName(ServerNameIn),
259 middlewares(middlewaresIn), getCachedDateStr(get_cached_date_str_f),
260 timerQueue(timerQueueIn)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700261 {
262 parser.emplace(std::piecewise_construct, std::make_tuple());
263 // Temporarily changed to 30MB; Need to modify uploading/authentication
264 // mechanism
265 parser->body_limit(httpReqBodyLimit);
266 req.emplace(parser->get());
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200267
268#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100269 auto ca_available = !std::filesystem::is_empty(
270 std::filesystem::path(ensuressl::trustStorePath));
271 if (ca_available && crow::persistent_data::SessionStore::getInstance()
272 .getAuthMethodsConfig()
273 .tls)
274 {
275 adaptor.set_verify_mode(boost::asio::ssl::verify_peer);
276 SSL_set_session_id_context(
277 adaptor.native_handle(),
278 reinterpret_cast<const unsigned char*>(serverName.c_str()),
Zbigniew Kurzynskicac94c52019-11-07 12:55:04 +0100279 static_cast<unsigned int>(serverName.length()));
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100280 BMCWEB_LOG_DEBUG << this << " TLS is enabled on this connection.";
281 }
282
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100283 adaptor.set_verify_callback([this](
284 bool preverified,
285 boost::asio::ssl::verify_context& ctx) {
286 // do nothing if TLS is disabled
287 if (!crow::persistent_data::SessionStore::getInstance()
288 .getAuthMethodsConfig()
289 .tls)
290 {
291 BMCWEB_LOG_DEBUG << this << " TLS auth_config is disabled";
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200292 return true;
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100293 }
294
295 // We always return true to allow full auth flow
296 if (!preverified)
297 {
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100298 BMCWEB_LOG_DEBUG << this << " TLS preverification failed.";
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100299 return true;
300 }
301
302 X509_STORE_CTX* cts = ctx.native_handle();
303 if (cts == nullptr)
304 {
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100305 BMCWEB_LOG_DEBUG << this << " Cannot get native TLS handle.";
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100306 return true;
307 }
308
309 // Get certificate
310 X509* peerCert =
311 X509_STORE_CTX_get_current_cert(ctx.native_handle());
312 if (peerCert == nullptr)
313 {
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100314 BMCWEB_LOG_DEBUG << this
315 << " Cannot get current TLS certificate.";
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100316 return true;
317 }
318
319 // Check if certificate is OK
320 int error = X509_STORE_CTX_get_error(cts);
321 if (error != X509_V_OK)
322 {
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100323 BMCWEB_LOG_INFO << this << " Last TLS error is: " << error;
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100324 return true;
325 }
326 // Check that we have reached final certificate in chain
327 int32_t depth = X509_STORE_CTX_get_error_depth(cts);
328 if (depth != 0)
329
330 {
331 BMCWEB_LOG_DEBUG
332 << this << " Certificate verification in progress (depth "
333 << depth << "), waiting to reach final depth";
334 return true;
335 }
336
337 BMCWEB_LOG_DEBUG << this
338 << " Certificate verification of final depth";
339
340 // Verify KeyUsage
341 bool isKeyUsageDigitalSignature = false;
342 bool isKeyUsageKeyAgreement = false;
343
344 ASN1_BIT_STRING* usage = static_cast<ASN1_BIT_STRING*>(
345 X509_get_ext_d2i(peerCert, NID_key_usage, NULL, NULL));
346
347 if (usage == nullptr)
348 {
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100349 BMCWEB_LOG_DEBUG << this << " TLS usage is null";
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100350 return true;
351 }
352
353 for (int i = 0; i < usage->length; i++)
354 {
355 if (KU_DIGITAL_SIGNATURE & usage->data[i])
356 {
357 isKeyUsageDigitalSignature = true;
358 }
359 if (KU_KEY_AGREEMENT & usage->data[i])
360 {
361 isKeyUsageKeyAgreement = true;
362 }
363 }
364
365 if (!isKeyUsageDigitalSignature || !isKeyUsageKeyAgreement)
366 {
367 BMCWEB_LOG_DEBUG << this
368 << " Certificate ExtendedKeyUsage does "
369 "not allow provided certificate to "
370 "be used for user authentication";
371 return true;
372 }
373
374 // Determine that ExtendedKeyUsage includes Client Auth
375
376 stack_st_ASN1_OBJECT* extUsage = static_cast<stack_st_ASN1_OBJECT*>(
377 X509_get_ext_d2i(peerCert, NID_ext_key_usage, NULL, NULL));
378
379 if (extUsage == nullptr)
380 {
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100381 BMCWEB_LOG_DEBUG << this << " TLS extUsage is null";
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100382 return true;
383 }
384
385 bool isExKeyUsageClientAuth = false;
386 for (int i = 0; i < sk_ASN1_OBJECT_num(extUsage); i++)
387 {
388 if (NID_client_auth ==
389 OBJ_obj2nid(sk_ASN1_OBJECT_value(extUsage, i)))
390 {
391 isExKeyUsageClientAuth = true;
392 break;
393 }
394 }
395
396 // Certificate has to have proper key usages set
397 if (!isExKeyUsageClientAuth)
398 {
399 BMCWEB_LOG_DEBUG << this
400 << " Certificate ExtendedKeyUsage does "
401 "not allow provided certificate to "
402 "be used for user authentication";
403 return true;
404 }
405 std::string sslUser;
406 // Extract username contained in CommonName
407 sslUser.resize(256, '\0');
408
409 int status = X509_NAME_get_text_by_NID(
410 X509_get_subject_name(peerCert), NID_commonName, sslUser.data(),
411 static_cast<int>(sslUser.size()));
412
413 if (status == -1)
414 {
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100415 BMCWEB_LOG_DEBUG
416 << this << " TLS cannot get username to create session";
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100417 return true;
418 }
419
420 size_t lastChar = sslUser.find('\0');
421 if (lastChar == std::string::npos || lastChar == 0)
422 {
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100423 BMCWEB_LOG_DEBUG << this << " Invalid TLS user name";
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100424 return true;
425 }
426 sslUser.resize(lastChar);
427
428 session = persistent_data::SessionStore::getInstance()
429 .generateUserSession(
430 sslUser,
431 crow::persistent_data::PersistenceType::TIMEOUT);
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100432 if (auto sp = session.lock())
433 {
434 BMCWEB_LOG_DEBUG << this
435 << " Generating TLS session: " << sp->uniqueId;
436 }
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100437 return true;
438 });
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200439#endif // BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
440
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700441#ifdef BMCWEB_ENABLE_DEBUG
Ed Tanous1abe55e2018-09-05 08:30:59 -0700442 connectionCount++;
443 BMCWEB_LOG_DEBUG << this << " Connection open, total "
444 << connectionCount;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700445#endif
Ed Tanous1abe55e2018-09-05 08:30:59 -0700446 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700447
Ed Tanous1abe55e2018-09-05 08:30:59 -0700448 ~Connection()
449 {
450 res.completeRequestHandler = nullptr;
451 cancelDeadlineTimer();
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700452#ifdef BMCWEB_ENABLE_DEBUG
Ed Tanous1abe55e2018-09-05 08:30:59 -0700453 connectionCount--;
454 BMCWEB_LOG_DEBUG << this << " Connection closed, total "
455 << connectionCount;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700456#endif
Ed Tanous7045c8d2017-04-03 10:04:37 -0700457 }
458
Ed Tanousceac6f72018-12-02 11:58:47 -0800459 Adaptor& socket()
Ed Tanous1abe55e2018-09-05 08:30:59 -0700460 {
Ed Tanousceac6f72018-12-02 11:58:47 -0800461 return adaptor;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700462 }
463
Ed Tanous1abe55e2018-09-05 08:30:59 -0700464 void start()
465 {
Ed Tanous7045c8d2017-04-03 10:04:37 -0700466
Ed Tanousceac6f72018-12-02 11:58:47 -0800467 startDeadline();
468 // TODO(ed) Abstract this to a more clever class with the idea of an
469 // asynchronous "start"
470 if constexpr (std::is_same_v<Adaptor,
471 boost::beast::ssl_stream<
472 boost::asio::ip::tcp::socket>>)
473 {
Jan Sowinskic00500b2019-12-03 10:37:06 +0100474 adaptor.async_handshake(boost::asio::ssl::stream_base::server,
475 [this, self(shared_from_this())](
476 const boost::system::error_code& ec) {
477 if (ec)
478 {
479 return;
480 }
481 doReadHeaders();
482 });
Ed Tanousceac6f72018-12-02 11:58:47 -0800483 }
484 else
485 {
486 doReadHeaders();
487 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700488 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700489
Ed Tanous1abe55e2018-09-05 08:30:59 -0700490 void handle()
491 {
492 cancelDeadlineTimer();
493 bool isInvalidRequest = false;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700494
Ed Tanous1abe55e2018-09-05 08:30:59 -0700495 // Check for HTTP version 1.1.
496 if (req->version() == 11)
497 {
498 if (req->getHeaderValue(boost::beast::http::field::host).empty())
499 {
500 isInvalidRequest = true;
Ed Tanousde5c9f32019-03-26 09:17:55 -0700501 res.result(boost::beast::http::status::bad_request);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700502 }
503 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700504
Ed Tanouse278c182019-03-13 16:23:37 -0700505 BMCWEB_LOG_INFO << "Request: "
506 << " " << this << " HTTP/" << req->version() / 10 << "."
507 << req->version() % 10 << ' ' << req->methodString()
508 << " " << req->target();
Ed Tanous7045c8d2017-04-03 10:04:37 -0700509
Ed Tanous1abe55e2018-09-05 08:30:59 -0700510 needToCallAfterHandlers = false;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700511
Ed Tanous1abe55e2018-09-05 08:30:59 -0700512 if (!isInvalidRequest)
513 {
514 res.completeRequestHandler = [] {};
Ed Tanouse278c182019-03-13 16:23:37 -0700515 res.isAliveHelper = [this]() -> bool { return isAlive(); };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700516
Ed Tanous1abe55e2018-09-05 08:30:59 -0700517 ctx = detail::Context<Middlewares...>();
Ed Tanouse278c182019-03-13 16:23:37 -0700518 req->middlewareContext = static_cast<void*>(&ctx);
519 req->ioService = static_cast<decltype(req->ioService)>(
520 &adaptor.get_executor().context());
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200521
522#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
523 if (auto sp = session.lock())
524 {
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100525 // set cookie only if this is req from the browser.
526 if (req->getHeaderValue("User-Agent").empty())
527 {
528 BMCWEB_LOG_DEBUG << this << " TLS session: " << sp->uniqueId
529 << " will be used for this request.";
530 req->session = sp;
531 }
532 else
533 {
534 std::string_view cookieValue =
535 req->getHeaderValue("Cookie");
536 if (cookieValue.empty() ||
537 cookieValue.find("SESSION=") == std::string::npos)
538 {
539 res.addHeader("Set-Cookie",
540 "XSRF-TOKEN=" + sp->csrfToken +
541 "; Secure\r\nSet-Cookie: SESSION=" +
542 sp->sessionToken +
Zbigniew Kurzynski26139a52019-12-11 19:11:18 +0100543 "; Secure; HttpOnly\r\nSet-Cookie: "
544 "IsAuthenticated=true; Secure");
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100545 BMCWEB_LOG_DEBUG
546 << this << " TLS session: " << sp->uniqueId
547 << " with cookie will be used for this request.";
548 req->session = sp;
549 }
550 }
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200551 }
552#endif // BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
553
Ed Tanous1abe55e2018-09-05 08:30:59 -0700554 detail::middlewareCallHelper<
Ed Tanous271584a2019-07-09 16:24:22 -0700555 0U, decltype(ctx), decltype(*middlewares), Middlewares...>(
Ed Tanous1abe55e2018-09-05 08:30:59 -0700556 *middlewares, *req, res, ctx);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700557
Ed Tanous1abe55e2018-09-05 08:30:59 -0700558 if (!res.completed)
559 {
Jan Sowinskic00500b2019-12-03 10:37:06 +0100560 needToCallAfterHandlers = true;
561 res.completeRequestHandler = [self(shared_from_this())] {
562 self->completeRequest();
563 };
Ed Tanous1abe55e2018-09-05 08:30:59 -0700564 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));
Jan Sowinskic00500b2019-12-03 10:37:06 +0100570 // delete lambda with self shared_ptr
571 // to enable connection destruction
572 res.completeRequestHandler = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700573 return;
574 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700575 handler->handle(*req, res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700576 }
577 else
578 {
579 completeRequest();
580 }
581 }
582 else
583 {
584 completeRequest();
585 }
586 }
Ed Tanouse0d918b2018-03-27 17:41:04 -0700587
Ed Tanouse278c182019-03-13 16:23:37 -0700588 bool isAlive()
589 {
590
591 if constexpr (std::is_same_v<Adaptor,
592 boost::beast::ssl_stream<
593 boost::asio::ip::tcp::socket>>)
594 {
595 return adaptor.next_layer().is_open();
596 }
597 else
598 {
599 return adaptor.is_open();
600 }
601 }
602 void close()
603 {
Ed Tanouse278c182019-03-13 16:23:37 -0700604 if constexpr (std::is_same_v<Adaptor,
605 boost::beast::ssl_stream<
606 boost::asio::ip::tcp::socket>>)
607 {
608 adaptor.next_layer().close();
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200609#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
610 if (auto sp = session.lock())
611 {
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100612 BMCWEB_LOG_DEBUG << this
613 << " Removing TLS session: " << sp->uniqueId;
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200614 persistent_data::SessionStore::getInstance().removeSession(sp);
615 }
616#endif // BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
Ed Tanouse278c182019-03-13 16:23:37 -0700617 }
618 else
619 {
620 adaptor.close();
621 }
622 }
623
Ed Tanous1abe55e2018-09-05 08:30:59 -0700624 void completeRequest()
625 {
626 BMCWEB_LOG_INFO << "Response: " << this << ' ' << req->url << ' '
627 << res.resultInt() << " keepalive=" << req->keepAlive();
Ed Tanous7045c8d2017-04-03 10:04:37 -0700628
Ed Tanous1abe55e2018-09-05 08:30:59 -0700629 if (needToCallAfterHandlers)
630 {
631 needToCallAfterHandlers = false;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700632
Ed Tanous1abe55e2018-09-05 08:30:59 -0700633 // call all afterHandler of middlewares
Ed Tanous271584a2019-07-09 16:24:22 -0700634 detail::afterHandlersCallHelper<sizeof...(Middlewares) - 1,
Ed Tanousb01bf292019-03-25 19:25:26 +0000635 decltype(ctx),
636 decltype(*middlewares)>(
637 *middlewares, ctx, *req, res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700638 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700639
Ed Tanouse278c182019-03-13 16:23:37 -0700640 if (!isAlive())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700641 {
642 // BMCWEB_LOG_DEBUG << this << " delete (socket is closed) " <<
643 // isReading
644 // << ' ' << isWriting;
645 // delete this;
Jan Sowinskic00500b2019-12-03 10:37:06 +0100646
647 // delete lambda with self shared_ptr
648 // to enable connection destruction
649 res.completeRequestHandler = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700650 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();
Jan Sowinskic00500b2019-12-03 10:37:06 +0100686
687 // delete lambda with self shared_ptr
688 // to enable connection destruction
689 res.completeRequestHandler = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700690 }
691
692 private:
693 void doReadHeaders()
694 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700695 BMCWEB_LOG_DEBUG << this << " doReadHeaders";
696
697 // Clean up any previous Connection.
698 boost::beast::http::async_read_header(
Ed Tanousceac6f72018-12-02 11:58:47 -0800699 adaptor, buffer, *parser,
Jan Sowinskic00500b2019-12-03 10:37:06 +0100700 [this,
701 self(shared_from_this())](const boost::system::error_code& ec,
702 std::size_t bytes_transferred) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700703 BMCWEB_LOG_ERROR << this << " async_read_header "
704 << bytes_transferred << " Bytes";
705 bool errorWhileReading = false;
706 if (ec)
707 {
708 errorWhileReading = true;
709 BMCWEB_LOG_ERROR
710 << this << " Error while reading: " << ec.message();
711 }
712 else
713 {
714 // if the adaptor isn't open anymore, and wasn't handed to a
715 // websocket, treat as an error
Ed Tanouse278c182019-03-13 16:23:37 -0700716 if (!isAlive() && !req->isUpgrade())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700717 {
718 errorWhileReading = true;
719 }
720 }
721
722 if (errorWhileReading)
723 {
724 cancelDeadlineTimer();
Ed Tanouse278c182019-03-13 16:23:37 -0700725 close();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700726 BMCWEB_LOG_DEBUG << this << " from read(1)";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700727 return;
728 }
729
730 // Compute the url parameters for the request
731 req->url = req->target();
732 std::size_t index = req->url.find("?");
Ed Tanous39e77502019-03-04 17:35:53 -0800733 if (index != std::string_view::npos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700734 {
Jason M. Bills43fcbe52018-10-16 15:19:20 -0700735 req->url = req->url.substr(0, index);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700736 }
737 req->urlParams = QueryString(std::string(req->target()));
738 doRead();
739 });
740 }
741
742 void doRead()
743 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700744 BMCWEB_LOG_DEBUG << this << " doRead";
745
746 boost::beast::http::async_read(
Ed Tanousceac6f72018-12-02 11:58:47 -0800747 adaptor, buffer, *parser,
Jan Sowinskic00500b2019-12-03 10:37:06 +0100748 [this,
749 self(shared_from_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";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700753
754 bool errorWhileReading = false;
755 if (ec)
756 {
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100757 BMCWEB_LOG_ERROR
758 << this << " Error while reading: " << ec.message();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700759 errorWhileReading = true;
760 }
761 else
762 {
Ed Tanouse278c182019-03-13 16:23:37 -0700763 if (!isAlive())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700764 {
765 errorWhileReading = true;
766 }
767 }
768 if (errorWhileReading)
769 {
770 cancelDeadlineTimer();
Ed Tanouse278c182019-03-13 16:23:37 -0700771 close();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700772 BMCWEB_LOG_DEBUG << this << " from read(1)";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700773 return;
774 }
775 handle();
776 });
777 }
778
779 void doWrite()
780 {
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100781 BMCWEB_LOG_DEBUG << this << " doWrite";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700782 res.preparePayload();
783 serializer.emplace(*res.stringResponse);
784 boost::beast::http::async_write(
Ed Tanousceac6f72018-12-02 11:58:47 -0800785 adaptor, *serializer,
Jan Sowinskic00500b2019-12-03 10:37:06 +0100786 [this,
787 self(shared_from_this())](const boost::system::error_code& ec,
788 std::size_t bytes_transferred) {
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100789 BMCWEB_LOG_DEBUG << this << " async_write " << bytes_transferred
Ed Tanous1abe55e2018-09-05 08:30:59 -0700790 << " bytes";
791
792 if (ec)
793 {
794 BMCWEB_LOG_DEBUG << this << " from write(2)";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700795 return;
796 }
Ed Tanousceac6f72018-12-02 11:58:47 -0800797 if (!res.keepAlive())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700798 {
Ed Tanouse278c182019-03-13 16:23:37 -0700799 close();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700800 BMCWEB_LOG_DEBUG << this << " from write(1)";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700801 return;
802 }
803
804 serializer.reset();
805 BMCWEB_LOG_DEBUG << this << " Clearing response";
806 res.clear();
807 parser.emplace(std::piecewise_construct, std::make_tuple());
808 parser->body_limit(httpReqBodyLimit); // reset body limit for
809 // newly created parser
810 buffer.consume(buffer.size());
811
812 req.emplace(parser->get());
813 doReadHeaders();
814 });
815 }
816
Ed Tanous1abe55e2018-09-05 08:30:59 -0700817 void cancelDeadlineTimer()
818 {
Jan Sowinskic00500b2019-12-03 10:37:06 +0100819 if (timerCancelKey)
820 {
821 BMCWEB_LOG_DEBUG << this << " timer cancelled: " << &timerQueue
822 << ' ' << *timerCancelKey;
823 timerQueue.cancel(*timerCancelKey);
824 timerCancelKey.reset();
825 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700826 }
827
828 void startDeadline()
829 {
830 cancelDeadlineTimer();
831
Jan Sowinskic00500b2019-12-03 10:37:06 +0100832 timerCancelKey = timerQueue.add([this, self(shared_from_this())] {
833 // Mark timer as not active to avoid canceling it during
834 // Connection destructor which leads to double free issue
835 timerCancelKey.reset();
Ed Tanouse278c182019-03-13 16:23:37 -0700836 if (!isAlive())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700837 {
838 return;
839 }
Ed Tanouse278c182019-03-13 16:23:37 -0700840 close();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700841 });
842 BMCWEB_LOG_DEBUG << this << " timer added: " << &timerQueue << ' '
Jan Sowinskic00500b2019-12-03 10:37:06 +0100843 << *timerCancelKey;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700844 }
845
846 private:
847 Adaptor adaptor;
848 Handler* handler;
849
Ed Tanousa24526d2018-12-10 15:17:59 -0800850 // Making this a std::optional allows it to be efficiently destroyed and
Ed Tanous1abe55e2018-09-05 08:30:59 -0700851 // re-created on Connection reset
Ed Tanousa24526d2018-12-10 15:17:59 -0800852 std::optional<
Ed Tanous1abe55e2018-09-05 08:30:59 -0700853 boost::beast::http::request_parser<boost::beast::http::string_body>>
854 parser;
855
Ed Tanous3112a142018-11-29 15:45:10 -0800856 boost::beast::flat_static_buffer<8192> buffer;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700857
Ed Tanousa24526d2018-12-10 15:17:59 -0800858 std::optional<boost::beast::http::response_serializer<
Ed Tanous1abe55e2018-09-05 08:30:59 -0700859 boost::beast::http::string_body>>
860 serializer;
861
Ed Tanousa24526d2018-12-10 15:17:59 -0800862 std::optional<crow::Request> req;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700863 crow::Response res;
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200864#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
865 std::weak_ptr<crow::persistent_data::UserSession> session;
866#endif // BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
Ed Tanous1abe55e2018-09-05 08:30:59 -0700867
868 const std::string& serverName;
869
Jan Sowinskic00500b2019-12-03 10:37:06 +0100870 std::optional<size_t> timerCancelKey;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700871
Ed Tanous1abe55e2018-09-05 08:30:59 -0700872 bool needToCallAfterHandlers{};
873 bool needToStartReadAfterComplete{};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700874
875 std::tuple<Middlewares...>* middlewares;
876 detail::Context<Middlewares...> ctx;
877
878 std::function<std::string()>& getCachedDateStr;
879 detail::TimerQueue& timerQueue;
Jan Sowinskic00500b2019-12-03 10:37:06 +0100880
881 using std::enable_shared_from_this<
882 Connection<Adaptor, Handler, Middlewares...>>::shared_from_this;
Ed Tanous3112a142018-11-29 15:45:10 -0800883};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700884} // namespace crow