blob: a8636360347c37b10e7b5bcc400e01c99ce0dd83 [file] [log] [blame]
Ed Tanousfca2cbe2021-01-28 14:49:59 -08001#pragma once
2#include "bmcweb_config.h"
3
4#include "async_resp.hpp"
5#include "authentication.hpp"
6#include "complete_response_fields.hpp"
Ed Tanous325310d2024-03-15 09:05:04 -07007#include "http_body.hpp"
Ed Tanousfca2cbe2021-01-28 14:49:59 -08008#include "http_response.hpp"
9#include "http_utility.hpp"
10#include "logging.hpp"
11#include "mutual_tls.hpp"
12#include "nghttp2_adapters.hpp"
13#include "ssl_key_handler.hpp"
14#include "utility.hpp"
15
Ed Tanousfca2cbe2021-01-28 14:49:59 -080016#include <boost/asio/io_context.hpp>
17#include <boost/asio/ip/tcp.hpp>
18#include <boost/asio/ssl/stream.hpp>
19#include <boost/asio/steady_timer.hpp>
Ed Tanousfca2cbe2021-01-28 14:49:59 -080020#include <boost/beast/http/error.hpp>
21#include <boost/beast/http/parser.hpp>
22#include <boost/beast/http/read.hpp>
23#include <boost/beast/http/serializer.hpp>
Ed Tanousfca2cbe2021-01-28 14:49:59 -080024#include <boost/beast/http/write.hpp>
Ed Tanousfca2cbe2021-01-28 14:49:59 -080025#include <boost/beast/websocket.hpp>
Ed Tanousd0882182024-01-26 23:45:25 -080026#include <boost/system/error_code.hpp>
Ed Tanousfca2cbe2021-01-28 14:49:59 -080027
Ed Tanousd0882182024-01-26 23:45:25 -080028#include <array>
Ed Tanousfca2cbe2021-01-28 14:49:59 -080029#include <atomic>
30#include <chrono>
Ed Tanousd0882182024-01-26 23:45:25 -080031#include <functional>
32#include <memory>
Ed Tanousfca2cbe2021-01-28 14:49:59 -080033#include <vector>
34
35namespace crow
36{
37
38struct Http2StreamData
39{
Jonathan Doman102a4cd2024-04-15 16:56:23 -070040 std::shared_ptr<Request> req = std::make_shared<Request>();
Ed Tanous325310d2024-03-15 09:05:04 -070041 std::optional<bmcweb::HttpBody::reader> reqReader;
Ed Tanous47f29342024-03-19 12:18:06 -070042 Response res;
Ed Tanousb2896142024-01-31 15:25:47 -080043 std::optional<bmcweb::HttpBody::writer> writer;
Ed Tanousfca2cbe2021-01-28 14:49:59 -080044};
45
46template <typename Adaptor, typename Handler>
47class HTTP2Connection :
48 public std::enable_shared_from_this<HTTP2Connection<Adaptor, Handler>>
49{
50 using self_type = HTTP2Connection<Adaptor, Handler>;
51
52 public:
53 HTTP2Connection(Adaptor&& adaptorIn, Handler* handlerIn,
Ed Tanous52e31622024-01-23 16:31:11 -080054 std::function<std::string()>& getCachedDateStrF) :
Ed Tanousfca2cbe2021-01-28 14:49:59 -080055 adaptor(std::move(adaptorIn)),
Ed Tanous52e31622024-01-23 16:31:11 -080056 ngSession(initializeNghttp2Session()), handler(handlerIn),
57 getCachedDateStr(getCachedDateStrF)
Ed Tanousfca2cbe2021-01-28 14:49:59 -080058 {}
59
60 void start()
61 {
62 // Create the control stream
Ed Tanousf42e8592023-08-25 10:47:44 -070063 streams[0];
Ed Tanousfca2cbe2021-01-28 14:49:59 -080064
65 if (sendServerConnectionHeader() != 0)
66 {
Ed Tanous62598e32023-07-17 17:06:25 -070067 BMCWEB_LOG_ERROR("send_server_connection_header failed");
Ed Tanousfca2cbe2021-01-28 14:49:59 -080068 return;
69 }
70 doRead();
71 }
72
73 int sendServerConnectionHeader()
74 {
Ed Tanous62598e32023-07-17 17:06:25 -070075 BMCWEB_LOG_DEBUG("send_server_connection_header()");
Ed Tanousfca2cbe2021-01-28 14:49:59 -080076
77 uint32_t maxStreams = 4;
78 std::array<nghttp2_settings_entry, 2> iv = {
79 {{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, maxStreams},
80 {NGHTTP2_SETTINGS_ENABLE_PUSH, 0}}};
81 int rv = ngSession.submitSettings(iv);
82 if (rv != 0)
83 {
Ed Tanous62598e32023-07-17 17:06:25 -070084 BMCWEB_LOG_ERROR("Fatal error: {}", nghttp2_strerror(rv));
Ed Tanousfca2cbe2021-01-28 14:49:59 -080085 return -1;
86 }
Ed Tanousd0882182024-01-26 23:45:25 -080087 writeBuffer();
Ed Tanousfca2cbe2021-01-28 14:49:59 -080088 return 0;
89 }
90
91 static ssize_t fileReadCallback(nghttp2_session* /* session */,
Ed Tanousf42e8592023-08-25 10:47:44 -070092 int32_t streamId, uint8_t* buf,
Ed Tanousfca2cbe2021-01-28 14:49:59 -080093 size_t length, uint32_t* dataFlags,
Ed Tanousf42e8592023-08-25 10:47:44 -070094 nghttp2_data_source* /*source*/,
95 void* userPtr)
Ed Tanousfca2cbe2021-01-28 14:49:59 -080096 {
Ed Tanousf42e8592023-08-25 10:47:44 -070097 self_type& self = userPtrToSelf(userPtr);
98
99 auto streamIt = self.streams.find(streamId);
100 if (streamIt == self.streams.end())
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800101 {
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800102 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
103 }
Ed Tanousf42e8592023-08-25 10:47:44 -0700104 Http2StreamData& stream = streamIt->second;
Ed Tanous62598e32023-07-17 17:06:25 -0700105 BMCWEB_LOG_DEBUG("File read callback length: {}", length);
Ed Tanousd547d8d2024-03-16 18:04:41 -0700106 if (!stream.writer)
107 {
108 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
109 }
Ed Tanous52e31622024-01-23 16:31:11 -0800110 boost::beast::error_code ec;
111 boost::optional<std::pair<boost::asio::const_buffer, bool>> out =
112 stream.writer->getWithMaxSize(ec, length);
113 if (ec)
Ed Tanous27b0cf92023-08-07 12:02:40 -0700114 {
Ed Tanous325310d2024-03-15 09:05:04 -0700115 BMCWEB_LOG_CRITICAL("Failed to get buffer");
Ed Tanous27b0cf92023-08-07 12:02:40 -0700116 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
117 }
Ed Tanous52e31622024-01-23 16:31:11 -0800118 if (!out)
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800119 {
Ed Tanous325310d2024-03-15 09:05:04 -0700120 BMCWEB_LOG_ERROR("Empty file, setting EOF");
Ed Tanous52e31622024-01-23 16:31:11 -0800121 *dataFlags |= NGHTTP2_DATA_FLAG_EOF;
122 return 0;
123 }
124
125 BMCWEB_LOG_DEBUG("Send chunk of size: {}", out->first.size());
126 if (length < out->first.size())
127 {
Ed Tanous325310d2024-03-15 09:05:04 -0700128 BMCWEB_LOG_CRITICAL(
129 "Buffer overflow that should never happen happened");
Ed Tanous52e31622024-01-23 16:31:11 -0800130 // Should never happen because of length limit on get() above
131 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
132 }
Ed Tanous325310d2024-03-15 09:05:04 -0700133 boost::asio::mutable_buffer writeableBuf(buf, length);
Ed Tanous52e31622024-01-23 16:31:11 -0800134 BMCWEB_LOG_DEBUG("Copying {} bytes to buf", out->first.size());
Ed Tanous325310d2024-03-15 09:05:04 -0700135 size_t copied = boost::asio::buffer_copy(writeableBuf, out->first);
136 if (copied != out->first.size())
137 {
138 BMCWEB_LOG_ERROR(
139 "Couldn't copy all {} bytes into buffer, only copied {}",
140 out->first.size(), copied);
141 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
142 }
Ed Tanous52e31622024-01-23 16:31:11 -0800143
144 if (!out->second)
145 {
Ed Tanous325310d2024-03-15 09:05:04 -0700146 BMCWEB_LOG_DEBUG("Setting EOF flag");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800147 *dataFlags |= NGHTTP2_DATA_FLAG_EOF;
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800148 }
Ed Tanous325310d2024-03-15 09:05:04 -0700149 return static_cast<ssize_t>(copied);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800150 }
151
152 nghttp2_nv headerFromStringViews(std::string_view name,
Ed Tanous52e31622024-01-23 16:31:11 -0800153 std::string_view value, uint8_t flags)
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800154 {
155 uint8_t* nameData = std::bit_cast<uint8_t*>(name.data());
156 uint8_t* valueData = std::bit_cast<uint8_t*>(value.data());
Ed Tanous52e31622024-01-23 16:31:11 -0800157 return {nameData, valueData, name.size(), value.size(), flags};
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800158 }
159
160 int sendResponse(Response& completedRes, int32_t streamId)
161 {
Ed Tanous62598e32023-07-17 17:06:25 -0700162 BMCWEB_LOG_DEBUG("send_response stream_id:{}", streamId);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800163
164 auto it = streams.find(streamId);
165 if (it == streams.end())
166 {
167 close();
168 return -1;
169 }
Ed Tanous499b5b42024-04-06 08:39:18 -0700170 Http2StreamData& stream = it->second;
171 Response& res = stream.res;
172 res = std::move(completedRes);
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700173 crow::Request& thisReq = *stream.req;
Ed Tanous499b5b42024-04-06 08:39:18 -0700174
175 completeResponseFields(thisReq, res);
176 res.addHeader(boost::beast::http::field::date, getCachedDateStr());
177 res.preparePayload();
178
179 boost::beast::http::fields& fields = res.fields();
180 std::string code = std::to_string(res.resultInt());
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800181 std::vector<nghttp2_nv> hdr;
Ed Tanous52e31622024-01-23 16:31:11 -0800182 hdr.emplace_back(
183 headerFromStringViews(":status", code, NGHTTP2_NV_FLAG_NONE));
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800184 for (const boost::beast::http::fields::value_type& header : fields)
185 {
Ed Tanous52e31622024-01-23 16:31:11 -0800186 hdr.emplace_back(headerFromStringViews(
Ed Tanousd0882182024-01-26 23:45:25 -0800187 header.name_string(), header.value(), NGHTTP2_NV_FLAG_NONE));
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800188 }
Ed Tanousb2896142024-01-31 15:25:47 -0800189 http::response<bmcweb::HttpBody>& fbody = res.response;
Ed Tanous52e31622024-01-23 16:31:11 -0800190 stream.writer.emplace(fbody.base(), fbody.body());
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800191
192 nghttp2_data_provider dataPrd{
Ed Tanousf42e8592023-08-25 10:47:44 -0700193 .source = {.fd = 0},
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800194 .read_callback = fileReadCallback,
195 };
196
197 int rv = ngSession.submitResponse(streamId, hdr, &dataPrd);
198 if (rv != 0)
199 {
Ed Tanous62598e32023-07-17 17:06:25 -0700200 BMCWEB_LOG_ERROR("Fatal error: {}", nghttp2_strerror(rv));
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800201 close();
202 return -1;
203 }
Ed Tanousd0882182024-01-26 23:45:25 -0800204 writeBuffer();
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800205
206 return 0;
207 }
208
209 nghttp2_session initializeNghttp2Session()
210 {
211 nghttp2_session_callbacks callbacks;
212 callbacks.setOnFrameRecvCallback(onFrameRecvCallbackStatic);
213 callbacks.setOnStreamCloseCallback(onStreamCloseCallbackStatic);
214 callbacks.setOnHeaderCallback(onHeaderCallbackStatic);
215 callbacks.setOnBeginHeadersCallback(onBeginHeadersCallbackStatic);
Ed Tanous325310d2024-03-15 09:05:04 -0700216 callbacks.setOnDataChunkRecvCallback(onDataChunkRecvStatic);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800217
218 nghttp2_session session(callbacks);
219 session.setUserData(this);
220
221 return session;
222 }
223
224 int onRequestRecv(int32_t streamId)
225 {
Ed Tanous62598e32023-07-17 17:06:25 -0700226 BMCWEB_LOG_DEBUG("on_request_recv");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800227
228 auto it = streams.find(streamId);
229 if (it == streams.end())
230 {
231 close();
232 return -1;
233 }
Ed Tanousd547d8d2024-03-16 18:04:41 -0700234 auto& reqReader = it->second.reqReader;
235 if (reqReader)
Ed Tanous325310d2024-03-15 09:05:04 -0700236 {
237 boost::beast::error_code ec;
Ed Tanousd547d8d2024-03-16 18:04:41 -0700238 reqReader->finish(ec);
Ed Tanous325310d2024-03-15 09:05:04 -0700239 if (ec)
240 {
241 BMCWEB_LOG_CRITICAL("Failed to finalize payload");
242 close();
243 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
244 }
245 }
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700246 crow::Request& thisReq = *it->second.req;
Ed Tanous8e8245d2024-04-11 22:21:38 -0700247 thisReq.ioService = static_cast<decltype(thisReq.ioService)>(
248 &adaptor.get_executor().context());
Ed Tanous62598e32023-07-17 17:06:25 -0700249 BMCWEB_LOG_DEBUG("Handling {} \"{}\"", logPtr(&thisReq),
250 thisReq.url().encoded_path());
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800251
Ed Tanousf42e8592023-08-25 10:47:44 -0700252 crow::Response& thisRes = it->second.res;
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800253
254 thisRes.setCompleteRequestHandler(
255 [this, streamId](Response& completeRes) {
Ed Tanous62598e32023-07-17 17:06:25 -0700256 BMCWEB_LOG_DEBUG("res.completeRequestHandler called");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800257 if (sendResponse(completeRes, streamId) != 0)
258 {
259 close();
260 return;
261 }
262 });
263 auto asyncResp =
Ed Tanousf42e8592023-08-25 10:47:44 -0700264 std::make_shared<bmcweb::AsyncResp>(std::move(it->second.res));
Ed Tanous83328312024-05-09 15:48:09 -0700265 if constexpr (!BMCWEB_INSECURE_DISABLE_AUTH)
Ed Tanous325310d2024-03-15 09:05:04 -0700266 {
Ed Tanous83328312024-05-09 15:48:09 -0700267 thisReq.session = crow::authentication::authenticate(
268 {}, asyncResp->res, thisReq.method(), thisReq.req, nullptr);
269 if (!crow::authentication::isOnAllowlist(thisReq.url().path(),
270 thisReq.method()) &&
271 thisReq.session == nullptr)
Ed Tanous499b5b42024-04-06 08:39:18 -0700272 {
Ed Tanous83328312024-05-09 15:48:09 -0700273 BMCWEB_LOG_WARNING("Authentication failed");
274 forward_unauthorized::sendUnauthorized(
275 thisReq.url().encoded_path(),
276 thisReq.getHeaderValue("X-Requested-With"),
277 thisReq.getHeaderValue("Accept"), asyncResp->res);
278 return 0;
Ed Tanous499b5b42024-04-06 08:39:18 -0700279 }
Ed Tanous325310d2024-03-15 09:05:04 -0700280 }
Ed Tanous83328312024-05-09 15:48:09 -0700281 std::string_view expected =
282 thisReq.getHeaderValue(boost::beast::http::field::if_none_match);
283 BMCWEB_LOG_DEBUG("Setting expected hash {}", expected);
284 if (!expected.empty())
285 {
286 asyncResp->res.setExpectedHash(expected);
287 }
288 handler->handle(it->second.req, asyncResp);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800289 return 0;
290 }
291
Ed Tanous325310d2024-03-15 09:05:04 -0700292 int onDataChunkRecvCallback(uint8_t /*flags*/, int32_t streamId,
293 const uint8_t* data, size_t len)
294 {
295 auto thisStream = streams.find(streamId);
296 if (thisStream == streams.end())
297 {
298 BMCWEB_LOG_ERROR("Unknown stream{}", streamId);
299 close();
300 return -1;
301 }
Ed Tanousd547d8d2024-03-16 18:04:41 -0700302
303 std::optional<bmcweb::HttpBody::reader>& reqReader =
304 thisStream->second.reqReader;
305 if (!reqReader)
Ed Tanous325310d2024-03-15 09:05:04 -0700306 {
Ed Tanous8e5cc7b2024-04-02 11:00:54 -0700307 reqReader.emplace(
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700308 bmcweb::HttpBody::reader(thisStream->second.req->req.base(),
309 thisStream->second.req->req.body()));
Ed Tanous325310d2024-03-15 09:05:04 -0700310 }
311 boost::beast::error_code ec;
Ed Tanousd547d8d2024-03-16 18:04:41 -0700312 reqReader->put(boost::asio::const_buffer(data, len), ec);
Ed Tanous325310d2024-03-15 09:05:04 -0700313 if (ec)
314 {
315 BMCWEB_LOG_CRITICAL("Failed to write payload");
316 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
317 }
318 return 0;
319 }
320
321 static int onDataChunkRecvStatic(nghttp2_session* /* session */,
322 uint8_t flags, int32_t streamId,
323 const uint8_t* data, size_t len,
324 void* userData)
325 {
326 BMCWEB_LOG_DEBUG("on_frame_recv_callback");
327 if (userData == nullptr)
328 {
329 BMCWEB_LOG_CRITICAL("user data was null?");
330 return NGHTTP2_ERR_CALLBACK_FAILURE;
331 }
332 return userPtrToSelf(userData).onDataChunkRecvCallback(flags, streamId,
333 data, len);
334 }
335
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800336 int onFrameRecvCallback(const nghttp2_frame& frame)
337 {
Ed Tanous62598e32023-07-17 17:06:25 -0700338 BMCWEB_LOG_DEBUG("frame type {}", static_cast<int>(frame.hd.type));
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800339 switch (frame.hd.type)
340 {
341 case NGHTTP2_DATA:
342 case NGHTTP2_HEADERS:
343 // Check that the client request has finished
344 if ((frame.hd.flags & NGHTTP2_FLAG_END_STREAM) != 0)
345 {
346 return onRequestRecv(frame.hd.stream_id);
347 }
348 break;
349 default:
350 break;
351 }
352 return 0;
353 }
354
355 static int onFrameRecvCallbackStatic(nghttp2_session* /* session */,
356 const nghttp2_frame* frame,
357 void* userData)
358 {
Ed Tanous62598e32023-07-17 17:06:25 -0700359 BMCWEB_LOG_DEBUG("on_frame_recv_callback");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800360 if (userData == nullptr)
361 {
Ed Tanous62598e32023-07-17 17:06:25 -0700362 BMCWEB_LOG_CRITICAL("user data was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800363 return NGHTTP2_ERR_CALLBACK_FAILURE;
364 }
365 if (frame == nullptr)
366 {
Ed Tanous62598e32023-07-17 17:06:25 -0700367 BMCWEB_LOG_CRITICAL("frame was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800368 return NGHTTP2_ERR_CALLBACK_FAILURE;
369 }
370 return userPtrToSelf(userData).onFrameRecvCallback(*frame);
371 }
372
373 static self_type& userPtrToSelf(void* userData)
374 {
375 // This method exists to keep the unsafe reinterpret cast in one
376 // place.
377 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
378 return *reinterpret_cast<self_type*>(userData);
379 }
380
381 static int onStreamCloseCallbackStatic(nghttp2_session* /* session */,
382 int32_t streamId,
383 uint32_t /*unused*/, void* userData)
384 {
Ed Tanous62598e32023-07-17 17:06:25 -0700385 BMCWEB_LOG_DEBUG("on_stream_close_callback stream {}", streamId);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800386 if (userData == nullptr)
387 {
Ed Tanous62598e32023-07-17 17:06:25 -0700388 BMCWEB_LOG_CRITICAL("user data was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800389 return NGHTTP2_ERR_CALLBACK_FAILURE;
390 }
Ed Tanousf42e8592023-08-25 10:47:44 -0700391 if (userPtrToSelf(userData).streams.erase(streamId) <= 0)
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800392 {
393 return -1;
394 }
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800395 return 0;
396 }
397
398 int onHeaderCallback(const nghttp2_frame& frame,
399 std::span<const uint8_t> name,
400 std::span<const uint8_t> value)
401 {
402 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
403 std::string_view nameSv(reinterpret_cast<const char*>(name.data()),
404 name.size());
405 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
406 std::string_view valueSv(reinterpret_cast<const char*>(value.data()),
407 value.size());
408
Ed Tanous62598e32023-07-17 17:06:25 -0700409 BMCWEB_LOG_DEBUG("on_header_callback name: {} value {}", nameSv,
410 valueSv);
Ed Tanousa07e9812024-03-19 10:31:13 -0700411 if (frame.hd.type != NGHTTP2_HEADERS)
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800412 {
Ed Tanousa07e9812024-03-19 10:31:13 -0700413 return 0;
414 }
415 if (frame.headers.cat != NGHTTP2_HCAT_REQUEST)
416 {
417 return 0;
418 }
419 auto thisStream = streams.find(frame.hd.stream_id);
420 if (thisStream == streams.end())
421 {
422 BMCWEB_LOG_ERROR("Unknown stream{}", frame.hd.stream_id);
423 close();
424 return -1;
425 }
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800426
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700427 crow::Request& thisReq = *thisStream->second.req;
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800428
Ed Tanousa07e9812024-03-19 10:31:13 -0700429 if (nameSv == ":path")
430 {
431 thisReq.target(valueSv);
432 }
433 else if (nameSv == ":method")
434 {
435 boost::beast::http::verb verb =
436 boost::beast::http::string_to_verb(valueSv);
437 if (verb == boost::beast::http::verb::unknown)
438 {
439 BMCWEB_LOG_ERROR("Unknown http verb {}", valueSv);
440 close();
441 return -1;
442 }
Myung Bae1873a042024-04-01 09:27:39 -0500443 thisReq.method(verb);
Ed Tanousa07e9812024-03-19 10:31:13 -0700444 }
445 else if (nameSv == ":scheme")
446 {
447 // Nothing to check on scheme
448 }
449 else
450 {
Myung Bae1873a042024-04-01 09:27:39 -0500451 thisReq.addHeader(nameSv, valueSv);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800452 }
453 return 0;
454 }
455
456 static int onHeaderCallbackStatic(nghttp2_session* /* session */,
457 const nghttp2_frame* frame,
458 const uint8_t* name, size_t namelen,
459 const uint8_t* value, size_t vallen,
460 uint8_t /* flags */, void* userData)
461 {
462 if (userData == nullptr)
463 {
Ed Tanous62598e32023-07-17 17:06:25 -0700464 BMCWEB_LOG_CRITICAL("user data was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800465 return NGHTTP2_ERR_CALLBACK_FAILURE;
466 }
467 if (frame == nullptr)
468 {
Ed Tanous62598e32023-07-17 17:06:25 -0700469 BMCWEB_LOG_CRITICAL("frame was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800470 return NGHTTP2_ERR_CALLBACK_FAILURE;
471 }
472 if (name == nullptr)
473 {
Ed Tanous62598e32023-07-17 17:06:25 -0700474 BMCWEB_LOG_CRITICAL("name was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800475 return NGHTTP2_ERR_CALLBACK_FAILURE;
476 }
477 if (value == nullptr)
478 {
Ed Tanous62598e32023-07-17 17:06:25 -0700479 BMCWEB_LOG_CRITICAL("value was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800480 return NGHTTP2_ERR_CALLBACK_FAILURE;
481 }
482 return userPtrToSelf(userData).onHeaderCallback(*frame, {name, namelen},
483 {value, vallen});
484 }
485
486 int onBeginHeadersCallback(const nghttp2_frame& frame)
487 {
488 if (frame.hd.type == NGHTTP2_HEADERS &&
489 frame.headers.cat == NGHTTP2_HCAT_REQUEST)
490 {
Ed Tanous62598e32023-07-17 17:06:25 -0700491 BMCWEB_LOG_DEBUG("create stream for id {}", frame.hd.stream_id);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800492
Ed Tanous3a499412024-06-05 16:05:35 -0700493 streams.emplace(frame.hd.stream_id, Http2StreamData());
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800494 }
495 return 0;
496 }
497
498 static int onBeginHeadersCallbackStatic(nghttp2_session* /* session */,
499 const nghttp2_frame* frame,
500 void* userData)
501 {
Ed Tanous62598e32023-07-17 17:06:25 -0700502 BMCWEB_LOG_DEBUG("on_begin_headers_callback");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800503 if (userData == nullptr)
504 {
Ed Tanous62598e32023-07-17 17:06:25 -0700505 BMCWEB_LOG_CRITICAL("user data was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800506 return NGHTTP2_ERR_CALLBACK_FAILURE;
507 }
508 if (frame == nullptr)
509 {
Ed Tanous62598e32023-07-17 17:06:25 -0700510 BMCWEB_LOG_CRITICAL("frame was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800511 return NGHTTP2_ERR_CALLBACK_FAILURE;
512 }
513 return userPtrToSelf(userData).onBeginHeadersCallback(*frame);
514 }
515
516 static void afterWriteBuffer(const std::shared_ptr<self_type>& self,
517 const boost::system::error_code& ec,
518 size_t sendLength)
519 {
520 self->isWriting = false;
Ed Tanous62598e32023-07-17 17:06:25 -0700521 BMCWEB_LOG_DEBUG("Sent {}", sendLength);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800522 if (ec)
523 {
524 self->close();
525 return;
526 }
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800527 self->writeBuffer();
528 }
529
530 void writeBuffer()
531 {
532 if (isWriting)
533 {
534 return;
535 }
Ed Tanousd0882182024-01-26 23:45:25 -0800536 std::span<const uint8_t> data = ngSession.memSend();
537 if (data.empty())
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800538 {
539 return;
540 }
541 isWriting = true;
Ed Tanous325310d2024-03-15 09:05:04 -0700542 boost::asio::async_write(
543 adaptor, boost::asio::const_buffer(data.data(), data.size()),
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800544 std::bind_front(afterWriteBuffer, shared_from_this()));
545 }
546
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800547 void close()
548 {
549 if constexpr (std::is_same_v<Adaptor,
Ed Tanous003301a2024-04-16 09:59:19 -0700550 boost::asio::ssl::stream<
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800551 boost::asio::ip::tcp::socket>>)
552 {
553 adaptor.next_layer().close();
554 }
555 else
556 {
557 adaptor.close();
558 }
559 }
560
Ed Tanousd0882182024-01-26 23:45:25 -0800561 void afterDoRead(const std::shared_ptr<self_type>& /*self*/,
562 const boost::system::error_code& ec,
563 size_t bytesTransferred)
564 {
565 BMCWEB_LOG_DEBUG("{} async_read_some {} Bytes", logPtr(this),
566 bytesTransferred);
567
568 if (ec)
569 {
570 BMCWEB_LOG_ERROR("{} Error while reading: {}", logPtr(this),
571 ec.message());
572 close();
573 BMCWEB_LOG_DEBUG("{} from read(1)", logPtr(this));
574 return;
575 }
576 std::span<uint8_t> bufferSpan{inBuffer.data(), bytesTransferred};
577
578 ssize_t readLen = ngSession.memRecv(bufferSpan);
579 if (readLen < 0)
580 {
581 BMCWEB_LOG_ERROR("nghttp2_session_mem_recv returned {}", readLen);
582 close();
583 return;
584 }
585 writeBuffer();
586
587 doRead();
588 }
589
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800590 void doRead()
591 {
Ed Tanous62598e32023-07-17 17:06:25 -0700592 BMCWEB_LOG_DEBUG("{} doRead", logPtr(this));
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800593 adaptor.async_read_some(
Ed Tanousd0882182024-01-26 23:45:25 -0800594 boost::asio::buffer(inBuffer),
595 std::bind_front(&self_type::afterDoRead, this, shared_from_this()));
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800596 }
597
598 // A mapping from http2 stream ID to Stream Data
Ed Tanous52e31622024-01-23 16:31:11 -0800599 std::map<int32_t, Http2StreamData> streams;
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800600
Ed Tanousd0882182024-01-26 23:45:25 -0800601 std::array<uint8_t, 8192> inBuffer{};
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800602
603 Adaptor adaptor;
604 bool isWriting = false;
605
606 nghttp2_session ngSession;
607
608 Handler* handler;
609 std::function<std::string()>& getCachedDateStr;
610
611 using std::enable_shared_from_this<
612 HTTP2Connection<Adaptor, Handler>>::shared_from_this;
613
614 using std::enable_shared_from_this<
615 HTTP2Connection<Adaptor, Handler>>::weak_from_this;
616};
617} // namespace crow