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