xref: /openbmc/bmcweb/http/http2_connection.hpp (revision 08fad5d9dc59323a8916ff97a035221621047d8c)
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