xref: /openbmc/bmcweb/http/http_connection.hpp (revision d78572018fc2022091ff8b8eb5a7fef2172ba3d6)
1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3 #pragma once
4 #include "bmcweb_config.h"
5 
6 #include "async_resp.hpp"
7 #include "authentication.hpp"
8 #include "complete_response_fields.hpp"
9 #include "forward_unauthorized.hpp"
10 #include "http2_connection.hpp"
11 #include "http_body.hpp"
12 #include "http_request.hpp"
13 #include "http_response.hpp"
14 #include "http_utility.hpp"
15 #include "logging.hpp"
16 #include "mutual_tls.hpp"
17 #include "sessions.hpp"
18 #include "str_utility.hpp"
19 
20 #include <boost/asio/error.hpp>
21 #include <boost/asio/ip/tcp.hpp>
22 #include <boost/asio/ssl/stream.hpp>
23 #include <boost/asio/ssl/stream_base.hpp>
24 #include <boost/asio/ssl/verify_context.hpp>
25 #include <boost/asio/steady_timer.hpp>
26 #include <boost/beast/_experimental/test/stream.hpp>
27 #include <boost/beast/core/buffers_generator.hpp>
28 #include <boost/beast/core/flat_static_buffer.hpp>
29 #include <boost/beast/http/error.hpp>
30 #include <boost/beast/http/field.hpp>
31 #include <boost/beast/http/message_generator.hpp>
32 #include <boost/beast/http/parser.hpp>
33 #include <boost/beast/http/read.hpp>
34 #include <boost/beast/http/status.hpp>
35 #include <boost/beast/http/verb.hpp>
36 #include <boost/none.hpp>
37 #include <boost/optional/optional.hpp>
38 
39 #include <bit>
40 #include <chrono>
41 #include <cstddef>
42 #include <cstdint>
43 #include <functional>
44 #include <memory>
45 #include <optional>
46 #include <string>
47 #include <string_view>
48 #include <system_error>
49 #include <type_traits>
50 #include <utility>
51 
52 namespace crow
53 {
54 
55 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
56 static int connectionCount = 0;
57 
58 // request body limit size set by the BMCWEB_HTTP_BODY_LIMIT option
59 constexpr uint64_t httpReqBodyLimit = 1024UL * 1024UL * BMCWEB_HTTP_BODY_LIMIT;
60 
61 constexpr uint64_t loggedOutPostBodyLimit = 4096U;
62 
63 constexpr uint32_t httpHeaderLimit = 8192U;
64 
65 template <typename>
66 struct IsTls : std::false_type
67 {};
68 
69 template <typename T>
70 struct IsTls<boost::asio::ssl::stream<T>> : std::true_type
71 {};
72 
73 template <typename Adaptor, typename Handler>
74 class Connection :
75     public std::enable_shared_from_this<Connection<Adaptor, Handler>>
76 {
77     using self_type = Connection<Adaptor, Handler>;
78 
79   public:
Connection(Handler * handlerIn,boost::asio::steady_timer && timerIn,std::function<std::string ()> & getCachedDateStrF,Adaptor && adaptorIn)80     Connection(Handler* handlerIn, boost::asio::steady_timer&& timerIn,
81                std::function<std::string()>& getCachedDateStrF,
82                Adaptor&& adaptorIn) :
83         adaptor(std::move(adaptorIn)), handler(handlerIn),
84         timer(std::move(timerIn)), getCachedDateStr(getCachedDateStrF)
85     {
86         initParser();
87 
88         connectionCount++;
89 
90         BMCWEB_LOG_DEBUG("{} Connection created, total {}", logPtr(this),
91                          connectionCount);
92     }
93 
~Connection()94     ~Connection()
95     {
96         res.releaseCompleteRequestHandler();
97         cancelDeadlineTimer();
98 
99         connectionCount--;
100         BMCWEB_LOG_DEBUG("{} Connection closed, total {}", logPtr(this),
101                          connectionCount);
102     }
103 
104     Connection(const Connection&) = delete;
105     Connection(Connection&&) = delete;
106     Connection& operator=(const Connection&) = delete;
107     Connection& operator=(Connection&&) = delete;
108 
tlsVerifyCallback(bool preverified,boost::asio::ssl::verify_context & ctx)109     bool tlsVerifyCallback(bool preverified,
110                            boost::asio::ssl::verify_context& ctx)
111     {
112         BMCWEB_LOG_DEBUG("{} tlsVerifyCallback called with preverified {}",
113                          logPtr(this), preverified);
114         if (preverified)
115         {
116             mtlsSession = verifyMtlsUser(ip, ctx);
117             if (mtlsSession)
118             {
119                 BMCWEB_LOG_DEBUG("{} Generated TLS session: {}", logPtr(this),
120                                  mtlsSession->uniqueId);
121             }
122         }
123         const persistent_data::AuthConfigMethods& c =
124             persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
125         if (c.tlsStrict)
126         {
127             BMCWEB_LOG_DEBUG(
128                 "{} TLS is in strict mode, returning preverified as is.",
129                 logPtr(this));
130             return preverified;
131         }
132         // If tls strict mode is disabled
133         // We always return true to allow full auth flow for resources that
134         // don't require auth
135         return true;
136     }
137 
prepareMutualTls()138     bool prepareMutualTls()
139     {
140         if constexpr (IsTls<Adaptor>::value)
141         {
142             BMCWEB_LOG_DEBUG("prepareMutualTls");
143 
144             constexpr std::string_view id = "bmcweb";
145 
146             const char* idPtr = id.data();
147             const auto* idCPtr = std::bit_cast<const unsigned char*>(idPtr);
148             auto idLen = static_cast<unsigned int>(id.length());
149             int ret = SSL_set_session_id_context(adaptor.native_handle(),
150                                                  idCPtr, idLen);
151             if (ret == 0)
152             {
153                 BMCWEB_LOG_ERROR("{} failed to set SSL id", logPtr(this));
154                 return false;
155             }
156 
157             BMCWEB_LOG_DEBUG("set_verify_callback");
158 
159             boost::system::error_code ec;
160             adaptor.set_verify_callback(
161                 std::bind_front(&self_type::tlsVerifyCallback, this), ec);
162             if (ec)
163             {
164                 BMCWEB_LOG_ERROR("Failed to set verify callback {}", ec);
165                 return false;
166             }
167         }
168 
169         return true;
170     }
171 
start()172     void start()
173     {
174         BMCWEB_LOG_DEBUG("{} Connection started, total {}", logPtr(this),
175                          connectionCount);
176         if (connectionCount >= 200)
177         {
178             BMCWEB_LOG_CRITICAL("{} Max connection count exceeded.",
179                                 logPtr(this));
180             return;
181         }
182 
183         if constexpr (BMCWEB_MUTUAL_TLS_AUTH)
184         {
185             if (!prepareMutualTls())
186             {
187                 BMCWEB_LOG_ERROR("{} Failed to prepare mTLS", logPtr(this));
188                 return;
189             }
190         }
191 
192         startDeadline();
193 
194         readClientIp();
195 
196         // TODO(ed) Abstract this to a more clever class with the idea of an
197         // asynchronous "start"
198         if constexpr (IsTls<Adaptor>::value)
199         {
200             adaptor.async_handshake(boost::asio::ssl::stream_base::server,
201                                     [this, self(shared_from_this())](
202                                         const boost::system::error_code& ec) {
203                                         if (ec)
204                                         {
205                                             return;
206                                         }
207                                         afterSslHandshake();
208                                     });
209         }
210         else
211         {
212             doReadHeaders();
213         }
214     }
215 
afterSslHandshake()216     void afterSslHandshake()
217     {
218         // If http2 is enabled, negotiate the protocol
219         if constexpr (BMCWEB_EXPERIMENTAL_HTTP2)
220         {
221             const unsigned char* alpn = nullptr;
222             unsigned int alpnlen = 0;
223             SSL_get0_alpn_selected(adaptor.native_handle(), &alpn, &alpnlen);
224             if (alpn != nullptr)
225             {
226                 std::string_view selectedProtocol(
227                     std::bit_cast<const char*>(alpn), alpnlen);
228                 BMCWEB_LOG_DEBUG("ALPN selected protocol \"{}\" len: {}",
229                                  selectedProtocol, alpnlen);
230                 if (selectedProtocol == "h2")
231                 {
232                     auto http2 =
233                         std::make_shared<HTTP2Connection<Adaptor, Handler>>(
234                             std::move(adaptor), handler, getCachedDateStr);
235                     http2->start();
236                     return;
237                 }
238             }
239         }
240 
241         doReadHeaders();
242     }
243 
initParser()244     void initParser()
245     {
246         boost::beast::http::request_parser<bmcweb::HttpBody>& instance =
247             parser.emplace();
248 
249         // reset header limit for newly created parser
250         instance.header_limit(httpHeaderLimit);
251 
252         // Initially set no body limit. We don't yet know if the user is
253         // authenticated.
254         instance.body_limit(boost::none);
255     }
256 
handle()257     void handle()
258     {
259         std::error_code reqEc;
260         if (!parser)
261         {
262             return;
263         }
264         req = std::make_shared<crow::Request>(parser->release(), reqEc);
265         if (reqEc)
266         {
267             BMCWEB_LOG_DEBUG("Request failed to construct{}", reqEc.message());
268             res.result(boost::beast::http::status::bad_request);
269             completeRequest(res);
270             return;
271         }
272         req->session = userSession;
273         accept = req->getHeaderValue("Accept");
274         // Fetch the client IP address
275         req->ipAddress = ip;
276 
277         // Check for HTTP version 1.1.
278         if (req->version() == 11)
279         {
280             if (req->getHeaderValue(boost::beast::http::field::host).empty())
281             {
282                 res.result(boost::beast::http::status::bad_request);
283                 completeRequest(res);
284                 return;
285             }
286         }
287 
288         BMCWEB_LOG_INFO("Request:  {} HTTP/{}.{} {} {} {}", logPtr(this),
289                         req->version() / 10, req->version() % 10,
290                         req->methodString(), req->target(),
291                         req->ipAddress.to_string());
292 
293         req->ioService = static_cast<decltype(req->ioService)>(
294             &adaptor.get_executor().context());
295 
296         if (res.completed)
297         {
298             completeRequest(res);
299             return;
300         }
301         keepAlive = req->keepAlive();
302         if constexpr (!std::is_same_v<Adaptor, boost::beast::test::stream>)
303         {
304             if constexpr (!BMCWEB_INSECURE_DISABLE_AUTH)
305             {
306                 if (!crow::authentication::isOnAllowlist(req->url().path(),
307                                                          req->method()) &&
308                     req->session == nullptr)
309                 {
310                     BMCWEB_LOG_WARNING("Authentication failed");
311                     forward_unauthorized::sendUnauthorized(
312                         req->url().encoded_path(),
313                         req->getHeaderValue("X-Requested-With"),
314                         req->getHeaderValue("Accept"), res);
315                     completeRequest(res);
316                     return;
317                 }
318             }
319         }
320         auto asyncResp = std::make_shared<bmcweb::AsyncResp>();
321         BMCWEB_LOG_DEBUG("Setting completion handler");
322         asyncResp->res.setCompleteRequestHandler(
323             [self(shared_from_this())](crow::Response& thisRes) {
324                 self->completeRequest(thisRes);
325             });
326         bool isSse =
327             isContentTypeAllowed(req->getHeaderValue("Accept"),
328                                  http_helpers::ContentType::EventStream, false);
329         std::string_view upgradeType(
330             req->getHeaderValue(boost::beast::http::field::upgrade));
331         if ((req->isUpgrade() &&
332              bmcweb::asciiIEquals(upgradeType, "websocket")) ||
333             isSse)
334         {
335             asyncResp->res.setCompleteRequestHandler(
336                 [self(shared_from_this())](crow::Response& thisRes) {
337                     if (thisRes.result() != boost::beast::http::status::ok)
338                     {
339                         // When any error occurs before handle upgradation,
340                         // the result in response will be set to respective
341                         // error. By default the Result will be OK (200),
342                         // which implies successful handle upgrade. Response
343                         // needs to be sent over this connection only on
344                         // failure.
345                         self->completeRequest(thisRes);
346                         return;
347                     }
348                 });
349             handler->handleUpgrade(req, asyncResp, std::move(adaptor));
350             return;
351         }
352         std::string_view expected =
353             req->getHeaderValue(boost::beast::http::field::if_none_match);
354         if (!expected.empty())
355         {
356             res.setExpectedHash(expected);
357         }
358         handler->handle(req, asyncResp);
359     }
360 
hardClose()361     void hardClose()
362     {
363         if (mtlsSession != nullptr)
364         {
365             BMCWEB_LOG_DEBUG("{} Removing TLS session: {}", logPtr(this),
366                              mtlsSession->uniqueId);
367             persistent_data::SessionStore::getInstance().removeSession(
368                 mtlsSession);
369         }
370         BMCWEB_LOG_DEBUG("{} Closing socket", logPtr(this));
371         boost::beast::get_lowest_layer(adaptor).close();
372     }
373 
tlsShutdownComplete(const std::shared_ptr<self_type> & self,const boost::system::error_code & ec)374     void tlsShutdownComplete(const std::shared_ptr<self_type>& self,
375                              const boost::system::error_code& ec)
376     {
377         if (ec)
378         {
379             BMCWEB_LOG_WARNING("{} Failed to shut down TLS cleanly {}",
380                                logPtr(self.get()), ec);
381         }
382         self->hardClose();
383     }
384 
gracefulClose()385     void gracefulClose()
386     {
387         BMCWEB_LOG_DEBUG("{} Socket close requested", logPtr(this));
388 
389         if constexpr (IsTls<Adaptor>::value)
390         {
391             adaptor.async_shutdown(std::bind_front(
392                 &self_type::tlsShutdownComplete, this, shared_from_this()));
393         }
394         else
395         {
396             hardClose();
397         }
398     }
399 
completeRequest(crow::Response & thisRes)400     void completeRequest(crow::Response& thisRes)
401     {
402         res = std::move(thisRes);
403         res.keepAlive(keepAlive);
404 
405         completeResponseFields(accept, res);
406         res.addHeader(boost::beast::http::field::date, getCachedDateStr());
407 
408         doWrite();
409 
410         // delete lambda with self shared_ptr
411         // to enable connection destruction
412         res.setCompleteRequestHandler(nullptr);
413     }
414 
readClientIp()415     void readClientIp()
416     {
417         boost::system::error_code ec;
418 
419         if constexpr (!std::is_same_v<Adaptor, boost::beast::test::stream>)
420         {
421             boost::asio::ip::tcp::endpoint endpoint =
422                 boost::beast::get_lowest_layer(adaptor).remote_endpoint(ec);
423 
424             if (ec)
425             {
426                 // If remote endpoint fails keep going. "ClientOriginIPAddress"
427                 // will be empty.
428                 BMCWEB_LOG_ERROR(
429                     "Failed to get the client's IP Address. ec : {}", ec);
430                 return;
431             }
432             ip = endpoint.address();
433         }
434     }
435 
436   private:
getContentLengthLimit()437     uint64_t getContentLengthLimit()
438     {
439         if constexpr (!BMCWEB_INSECURE_DISABLE_AUTH)
440         {
441             if (userSession == nullptr)
442             {
443                 return loggedOutPostBodyLimit;
444             }
445         }
446 
447         return httpReqBodyLimit;
448     }
449 
450     // Returns true if content length was within limits
451     // Returns false if content length error has been returned
handleContentLengthError()452     bool handleContentLengthError()
453     {
454         if (!parser)
455         {
456             BMCWEB_LOG_CRITICAL("Parser was null");
457             return false;
458         }
459         const boost::optional<uint64_t> contentLength =
460             parser->content_length();
461         if (!contentLength)
462         {
463             BMCWEB_LOG_DEBUG("{} No content length available", logPtr(this));
464             return true;
465         }
466 
467         uint64_t maxAllowedContentLength = getContentLengthLimit();
468 
469         if (*contentLength > maxAllowedContentLength)
470         {
471             // If the users content limit is between the logged in
472             // and logged out limits They probably just didn't log
473             // in
474             if (*contentLength > loggedOutPostBodyLimit &&
475                 *contentLength < httpReqBodyLimit)
476             {
477                 BMCWEB_LOG_DEBUG(
478                     "{} Content length {} valid, but greater than logged out"
479                     " limit of {}. Setting unauthorized",
480                     logPtr(this), *contentLength, loggedOutPostBodyLimit);
481                 res.result(boost::beast::http::status::unauthorized);
482             }
483             else
484             {
485                 // Otherwise they're over both limits, so inform
486                 // them
487                 BMCWEB_LOG_DEBUG(
488                     "{} Content length {} was greater than global limit {}."
489                     " Setting payload too large",
490                     logPtr(this), *contentLength, httpReqBodyLimit);
491                 res.result(boost::beast::http::status::payload_too_large);
492             }
493 
494             keepAlive = false;
495             doWrite();
496             return false;
497         }
498 
499         return true;
500     }
501 
afterReadHeaders(const std::shared_ptr<self_type> &,const boost::system::error_code & ec,std::size_t bytesTransferred)502     void afterReadHeaders(const std::shared_ptr<self_type>& /*self*/,
503                           const boost::system::error_code& ec,
504                           std::size_t bytesTransferred)
505     {
506         BMCWEB_LOG_DEBUG("{} async_read_header {} Bytes", logPtr(this),
507                          bytesTransferred);
508 
509         if (ec)
510         {
511             cancelDeadlineTimer();
512 
513             if (ec == boost::beast::http::error::header_limit)
514             {
515                 BMCWEB_LOG_ERROR("{} Header field too large, closing",
516                                  logPtr(this), ec.message());
517 
518                 res.result(boost::beast::http::status::
519                                request_header_fields_too_large);
520                 keepAlive = false;
521                 doWrite();
522                 return;
523             }
524             if (ec == boost::beast::http::error::end_of_stream)
525             {
526                 BMCWEB_LOG_WARNING("{} End of stream, closing {}", logPtr(this),
527                                    ec);
528                 hardClose();
529                 return;
530             }
531 
532             BMCWEB_LOG_DEBUG("{} Closing socket due to read error {}",
533                              logPtr(this), ec.message());
534             gracefulClose();
535 
536             return;
537         }
538 
539         if (!parser)
540         {
541             BMCWEB_LOG_ERROR("Parser was unexpectedly null");
542             return;
543         }
544 
545         constexpr bool isTest =
546             std::is_same_v<Adaptor, boost::beast::test::stream>;
547 
548         if constexpr (!BMCWEB_INSECURE_DISABLE_AUTH && !isTest)
549         {
550             boost::beast::http::verb method = parser->get().method();
551             userSession = crow::authentication::authenticate(
552                 ip, res, method, parser->get().base(), mtlsSession);
553         }
554 
555         std::string_view expect =
556             parser->get()[boost::beast::http::field::expect];
557         if (bmcweb::asciiIEquals(expect, "100-continue"))
558         {
559             res.result(boost::beast::http::status::continue_);
560             doWrite();
561             return;
562         }
563 
564         if (!handleContentLengthError())
565         {
566             return;
567         }
568 
569         parser->body_limit(getContentLengthLimit());
570 
571         if (parser->is_done())
572         {
573             handle();
574             return;
575         }
576 
577         doRead();
578     }
579 
doReadHeaders()580     void doReadHeaders()
581     {
582         BMCWEB_LOG_DEBUG("{} doReadHeaders", logPtr(this));
583         if (!parser)
584         {
585             BMCWEB_LOG_CRITICAL("Parser was not initialized.");
586             return;
587         }
588         // Clean up any previous Connection.
589         boost::beast::http::async_read_header(
590             adaptor, buffer, *parser,
591             std::bind_front(&self_type::afterReadHeaders, this,
592                             shared_from_this()));
593     }
594 
afterRead(const std::shared_ptr<self_type> &,const boost::system::error_code & ec,std::size_t bytesTransferred)595     void afterRead(const std::shared_ptr<self_type>& /*self*/,
596                    const boost::system::error_code& ec,
597                    std::size_t bytesTransferred)
598     {
599         BMCWEB_LOG_DEBUG("{} async_read_some {} Bytes", logPtr(this),
600                          bytesTransferred);
601 
602         if (ec)
603         {
604             BMCWEB_LOG_ERROR("{} Error while reading: {}", logPtr(this),
605                              ec.message());
606             if (ec == boost::beast::http::error::body_limit)
607             {
608                 if (handleContentLengthError())
609                 {
610                     BMCWEB_LOG_CRITICAL("Body length limit reached, "
611                                         "but no content-length "
612                                         "available?  Should never happen");
613                     res.result(
614                         boost::beast::http::status::internal_server_error);
615                     keepAlive = false;
616                     doWrite();
617                 }
618                 return;
619             }
620 
621             gracefulClose();
622             return;
623         }
624 
625         // If the user is logged in, allow them to send files
626         // incrementally one piece at a time. If authentication is
627         // disabled then there is no user session hence always allow to
628         // send one piece at a time.
629         if (userSession != nullptr)
630         {
631             cancelDeadlineTimer();
632         }
633 
634         if (!parser)
635         {
636             BMCWEB_LOG_ERROR("Parser was unexpectedly null");
637             return;
638         }
639         if (!parser->is_done())
640         {
641             doRead();
642             return;
643         }
644 
645         cancelDeadlineTimer();
646         handle();
647     }
648 
doRead()649     void doRead()
650     {
651         BMCWEB_LOG_DEBUG("{} doRead", logPtr(this));
652         if (!parser)
653         {
654             return;
655         }
656         startDeadline();
657         boost::beast::http::async_read_some(
658             adaptor, buffer, *parser,
659             std::bind_front(&self_type::afterRead, this, shared_from_this()));
660     }
661 
afterDoWrite(const std::shared_ptr<self_type> &,const boost::system::error_code & ec,std::size_t bytesTransferred)662     void afterDoWrite(const std::shared_ptr<self_type>& /*self*/,
663                       const boost::system::error_code& ec,
664                       std::size_t bytesTransferred)
665     {
666         BMCWEB_LOG_DEBUG("{} async_write wrote {} bytes, ec={}", logPtr(this),
667                          bytesTransferred, ec);
668 
669         cancelDeadlineTimer();
670 
671         if (ec == boost::system::errc::operation_would_block ||
672             ec == boost::system::errc::resource_unavailable_try_again)
673         {
674             doWrite();
675             return;
676         }
677         if (ec)
678         {
679             BMCWEB_LOG_DEBUG("{} from write(2)", logPtr(this));
680             return;
681         }
682 
683         if (res.result() == boost::beast::http::status::continue_)
684         {
685             // Reset the result to ok
686             res.result(boost::beast::http::status::ok);
687             doRead();
688             return;
689         }
690 
691         if (!keepAlive)
692         {
693             BMCWEB_LOG_DEBUG("{} keepalive not set.  Closing socket",
694                              logPtr(this));
695 
696             gracefulClose();
697             return;
698         }
699 
700         BMCWEB_LOG_DEBUG("{} Clearing response", logPtr(this));
701         res.clear();
702         initParser();
703 
704         userSession = nullptr;
705 
706         req->clear();
707         doReadHeaders();
708     }
709 
doWrite()710     void doWrite()
711     {
712         BMCWEB_LOG_DEBUG("{} doWrite", logPtr(this));
713         res.preparePayload();
714 
715         startDeadline();
716         boost::beast::async_write(
717             adaptor,
718             boost::beast::http::message_generator(std::move(res.response)),
719             std::bind_front(&self_type::afterDoWrite, this,
720                             shared_from_this()));
721     }
722 
cancelDeadlineTimer()723     void cancelDeadlineTimer()
724     {
725         timer.cancel();
726     }
727 
afterTimerWait(const std::weak_ptr<self_type> & weakSelf,const boost::system::error_code & ec)728     void afterTimerWait(const std::weak_ptr<self_type>& weakSelf,
729                         const boost::system::error_code& ec)
730     {
731         // Note, we are ignoring other types of errors here;  If the timer
732         // failed for any reason, we should still close the connection
733         std::shared_ptr<Connection<Adaptor, Handler>> self = weakSelf.lock();
734         if (!self)
735         {
736             if (ec == boost::asio::error::operation_aborted)
737             {
738                 BMCWEB_LOG_DEBUG(
739                     "{} Timer canceled on connection being destroyed",
740                     logPtr(self.get()));
741             }
742             else
743             {
744                 BMCWEB_LOG_CRITICAL("{} Failed to capture connection",
745                                     logPtr(self.get()));
746             }
747             return;
748         }
749 
750         self->timerStarted = false;
751 
752         if (ec)
753         {
754             if (ec == boost::asio::error::operation_aborted)
755             {
756                 BMCWEB_LOG_DEBUG("{} Timer canceled", logPtr(self.get()));
757                 return;
758             }
759             BMCWEB_LOG_CRITICAL("{} Timer failed {}", logPtr(self.get()), ec);
760         }
761 
762         BMCWEB_LOG_WARNING("{} Connection timed out, hard closing",
763                            logPtr(self.get()));
764 
765         self->hardClose();
766     }
767 
startDeadline()768     void startDeadline()
769     {
770         // Timer is already started so no further action is required.
771         if (timerStarted)
772         {
773             return;
774         }
775 
776         std::chrono::seconds timeout(15);
777 
778         std::weak_ptr<Connection<Adaptor, Handler>> weakSelf = weak_from_this();
779         timer.expires_after(timeout);
780         timer.async_wait(std::bind_front(&self_type::afterTimerWait, this,
781                                          weak_from_this()));
782 
783         timerStarted = true;
784         BMCWEB_LOG_DEBUG("{} timer started", logPtr(this));
785     }
786 
787     Adaptor adaptor;
788     Handler* handler;
789 
790     boost::asio::ip::address ip;
791 
792     // Making this a std::optional allows it to be efficiently destroyed and
793     // re-created on Connection reset
794     std::optional<boost::beast::http::request_parser<bmcweb::HttpBody>> parser;
795 
796     boost::beast::flat_static_buffer<8192> buffer;
797 
798     std::shared_ptr<crow::Request> req;
799     std::string accept;
800 
801     crow::Response res;
802 
803     std::shared_ptr<persistent_data::UserSession> userSession;
804     std::shared_ptr<persistent_data::UserSession> mtlsSession;
805 
806     boost::asio::steady_timer timer;
807 
808     bool keepAlive = true;
809 
810     bool timerStarted = false;
811 
812     std::function<std::string()>& getCachedDateStr;
813 
814     using std::enable_shared_from_this<
815         Connection<Adaptor, Handler>>::shared_from_this;
816 
817     using std::enable_shared_from_this<
818         Connection<Adaptor, Handler>>::weak_from_this;
819 };
820 } // namespace crow
821