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