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