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