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 [weakSelf = weak_from_this(), streamId](Response& completeRes) { 313 BMCWEB_LOG_DEBUG("res.completeRequestHandler called"); 314 if (auto self = weakSelf.lock(); self) 315 { 316 if (self->sendResponse(completeRes, streamId) != 0) 317 { 318 self->close(); 319 return; 320 } 321 } 322 }); 323 324 auto asyncResp = 325 std::make_shared<bmcweb::AsyncResp>(std::move(it->second.res)); 326 if constexpr (!BMCWEB_INSECURE_DISABLE_AUTH) 327 { 328 thisReq.session = authentication::authenticate( 329 {}, asyncResp->res, thisReq.method(), thisReq.req, mtlsSession); 330 if (!authentication::isOnAllowlist(thisReq.url().path(), 331 thisReq.method()) && 332 thisReq.session == nullptr) 333 { 334 BMCWEB_LOG_WARNING("Authentication failed"); 335 forward_unauthorized::sendUnauthorized( 336 thisReq.url().encoded_path(), 337 thisReq.getHeaderValue("X-Requested-With"), 338 thisReq.getHeaderValue("Accept"), asyncResp->res); 339 return 0; 340 } 341 } 342 std::string_view expectedEtag = 343 thisReq.getHeaderValue(boost::beast::http::field::if_none_match); 344 BMCWEB_LOG_DEBUG("Setting expected etag {}", expectedEtag); 345 if (!expectedEtag.empty()) 346 { 347 asyncResp->res.setExpectedEtag(expectedEtag); 348 } 349 handler->handle(it->second.req, asyncResp); 350 return 0; 351 } 352 onDataChunkRecvCallback(uint8_t,int32_t streamId,const uint8_t * data,size_t len)353 int onDataChunkRecvCallback(uint8_t /*flags*/, int32_t streamId, 354 const uint8_t* data, size_t len) 355 { 356 auto thisStream = streams.find(streamId); 357 if (thisStream == streams.end()) 358 { 359 BMCWEB_LOG_ERROR("Unknown stream{}", streamId); 360 close(); 361 return -1; 362 } 363 364 std::optional<bmcweb::HttpBody::reader>& reqReader = 365 thisStream->second.reqReader; 366 if (!reqReader) 367 { 368 Request::Body& req = thisStream->second.req->req; 369 reqReader.emplace(req.base(), req.body()); 370 } 371 boost::beast::error_code ec; 372 reqReader->put(boost::asio::const_buffer(data, len), ec); 373 if (ec) 374 { 375 BMCWEB_LOG_CRITICAL("Failed to write payload"); 376 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; 377 } 378 return 0; 379 } 380 onDataChunkRecvStatic(nghttp2_session *,uint8_t flags,int32_t streamId,const uint8_t * data,size_t len,void * userData)381 static int onDataChunkRecvStatic( 382 nghttp2_session* /* session */, uint8_t flags, int32_t streamId, 383 const uint8_t* data, size_t len, void* userData) 384 { 385 BMCWEB_LOG_DEBUG("onDataChunkRecvStatic"); 386 if (userData == nullptr) 387 { 388 BMCWEB_LOG_CRITICAL("user data was null?"); 389 return NGHTTP2_ERR_CALLBACK_FAILURE; 390 } 391 return userPtrToSelf(userData).onDataChunkRecvCallback( 392 flags, streamId, data, len); 393 } 394 onFrameRecvCallback(const nghttp2_frame & frame)395 int onFrameRecvCallback(const nghttp2_frame& frame) 396 { 397 BMCWEB_LOG_DEBUG("frame type {}", static_cast<int>(frame.hd.type)); 398 switch (frame.hd.type) 399 { 400 case NGHTTP2_DATA: 401 case NGHTTP2_HEADERS: 402 // Check that the client request has finished 403 if ((frame.hd.flags & NGHTTP2_FLAG_END_STREAM) != 0) 404 { 405 return onRequestRecv(frame.hd.stream_id); 406 } 407 break; 408 default: 409 break; 410 } 411 return 0; 412 } 413 onFrameRecvCallbackStatic(nghttp2_session *,const nghttp2_frame * frame,void * userData)414 static int onFrameRecvCallbackStatic(nghttp2_session* /* session */, 415 const nghttp2_frame* frame, 416 void* userData) 417 { 418 BMCWEB_LOG_DEBUG("on_frame_recv_callback. Frame type {}", 419 static_cast<int>(frame->hd.type)); 420 if (userData == nullptr) 421 { 422 BMCWEB_LOG_CRITICAL("user data was null?"); 423 return NGHTTP2_ERR_CALLBACK_FAILURE; 424 } 425 if (frame == nullptr) 426 { 427 BMCWEB_LOG_CRITICAL("frame was null?"); 428 return NGHTTP2_ERR_CALLBACK_FAILURE; 429 } 430 return userPtrToSelf(userData).onFrameRecvCallback(*frame); 431 } 432 userPtrToSelf(void * userData)433 static self_type& userPtrToSelf(void* userData) 434 { 435 // This method exists to keep the unsafe reinterpret cast in one 436 // place. 437 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) 438 return *reinterpret_cast<self_type*>(userData); 439 } 440 onStreamCloseCallbackStatic(nghttp2_session *,int32_t streamId,uint32_t,void * userData)441 static int onStreamCloseCallbackStatic(nghttp2_session* /* session */, 442 int32_t streamId, 443 uint32_t /*unused*/, void* userData) 444 { 445 BMCWEB_LOG_DEBUG("on_stream_close_callback stream {}", streamId); 446 if (userData == nullptr) 447 { 448 BMCWEB_LOG_CRITICAL("user data was null?"); 449 return NGHTTP2_ERR_CALLBACK_FAILURE; 450 } 451 if (userPtrToSelf(userData).streams.erase(streamId) <= 0) 452 { 453 return -1; 454 } 455 return 0; 456 } 457 onHeaderCallback(const nghttp2_frame & frame,std::span<const uint8_t> name,std::span<const uint8_t> value)458 int onHeaderCallback(const nghttp2_frame& frame, 459 std::span<const uint8_t> name, 460 std::span<const uint8_t> value) 461 { 462 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) 463 std::string_view nameSv(reinterpret_cast<const char*>(name.data()), 464 name.size()); 465 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) 466 std::string_view valueSv(reinterpret_cast<const char*>(value.data()), 467 value.size()); 468 469 BMCWEB_LOG_DEBUG("on_header_callback name: {} value {}", nameSv, 470 valueSv); 471 if (frame.hd.type != NGHTTP2_HEADERS) 472 { 473 return 0; 474 } 475 if (frame.headers.cat != NGHTTP2_HCAT_REQUEST) 476 { 477 return 0; 478 } 479 auto thisStream = streams.find(frame.hd.stream_id); 480 if (thisStream == streams.end()) 481 { 482 BMCWEB_LOG_ERROR("Unknown stream{}", frame.hd.stream_id); 483 close(); 484 return -1; 485 } 486 487 Request& thisReq = *thisStream->second.req; 488 489 if (nameSv == ":path") 490 { 491 thisReq.target(valueSv); 492 } 493 else if (nameSv == ":method") 494 { 495 boost::beast::http::verb verb = 496 boost::beast::http::string_to_verb(valueSv); 497 if (verb == boost::beast::http::verb::unknown) 498 { 499 BMCWEB_LOG_ERROR("Unknown http verb {}", valueSv); 500 verb = boost::beast::http::verb::trace; 501 } 502 thisReq.method(verb); 503 } 504 else if (nameSv.starts_with(":")) 505 { 506 // Ignore all other http2 headers 507 // :scheme and :authority are other valid http2 fields that might 508 // show up here. 509 } 510 else 511 { 512 thisReq.addHeader(nameSv, valueSv); 513 } 514 return 0; 515 } 516 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)517 static int onHeaderCallbackStatic( 518 nghttp2_session* /* session */, const nghttp2_frame* frame, 519 const uint8_t* name, size_t namelen, const uint8_t* value, 520 size_t vallen, uint8_t /* flags */, void* userData) 521 { 522 if (userData == nullptr) 523 { 524 BMCWEB_LOG_CRITICAL("user data was null?"); 525 return NGHTTP2_ERR_CALLBACK_FAILURE; 526 } 527 if (frame == nullptr) 528 { 529 BMCWEB_LOG_CRITICAL("frame was null?"); 530 return NGHTTP2_ERR_CALLBACK_FAILURE; 531 } 532 if (name == nullptr) 533 { 534 BMCWEB_LOG_CRITICAL("name was null?"); 535 return NGHTTP2_ERR_CALLBACK_FAILURE; 536 } 537 if (value == nullptr) 538 { 539 BMCWEB_LOG_CRITICAL("value was null?"); 540 return NGHTTP2_ERR_CALLBACK_FAILURE; 541 } 542 return userPtrToSelf(userData).onHeaderCallback(*frame, {name, namelen}, 543 {value, vallen}); 544 } 545 onBeginHeadersCallback(const nghttp2_frame & frame)546 int onBeginHeadersCallback(const nghttp2_frame& frame) 547 { 548 if (frame.hd.type == NGHTTP2_HEADERS && 549 frame.headers.cat == NGHTTP2_HCAT_REQUEST) 550 { 551 BMCWEB_LOG_DEBUG("create stream for id {}", frame.hd.stream_id); 552 553 streams[frame.hd.stream_id]; 554 if (ngSession.setLocalWindowSize( 555 NGHTTP2_FLAG_NONE, frame.hd.stream_id, 16384 * 32) != 0) 556 { 557 BMCWEB_LOG_ERROR("Failed to set local window size"); 558 } 559 } 560 return 0; 561 } 562 onBeginHeadersCallbackStatic(nghttp2_session *,const nghttp2_frame * frame,void * userData)563 static int onBeginHeadersCallbackStatic(nghttp2_session* /* session */, 564 const nghttp2_frame* frame, 565 void* userData) 566 { 567 BMCWEB_LOG_DEBUG("on_begin_headers_callback"); 568 if (userData == nullptr) 569 { 570 BMCWEB_LOG_CRITICAL("user data was null?"); 571 return NGHTTP2_ERR_CALLBACK_FAILURE; 572 } 573 if (frame == nullptr) 574 { 575 BMCWEB_LOG_CRITICAL("frame was null?"); 576 return NGHTTP2_ERR_CALLBACK_FAILURE; 577 } 578 return userPtrToSelf(userData).onBeginHeadersCallback(*frame); 579 } 580 afterWriteBuffer(const std::shared_ptr<self_type> & self,const boost::system::error_code & ec,size_t sendLength)581 static void afterWriteBuffer(const std::shared_ptr<self_type>& self, 582 const boost::system::error_code& ec, 583 size_t sendLength) 584 { 585 self->isWriting = false; 586 BMCWEB_LOG_DEBUG("Sent {}", sendLength); 587 if (ec) 588 { 589 self->close(); 590 return; 591 } 592 self->writeBuffer(); 593 } 594 writeBuffer()595 void writeBuffer() 596 { 597 if (isWriting) 598 { 599 return; 600 } 601 std::span<const uint8_t> data = ngSession.memSend(); 602 if (data.empty()) 603 { 604 return; 605 } 606 isWriting = true; 607 if (httpType == HttpType::HTTPS) 608 { 609 boost::asio::async_write( 610 adaptor, boost::asio::const_buffer(data.data(), data.size()), 611 std::bind_front(afterWriteBuffer, shared_from_this())); 612 } 613 else if (httpType == HttpType::HTTP) 614 { 615 boost::asio::async_write( 616 adaptor.next_layer(), 617 boost::asio::const_buffer(data.data(), data.size()), 618 std::bind_front(afterWriteBuffer, shared_from_this())); 619 } 620 } 621 close()622 void close() 623 { 624 adaptor.next_layer().close(); 625 } 626 afterDoRead(const std::shared_ptr<self_type> &,const boost::system::error_code & ec,size_t bytesTransferred)627 void afterDoRead(const std::shared_ptr<self_type>& /*self*/, 628 const boost::system::error_code& ec, 629 size_t bytesTransferred) 630 { 631 BMCWEB_LOG_DEBUG("{} async_read_some {} Bytes", logPtr(this), 632 bytesTransferred); 633 634 if (ec) 635 { 636 // EOF is normal when client closes HTTP/2 connection 637 // Only log non-EOF errors 638 if (ec != boost::asio::error::eof) 639 { 640 BMCWEB_LOG_ERROR("{} Error while reading: {}", logPtr(this), 641 ec.message()); 642 } 643 close(); 644 BMCWEB_LOG_DEBUG("{} from read(1)", logPtr(this)); 645 return; 646 } 647 std::span<uint8_t> bufferSpan{inBuffer.data(), bytesTransferred}; 648 649 ssize_t readLen = ngSession.memRecv(bufferSpan); 650 if (readLen < 0) 651 { 652 BMCWEB_LOG_ERROR("nghttp2_session_mem_recv returned {}", readLen); 653 close(); 654 return; 655 } 656 writeBuffer(); 657 658 doRead(); 659 } 660 doRead()661 void doRead() 662 { 663 BMCWEB_LOG_DEBUG("{} doRead", logPtr(this)); 664 if (httpType == HttpType::HTTPS) 665 { 666 adaptor.async_read_some(boost::asio::buffer(inBuffer), 667 std::bind_front(&self_type::afterDoRead, 668 this, shared_from_this())); 669 } 670 else if (httpType == HttpType::HTTP) 671 { 672 adaptor.next_layer().async_read_some( 673 boost::asio::buffer(inBuffer), 674 std::bind_front(&self_type::afterDoRead, this, 675 shared_from_this())); 676 } 677 } 678 679 // A mapping from http2 stream ID to Stream Data 680 std::map<int32_t, Http2StreamData> streams; 681 682 std::array<uint8_t, 8192> inBuffer{}; 683 684 HttpType httpType = HttpType::BOTH; 685 boost::asio::ssl::stream<Adaptor> adaptor; 686 bool isWriting = false; 687 688 nghttp2_session ngSession; 689 690 Handler* handler; 691 std::function<std::string()>& getCachedDateStr; 692 693 std::shared_ptr<persistent_data::UserSession> mtlsSession; 694 695 using std::enable_shared_from_this< 696 HTTP2Connection<Adaptor, Handler>>::shared_from_this; 697 698 using std::enable_shared_from_this< 699 HTTP2Connection<Adaptor, Handler>>::weak_from_this; 700 }; 701 } // namespace crow 702