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