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