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