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