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 "http_body.hpp" 11 #include "http_connect_types.hpp" 12 #include "http_request.hpp" 13 #include "http_response.hpp" 14 #include "logging.hpp" 15 16 // NOLINTNEXTLINE(misc-include-cleaner) 17 #include "nghttp2_adapters.hpp" 18 #include "sessions.hpp" 19 20 #include <nghttp2/nghttp2.h> 21 #include <unistd.h> 22 23 #include <boost/asio/buffer.hpp> 24 #include <boost/asio/ssl/stream.hpp> 25 #include <boost/beast/core/error.hpp> 26 #include <boost/beast/http/field.hpp> 27 #include <boost/beast/http/fields.hpp> 28 #include <boost/beast/http/message.hpp> 29 #include <boost/beast/http/verb.hpp> 30 #include <boost/optional/optional.hpp> 31 #include <boost/system/error_code.hpp> 32 #include <boost/url/url_view.hpp> 33 34 #include <array> 35 #include <bit> 36 #include <cstddef> 37 #include <cstdint> 38 #include <functional> 39 #include <map> 40 #include <memory> 41 #include <optional> 42 #include <span> 43 #include <string> 44 #include <string_view> 45 #include <type_traits> 46 #include <utility> 47 #include <vector> 48 49 namespace crow 50 { 51 52 struct Http2StreamData 53 { 54 std::shared_ptr<Request> req = std::make_shared<Request>(); 55 std::optional<bmcweb::HttpBody::reader> reqReader; 56 std::string accept; 57 std::string acceptEnc; 58 Response res; 59 std::optional<bmcweb::HttpBody::writer> writer; 60 }; 61 62 template <typename Adaptor, typename Handler> 63 class HTTP2Connection : 64 public std::enable_shared_from_this<HTTP2Connection<Adaptor, Handler>> 65 { 66 using self_type = HTTP2Connection<Adaptor, Handler>; 67 68 public: HTTP2Connection(boost::asio::ssl::stream<Adaptor> && adaptorIn,Handler * handlerIn,std::function<std::string ()> & getCachedDateStrF,HttpType httpTypeIn,const std::shared_ptr<persistent_data::UserSession> & mtlsSessionIn)69 HTTP2Connection( 70 boost::asio::ssl::stream<Adaptor>&& adaptorIn, Handler* handlerIn, 71 std::function<std::string()>& getCachedDateStrF, HttpType httpTypeIn, 72 const std::shared_ptr<persistent_data::UserSession>& mtlsSessionIn) : 73 httpType(httpTypeIn), adaptor(std::move(adaptorIn)), 74 ngSession(initializeNghttp2Session()), handler(handlerIn), 75 getCachedDateStr(getCachedDateStrF), mtlsSession(mtlsSessionIn) 76 {} 77 start()78 void start() 79 { 80 // Create the control stream 81 streams[0]; 82 83 if (sendServerConnectionHeader() != 0) 84 { 85 BMCWEB_LOG_ERROR("send_server_connection_header failed"); 86 return; 87 } 88 doRead(); 89 } 90 startFromSettings(std::string_view http2UpgradeSettings)91 void startFromSettings(std::string_view http2UpgradeSettings) 92 { 93 int ret = ngSession.sessionUpgrade2(http2UpgradeSettings, 94 false /*head_request*/); 95 if (ret != 0) 96 { 97 BMCWEB_LOG_ERROR("Failed to load upgrade header"); 98 return; 99 } 100 // Create the control stream 101 streams[0]; 102 103 if (sendServerConnectionHeader() != 0) 104 { 105 BMCWEB_LOG_ERROR("send_server_connection_header failed"); 106 return; 107 } 108 doRead(); 109 } 110 sendServerConnectionHeader()111 int sendServerConnectionHeader() 112 { 113 BMCWEB_LOG_DEBUG("send_server_connection_header()"); 114 115 uint32_t maxStreams = 4; 116 117 // Both of these settings were found experimentally to allow a single 118 // fast stream to upload at a rate equivalent to http1.1 They will 119 // likely be tuned in the future. 120 uint32_t maxFrameSize = 1 << 14; 121 uint32_t windowSize = 1 << 20; 122 std::array<nghttp2_settings_entry, 4> iv = {{ 123 {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, maxStreams}, 124 {NGHTTP2_SETTINGS_ENABLE_PUSH, 0}, 125 // Set an approximately 1MB window size 126 {NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, windowSize}, 127 {NGHTTP2_SETTINGS_MAX_FRAME_SIZE, maxFrameSize}, 128 }}; 129 if (ngSession.setLocalWindowSize(NGHTTP2_FLAG_NONE, 0, 1 << 20) != 0) 130 { 131 BMCWEB_LOG_ERROR("Failed to set local window size"); 132 } 133 int rv = ngSession.submitSettings(iv); 134 if (rv != 0) 135 { 136 BMCWEB_LOG_ERROR("Fatal error: {}", nghttp2_strerror(rv)); 137 return -1; 138 } 139 writeBuffer(); 140 return 0; 141 } 142 fileReadCallback(nghttp2_session *,int32_t streamId,uint8_t * buf,size_t length,uint32_t * dataFlags,nghttp2_data_source *,void * userPtr)143 static ssize_t fileReadCallback( 144 nghttp2_session* /* session */, int32_t streamId, uint8_t* buf, 145 size_t length, uint32_t* dataFlags, nghttp2_data_source* /*source*/, 146 void* userPtr) 147 { 148 self_type& self = userPtrToSelf(userPtr); 149 150 auto streamIt = self.streams.find(streamId); 151 if (streamIt == self.streams.end()) 152 { 153 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; 154 } 155 Http2StreamData& stream = streamIt->second; 156 BMCWEB_LOG_DEBUG("File read callback length: {}", length); 157 if (!stream.writer) 158 { 159 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; 160 } 161 boost::beast::error_code ec; 162 boost::optional<std::pair<boost::asio::const_buffer, bool>> out = 163 stream.writer->getWithMaxSize(ec, length); 164 if (ec) 165 { 166 BMCWEB_LOG_CRITICAL("Failed to get buffer"); 167 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; 168 } 169 if (!out) 170 { 171 BMCWEB_LOG_ERROR("Empty file, setting EOF"); 172 *dataFlags |= NGHTTP2_DATA_FLAG_EOF; 173 return 0; 174 } 175 176 BMCWEB_LOG_DEBUG("Send chunk of size: {}", out->first.size()); 177 if (length < out->first.size()) 178 { 179 BMCWEB_LOG_CRITICAL( 180 "Buffer overflow that should never happen happened"); 181 // Should never happen because of length limit on get() above 182 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; 183 } 184 boost::asio::mutable_buffer writeableBuf(buf, length); 185 BMCWEB_LOG_DEBUG("Copying {} bytes to buf", out->first.size()); 186 size_t copied = boost::asio::buffer_copy(writeableBuf, out->first); 187 if (copied != out->first.size()) 188 { 189 BMCWEB_LOG_ERROR( 190 "Couldn't copy all {} bytes into buffer, only copied {}", 191 out->first.size(), copied); 192 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; 193 } 194 195 if (!out->second) 196 { 197 BMCWEB_LOG_DEBUG("Setting EOF flag"); 198 *dataFlags |= NGHTTP2_DATA_FLAG_EOF; 199 } 200 return static_cast<ssize_t>(copied); 201 } 202 headerFromStringViews(std::string_view name,std::string_view value,uint8_t flags)203 nghttp2_nv headerFromStringViews(std::string_view name, 204 std::string_view value, uint8_t flags) 205 { 206 uint8_t* nameData = std::bit_cast<uint8_t*>(name.data()); 207 uint8_t* valueData = std::bit_cast<uint8_t*>(value.data()); 208 return {nameData, valueData, name.size(), value.size(), flags}; 209 } 210 sendResponse(Response & completedRes,int32_t streamId)211 int sendResponse(Response& completedRes, int32_t streamId) 212 { 213 BMCWEB_LOG_DEBUG("send_response stream_id:{}", streamId); 214 215 auto it = streams.find(streamId); 216 if (it == streams.end()) 217 { 218 close(); 219 return -1; 220 } 221 Http2StreamData& stream = it->second; 222 Response& res = stream.res; 223 res = std::move(completedRes); 224 225 completeResponseFields(stream.accept, stream.acceptEnc, res); 226 res.addHeader(boost::beast::http::field::date, getCachedDateStr()); 227 boost::urls::url_view urlView; 228 if (stream.req != nullptr) 229 { 230 urlView = stream.req->url(); 231 } 232 res.preparePayload(urlView); 233 234 boost::beast::http::fields& fields = res.fields(); 235 std::string code = std::to_string(res.resultInt()); 236 std::vector<nghttp2_nv> hdr; 237 hdr.emplace_back( 238 headerFromStringViews(":status", code, NGHTTP2_NV_FLAG_NONE)); 239 for (const boost::beast::http::fields::value_type& header : fields) 240 { 241 hdr.emplace_back(headerFromStringViews( 242 header.name_string(), header.value(), NGHTTP2_NV_FLAG_NONE)); 243 } 244 http::response<bmcweb::HttpBody>& fbody = res.response; 245 stream.writer.emplace(fbody.base(), fbody.body()); 246 247 nghttp2_data_provider dataPrd{ 248 .source = {.fd = 0}, 249 .read_callback = fileReadCallback, 250 }; 251 252 int rv = ngSession.submitResponse(streamId, hdr, &dataPrd); 253 if (rv != 0) 254 { 255 BMCWEB_LOG_ERROR("Fatal error: {}", nghttp2_strerror(rv)); 256 close(); 257 return -1; 258 } 259 writeBuffer(); 260 261 return 0; 262 } 263 initializeNghttp2Session()264 nghttp2_session initializeNghttp2Session() 265 { 266 nghttp2_session_callbacks callbacks; 267 callbacks.setOnFrameRecvCallback(onFrameRecvCallbackStatic); 268 callbacks.setOnStreamCloseCallback(onStreamCloseCallbackStatic); 269 callbacks.setOnHeaderCallback(onHeaderCallbackStatic); 270 callbacks.setOnBeginHeadersCallback(onBeginHeadersCallbackStatic); 271 callbacks.setOnDataChunkRecvCallback(onDataChunkRecvStatic); 272 273 nghttp2_session session(callbacks); 274 session.setUserData(this); 275 276 return session; 277 } 278 onRequestRecv(int32_t streamId)279 int onRequestRecv(int32_t streamId) 280 { 281 BMCWEB_LOG_DEBUG("on_request_recv"); 282 283 auto it = streams.find(streamId); 284 if (it == streams.end()) 285 { 286 close(); 287 return -1; 288 } 289 auto& reqReader = it->second.reqReader; 290 if (reqReader) 291 { 292 boost::beast::error_code ec; 293 bmcweb::HttpBody::reader::finish(ec); 294 if (ec) 295 { 296 BMCWEB_LOG_CRITICAL("Failed to finalize payload"); 297 close(); 298 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; 299 } 300 } 301 Request& thisReq = *it->second.req; 302 using boost::beast::http::field; 303 it->second.accept = thisReq.getHeaderValue(field::accept); 304 it->second.acceptEnc = thisReq.getHeaderValue(field::accept_encoding); 305 306 BMCWEB_LOG_DEBUG("Handling {} \"{}\"", logPtr(&thisReq), 307 thisReq.url().encoded_path()); 308 309 Response& thisRes = it->second.res; 310 311 thisRes.setCompleteRequestHandler( 312 [this, streamId](Response& completeRes) { 313 BMCWEB_LOG_DEBUG("res.completeRequestHandler called"); 314 if (sendResponse(completeRes, streamId) != 0) 315 { 316 close(); 317 return; 318 } 319 }); 320 auto asyncResp = 321 std::make_shared<bmcweb::AsyncResp>(std::move(it->second.res)); 322 if constexpr (!BMCWEB_INSECURE_DISABLE_AUTH) 323 { 324 thisReq.session = authentication::authenticate( 325 {}, asyncResp->res, thisReq.method(), thisReq.req, mtlsSession); 326 if (!authentication::isOnAllowlist(thisReq.url().path(), 327 thisReq.method()) && 328 thisReq.session == nullptr) 329 { 330 BMCWEB_LOG_WARNING("Authentication failed"); 331 forward_unauthorized::sendUnauthorized( 332 thisReq.url().encoded_path(), 333 thisReq.getHeaderValue("X-Requested-With"), 334 thisReq.getHeaderValue("Accept"), asyncResp->res); 335 return 0; 336 } 337 } 338 std::string_view expectedEtag = 339 thisReq.getHeaderValue(boost::beast::http::field::if_none_match); 340 BMCWEB_LOG_DEBUG("Setting expected etag {}", expectedEtag); 341 if (!expectedEtag.empty()) 342 { 343 asyncResp->res.setExpectedEtag(expectedEtag); 344 } 345 handler->handle(it->second.req, asyncResp); 346 return 0; 347 } 348 onDataChunkRecvCallback(uint8_t,int32_t streamId,const uint8_t * data,size_t len)349 int onDataChunkRecvCallback(uint8_t /*flags*/, int32_t streamId, 350 const uint8_t* data, size_t len) 351 { 352 auto thisStream = streams.find(streamId); 353 if (thisStream == streams.end()) 354 { 355 BMCWEB_LOG_ERROR("Unknown stream{}", streamId); 356 close(); 357 return -1; 358 } 359 360 std::optional<bmcweb::HttpBody::reader>& reqReader = 361 thisStream->second.reqReader; 362 if (!reqReader) 363 { 364 reqReader.emplace( 365 bmcweb::HttpBody::reader(thisStream->second.req->req.base(), 366 thisStream->second.req->req.body())); 367 } 368 boost::beast::error_code ec; 369 reqReader->put(boost::asio::const_buffer(data, len), ec); 370 if (ec) 371 { 372 BMCWEB_LOG_CRITICAL("Failed to write payload"); 373 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; 374 } 375 return 0; 376 } 377 onDataChunkRecvStatic(nghttp2_session *,uint8_t flags,int32_t streamId,const uint8_t * data,size_t len,void * userData)378 static int onDataChunkRecvStatic( 379 nghttp2_session* /* session */, uint8_t flags, int32_t streamId, 380 const uint8_t* data, size_t len, void* userData) 381 { 382 BMCWEB_LOG_DEBUG("onDataChunkRecvStatic"); 383 if (userData == nullptr) 384 { 385 BMCWEB_LOG_CRITICAL("user data was null?"); 386 return NGHTTP2_ERR_CALLBACK_FAILURE; 387 } 388 return userPtrToSelf(userData).onDataChunkRecvCallback( 389 flags, streamId, data, len); 390 } 391 onFrameRecvCallback(const nghttp2_frame & frame)392 int onFrameRecvCallback(const nghttp2_frame& frame) 393 { 394 BMCWEB_LOG_DEBUG("frame type {}", static_cast<int>(frame.hd.type)); 395 switch (frame.hd.type) 396 { 397 case NGHTTP2_DATA: 398 case NGHTTP2_HEADERS: 399 // Check that the client request has finished 400 if ((frame.hd.flags & NGHTTP2_FLAG_END_STREAM) != 0) 401 { 402 return onRequestRecv(frame.hd.stream_id); 403 } 404 break; 405 default: 406 break; 407 } 408 return 0; 409 } 410 onFrameRecvCallbackStatic(nghttp2_session *,const nghttp2_frame * frame,void * userData)411 static int onFrameRecvCallbackStatic(nghttp2_session* /* session */, 412 const nghttp2_frame* frame, 413 void* userData) 414 { 415 BMCWEB_LOG_DEBUG("on_frame_recv_callback. Frame type {}", 416 static_cast<int>(frame->hd.type)); 417 if (userData == nullptr) 418 { 419 BMCWEB_LOG_CRITICAL("user data was null?"); 420 return NGHTTP2_ERR_CALLBACK_FAILURE; 421 } 422 if (frame == nullptr) 423 { 424 BMCWEB_LOG_CRITICAL("frame was null?"); 425 return NGHTTP2_ERR_CALLBACK_FAILURE; 426 } 427 return userPtrToSelf(userData).onFrameRecvCallback(*frame); 428 } 429 userPtrToSelf(void * userData)430 static self_type& userPtrToSelf(void* userData) 431 { 432 // This method exists to keep the unsafe reinterpret cast in one 433 // place. 434 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) 435 return *reinterpret_cast<self_type*>(userData); 436 } 437 onStreamCloseCallbackStatic(nghttp2_session *,int32_t streamId,uint32_t,void * userData)438 static int onStreamCloseCallbackStatic(nghttp2_session* /* session */, 439 int32_t streamId, 440 uint32_t /*unused*/, void* userData) 441 { 442 BMCWEB_LOG_DEBUG("on_stream_close_callback stream {}", streamId); 443 if (userData == nullptr) 444 { 445 BMCWEB_LOG_CRITICAL("user data was null?"); 446 return NGHTTP2_ERR_CALLBACK_FAILURE; 447 } 448 if (userPtrToSelf(userData).streams.erase(streamId) <= 0) 449 { 450 return -1; 451 } 452 return 0; 453 } 454 onHeaderCallback(const nghttp2_frame & frame,std::span<const uint8_t> name,std::span<const uint8_t> value)455 int onHeaderCallback(const nghttp2_frame& frame, 456 std::span<const uint8_t> name, 457 std::span<const uint8_t> value) 458 { 459 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) 460 std::string_view nameSv(reinterpret_cast<const char*>(name.data()), 461 name.size()); 462 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) 463 std::string_view valueSv(reinterpret_cast<const char*>(value.data()), 464 value.size()); 465 466 BMCWEB_LOG_DEBUG("on_header_callback name: {} value {}", nameSv, 467 valueSv); 468 if (frame.hd.type != NGHTTP2_HEADERS) 469 { 470 return 0; 471 } 472 if (frame.headers.cat != NGHTTP2_HCAT_REQUEST) 473 { 474 return 0; 475 } 476 auto thisStream = streams.find(frame.hd.stream_id); 477 if (thisStream == streams.end()) 478 { 479 BMCWEB_LOG_ERROR("Unknown stream{}", frame.hd.stream_id); 480 close(); 481 return -1; 482 } 483 484 Request& thisReq = *thisStream->second.req; 485 486 if (nameSv == ":path") 487 { 488 thisReq.target(valueSv); 489 } 490 else if (nameSv == ":method") 491 { 492 boost::beast::http::verb verb = 493 boost::beast::http::string_to_verb(valueSv); 494 if (verb == boost::beast::http::verb::unknown) 495 { 496 BMCWEB_LOG_ERROR("Unknown http verb {}", valueSv); 497 verb = boost::beast::http::verb::trace; 498 } 499 thisReq.method(verb); 500 } 501 else if (nameSv.starts_with(":")) 502 { 503 // Ignore all other http2 headers 504 // :scheme and :authority are other valid http2 fields that might 505 // show up here. 506 } 507 else 508 { 509 thisReq.addHeader(nameSv, valueSv); 510 } 511 return 0; 512 } 513 onHeaderCallbackStatic(nghttp2_session *,const nghttp2_frame * frame,const uint8_t * name,size_t namelen,const uint8_t * value,size_t vallen,uint8_t,void * userData)514 static int onHeaderCallbackStatic( 515 nghttp2_session* /* session */, const nghttp2_frame* frame, 516 const uint8_t* name, size_t namelen, const uint8_t* value, 517 size_t vallen, uint8_t /* flags */, void* userData) 518 { 519 if (userData == nullptr) 520 { 521 BMCWEB_LOG_CRITICAL("user data was null?"); 522 return NGHTTP2_ERR_CALLBACK_FAILURE; 523 } 524 if (frame == nullptr) 525 { 526 BMCWEB_LOG_CRITICAL("frame was null?"); 527 return NGHTTP2_ERR_CALLBACK_FAILURE; 528 } 529 if (name == nullptr) 530 { 531 BMCWEB_LOG_CRITICAL("name was null?"); 532 return NGHTTP2_ERR_CALLBACK_FAILURE; 533 } 534 if (value == nullptr) 535 { 536 BMCWEB_LOG_CRITICAL("value was null?"); 537 return NGHTTP2_ERR_CALLBACK_FAILURE; 538 } 539 return userPtrToSelf(userData).onHeaderCallback(*frame, {name, namelen}, 540 {value, vallen}); 541 } 542 onBeginHeadersCallback(const nghttp2_frame & frame)543 int onBeginHeadersCallback(const nghttp2_frame& frame) 544 { 545 if (frame.hd.type == NGHTTP2_HEADERS && 546 frame.headers.cat == NGHTTP2_HCAT_REQUEST) 547 { 548 BMCWEB_LOG_DEBUG("create stream for id {}", frame.hd.stream_id); 549 550 streams[frame.hd.stream_id]; 551 if (ngSession.setLocalWindowSize( 552 NGHTTP2_FLAG_NONE, frame.hd.stream_id, 16384 * 32) != 0) 553 { 554 BMCWEB_LOG_ERROR("Failed to set local window size"); 555 } 556 } 557 return 0; 558 } 559 onBeginHeadersCallbackStatic(nghttp2_session *,const nghttp2_frame * frame,void * userData)560 static int onBeginHeadersCallbackStatic(nghttp2_session* /* session */, 561 const nghttp2_frame* frame, 562 void* userData) 563 { 564 BMCWEB_LOG_DEBUG("on_begin_headers_callback"); 565 if (userData == nullptr) 566 { 567 BMCWEB_LOG_CRITICAL("user data was null?"); 568 return NGHTTP2_ERR_CALLBACK_FAILURE; 569 } 570 if (frame == nullptr) 571 { 572 BMCWEB_LOG_CRITICAL("frame was null?"); 573 return NGHTTP2_ERR_CALLBACK_FAILURE; 574 } 575 return userPtrToSelf(userData).onBeginHeadersCallback(*frame); 576 } 577 afterWriteBuffer(const std::shared_ptr<self_type> & self,const boost::system::error_code & ec,size_t sendLength)578 static void afterWriteBuffer(const std::shared_ptr<self_type>& self, 579 const boost::system::error_code& ec, 580 size_t sendLength) 581 { 582 self->isWriting = false; 583 BMCWEB_LOG_DEBUG("Sent {}", sendLength); 584 if (ec) 585 { 586 self->close(); 587 return; 588 } 589 self->writeBuffer(); 590 } 591 writeBuffer()592 void writeBuffer() 593 { 594 if (isWriting) 595 { 596 return; 597 } 598 std::span<const uint8_t> data = ngSession.memSend(); 599 if (data.empty()) 600 { 601 return; 602 } 603 isWriting = true; 604 if (httpType == HttpType::HTTPS) 605 { 606 boost::asio::async_write( 607 adaptor, boost::asio::const_buffer(data.data(), data.size()), 608 std::bind_front(afterWriteBuffer, shared_from_this())); 609 } 610 else if (httpType == HttpType::HTTP) 611 { 612 boost::asio::async_write( 613 adaptor.next_layer(), 614 boost::asio::const_buffer(data.data(), data.size()), 615 std::bind_front(afterWriteBuffer, shared_from_this())); 616 } 617 } 618 close()619 void close() 620 { 621 adaptor.next_layer().close(); 622 } 623 afterDoRead(const std::shared_ptr<self_type> &,const boost::system::error_code & ec,size_t bytesTransferred)624 void afterDoRead(const std::shared_ptr<self_type>& /*self*/, 625 const boost::system::error_code& ec, 626 size_t bytesTransferred) 627 { 628 BMCWEB_LOG_DEBUG("{} async_read_some {} Bytes", logPtr(this), 629 bytesTransferred); 630 631 if (ec) 632 { 633 BMCWEB_LOG_ERROR("{} Error while reading: {}", logPtr(this), 634 ec.message()); 635 close(); 636 BMCWEB_LOG_DEBUG("{} from read(1)", logPtr(this)); 637 return; 638 } 639 std::span<uint8_t> bufferSpan{inBuffer.data(), bytesTransferred}; 640 641 ssize_t readLen = ngSession.memRecv(bufferSpan); 642 if (readLen < 0) 643 { 644 BMCWEB_LOG_ERROR("nghttp2_session_mem_recv returned {}", readLen); 645 close(); 646 return; 647 } 648 writeBuffer(); 649 650 doRead(); 651 } 652 doRead()653 void doRead() 654 { 655 BMCWEB_LOG_DEBUG("{} doRead", logPtr(this)); 656 if (httpType == HttpType::HTTPS) 657 { 658 adaptor.async_read_some(boost::asio::buffer(inBuffer), 659 std::bind_front(&self_type::afterDoRead, 660 this, shared_from_this())); 661 } 662 else if (httpType == HttpType::HTTP) 663 { 664 adaptor.next_layer().async_read_some( 665 boost::asio::buffer(inBuffer), 666 std::bind_front(&self_type::afterDoRead, this, 667 shared_from_this())); 668 } 669 } 670 671 // A mapping from http2 stream ID to Stream Data 672 std::map<int32_t, Http2StreamData> streams; 673 674 std::array<uint8_t, 8192> inBuffer{}; 675 676 HttpType httpType = HttpType::BOTH; 677 boost::asio::ssl::stream<Adaptor> adaptor; 678 bool isWriting = false; 679 680 nghttp2_session ngSession; 681 682 Handler* handler; 683 std::function<std::string()>& getCachedDateStr; 684 685 std::shared_ptr<persistent_data::UserSession> mtlsSession; 686 687 using std::enable_shared_from_this< 688 HTTP2Connection<Adaptor, Handler>>::shared_from_this; 689 690 using std::enable_shared_from_this< 691 HTTP2Connection<Adaptor, Handler>>::weak_from_this; 692 }; 693 } // namespace crow 694