blob: 5399bd49474b87288e5e82cf9166122d7c8c9962 [file] [log] [blame]
Ed Tanous40e9b922024-09-10 13:50:16 -07001// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright OpenBMC Authors
Ed Tanousfca2cbe2021-01-28 14:49:59 -08003#pragma once
4#include "bmcweb_config.h"
5
6#include "async_resp.hpp"
7#include "authentication.hpp"
8#include "complete_response_fields.hpp"
Ed Tanousd7857202025-01-28 15:32:26 -08009#include "forward_unauthorized.hpp"
Ed Tanous325310d2024-03-15 09:05:04 -070010#include "http_body.hpp"
Ed Tanousd7857202025-01-28 15:32:26 -080011#include "http_request.hpp"
Ed Tanousfca2cbe2021-01-28 14:49:59 -080012#include "http_response.hpp"
Ed Tanousfca2cbe2021-01-28 14:49:59 -080013#include "logging.hpp"
Ed Tanousfca2cbe2021-01-28 14:49:59 -080014
Ed Tanousd7857202025-01-28 15:32:26 -080015// NOLINTNEXTLINE(misc-include-cleaner)
16#include "nghttp2_adapters.hpp"
17
18#include <nghttp2/nghttp2.h>
19#include <unistd.h>
20
21#include <boost/asio/buffer.hpp>
Ed Tanousfca2cbe2021-01-28 14:49:59 -080022#include <boost/asio/ssl/stream.hpp>
Ed Tanousd7857202025-01-28 15:32:26 -080023#include <boost/beast/core/error.hpp>
24#include <boost/beast/http/field.hpp>
25#include <boost/beast/http/fields.hpp>
26#include <boost/beast/http/message.hpp>
27#include <boost/beast/http/verb.hpp>
28#include <boost/optional/optional.hpp>
Ed Tanousd0882182024-01-26 23:45:25 -080029#include <boost/system/error_code.hpp>
Ed Tanousfca2cbe2021-01-28 14:49:59 -080030
Ed Tanousd0882182024-01-26 23:45:25 -080031#include <array>
Ed Tanousd7857202025-01-28 15:32:26 -080032#include <bit>
33#include <cstddef>
34#include <cstdint>
Ed Tanousd0882182024-01-26 23:45:25 -080035#include <functional>
Ed Tanousd7857202025-01-28 15:32:26 -080036#include <map>
Ed Tanousd0882182024-01-26 23:45:25 -080037#include <memory>
Ed Tanousd7857202025-01-28 15:32:26 -080038#include <optional>
39#include <span>
Ed Tanous89cda632024-04-16 08:45:54 -070040#include <string>
Ed Tanousd7857202025-01-28 15:32:26 -080041#include <string_view>
Ed Tanous796ba932020-08-02 04:29:21 +000042#include <type_traits>
Ed Tanousd7857202025-01-28 15:32:26 -080043#include <utility>
Ed Tanousfca2cbe2021-01-28 14:49:59 -080044#include <vector>
45
46namespace crow
47{
48
Ed Tanous796ba932020-08-02 04:29:21 +000049template <typename>
50struct IsTls : std::false_type
51{};
52
53template <typename T>
54struct IsTls<boost::asio::ssl::stream<T>> : std::true_type
55{};
56
Ed Tanousfca2cbe2021-01-28 14:49:59 -080057struct Http2StreamData
58{
Jonathan Doman102a4cd2024-04-15 16:56:23 -070059 std::shared_ptr<Request> req = std::make_shared<Request>();
Ed Tanous325310d2024-03-15 09:05:04 -070060 std::optional<bmcweb::HttpBody::reader> reqReader;
Ed Tanous89cda632024-04-16 08:45:54 -070061 std::string accept;
Ed Tanous47f29342024-03-19 12:18:06 -070062 Response res;
Ed Tanousb2896142024-01-31 15:25:47 -080063 std::optional<bmcweb::HttpBody::writer> writer;
Ed Tanousfca2cbe2021-01-28 14:49:59 -080064};
65
66template <typename Adaptor, typename Handler>
67class HTTP2Connection :
68 public std::enable_shared_from_this<HTTP2Connection<Adaptor, Handler>>
69{
70 using self_type = HTTP2Connection<Adaptor, Handler>;
71
72 public:
73 HTTP2Connection(Adaptor&& adaptorIn, Handler* handlerIn,
Ed Tanous52e31622024-01-23 16:31:11 -080074 std::function<std::string()>& getCachedDateStrF) :
Patrick Williamsbd79bce2024-08-16 15:22:20 -040075 adaptor(std::move(adaptorIn)), ngSession(initializeNghttp2Session()),
76 handler(handlerIn), getCachedDateStr(getCachedDateStrF)
Ed Tanousfca2cbe2021-01-28 14:49:59 -080077 {}
78
79 void start()
80 {
81 // Create the control stream
Ed Tanousf42e8592023-08-25 10:47:44 -070082 streams[0];
Ed Tanousfca2cbe2021-01-28 14:49:59 -080083
84 if (sendServerConnectionHeader() != 0)
85 {
Ed Tanous62598e32023-07-17 17:06:25 -070086 BMCWEB_LOG_ERROR("send_server_connection_header failed");
Ed Tanousfca2cbe2021-01-28 14:49:59 -080087 return;
88 }
89 doRead();
90 }
91
Ed Tanouscd7dbb32025-02-01 12:37:56 -080092 void startFromSettings(std::string_view http2UpgradeSettings)
93 {
94 int ret = ngSession.sessionUpgrade2(http2UpgradeSettings,
95 false /*head_request*/);
96 if (ret != 0)
97 {
98 BMCWEB_LOG_ERROR("Failed to load upgrade header");
99 return;
100 }
101 // Create the control stream
102 streams[0];
103
104 if (sendServerConnectionHeader() != 0)
105 {
106 BMCWEB_LOG_ERROR("send_server_connection_header failed");
107 return;
108 }
109 doRead();
110 }
111
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800112 int sendServerConnectionHeader()
113 {
Ed Tanous62598e32023-07-17 17:06:25 -0700114 BMCWEB_LOG_DEBUG("send_server_connection_header()");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800115
116 uint32_t maxStreams = 4;
117 std::array<nghttp2_settings_entry, 2> iv = {
118 {{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, maxStreams},
119 {NGHTTP2_SETTINGS_ENABLE_PUSH, 0}}};
120 int rv = ngSession.submitSettings(iv);
121 if (rv != 0)
122 {
Ed Tanous62598e32023-07-17 17:06:25 -0700123 BMCWEB_LOG_ERROR("Fatal error: {}", nghttp2_strerror(rv));
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800124 return -1;
125 }
Ed Tanousd0882182024-01-26 23:45:25 -0800126 writeBuffer();
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800127 return 0;
128 }
129
Patrick Williams504af5a2025-02-03 14:29:03 -0500130 static ssize_t fileReadCallback(
131 nghttp2_session* /* session */, int32_t streamId, uint8_t* buf,
132 size_t length, uint32_t* dataFlags, nghttp2_data_source* /*source*/,
133 void* userPtr)
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800134 {
Ed Tanousf42e8592023-08-25 10:47:44 -0700135 self_type& self = userPtrToSelf(userPtr);
136
137 auto streamIt = self.streams.find(streamId);
138 if (streamIt == self.streams.end())
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800139 {
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800140 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
141 }
Ed Tanousf42e8592023-08-25 10:47:44 -0700142 Http2StreamData& stream = streamIt->second;
Ed Tanous62598e32023-07-17 17:06:25 -0700143 BMCWEB_LOG_DEBUG("File read callback length: {}", length);
Ed Tanousd547d8d2024-03-16 18:04:41 -0700144 if (!stream.writer)
145 {
146 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
147 }
Ed Tanous52e31622024-01-23 16:31:11 -0800148 boost::beast::error_code ec;
149 boost::optional<std::pair<boost::asio::const_buffer, bool>> out =
150 stream.writer->getWithMaxSize(ec, length);
151 if (ec)
Ed Tanous27b0cf92023-08-07 12:02:40 -0700152 {
Ed Tanous325310d2024-03-15 09:05:04 -0700153 BMCWEB_LOG_CRITICAL("Failed to get buffer");
Ed Tanous27b0cf92023-08-07 12:02:40 -0700154 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
155 }
Ed Tanous52e31622024-01-23 16:31:11 -0800156 if (!out)
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800157 {
Ed Tanous325310d2024-03-15 09:05:04 -0700158 BMCWEB_LOG_ERROR("Empty file, setting EOF");
Ed Tanous52e31622024-01-23 16:31:11 -0800159 *dataFlags |= NGHTTP2_DATA_FLAG_EOF;
160 return 0;
161 }
162
163 BMCWEB_LOG_DEBUG("Send chunk of size: {}", out->first.size());
164 if (length < out->first.size())
165 {
Ed Tanous325310d2024-03-15 09:05:04 -0700166 BMCWEB_LOG_CRITICAL(
167 "Buffer overflow that should never happen happened");
Ed Tanous52e31622024-01-23 16:31:11 -0800168 // Should never happen because of length limit on get() above
169 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
170 }
Ed Tanous325310d2024-03-15 09:05:04 -0700171 boost::asio::mutable_buffer writeableBuf(buf, length);
Ed Tanous52e31622024-01-23 16:31:11 -0800172 BMCWEB_LOG_DEBUG("Copying {} bytes to buf", out->first.size());
Ed Tanous325310d2024-03-15 09:05:04 -0700173 size_t copied = boost::asio::buffer_copy(writeableBuf, out->first);
174 if (copied != out->first.size())
175 {
176 BMCWEB_LOG_ERROR(
177 "Couldn't copy all {} bytes into buffer, only copied {}",
178 out->first.size(), copied);
179 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
180 }
Ed Tanous52e31622024-01-23 16:31:11 -0800181
182 if (!out->second)
183 {
Ed Tanous325310d2024-03-15 09:05:04 -0700184 BMCWEB_LOG_DEBUG("Setting EOF flag");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800185 *dataFlags |= NGHTTP2_DATA_FLAG_EOF;
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800186 }
Ed Tanous325310d2024-03-15 09:05:04 -0700187 return static_cast<ssize_t>(copied);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800188 }
189
190 nghttp2_nv headerFromStringViews(std::string_view name,
Ed Tanous52e31622024-01-23 16:31:11 -0800191 std::string_view value, uint8_t flags)
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800192 {
193 uint8_t* nameData = std::bit_cast<uint8_t*>(name.data());
194 uint8_t* valueData = std::bit_cast<uint8_t*>(value.data());
Ed Tanous52e31622024-01-23 16:31:11 -0800195 return {nameData, valueData, name.size(), value.size(), flags};
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800196 }
197
198 int sendResponse(Response& completedRes, int32_t streamId)
199 {
Ed Tanous62598e32023-07-17 17:06:25 -0700200 BMCWEB_LOG_DEBUG("send_response stream_id:{}", streamId);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800201
202 auto it = streams.find(streamId);
203 if (it == streams.end())
204 {
205 close();
206 return -1;
207 }
Ed Tanous499b5b42024-04-06 08:39:18 -0700208 Http2StreamData& stream = it->second;
209 Response& res = stream.res;
210 res = std::move(completedRes);
Ed Tanous499b5b42024-04-06 08:39:18 -0700211
Ed Tanous89cda632024-04-16 08:45:54 -0700212 completeResponseFields(stream.accept, res);
Ed Tanous499b5b42024-04-06 08:39:18 -0700213 res.addHeader(boost::beast::http::field::date, getCachedDateStr());
214 res.preparePayload();
215
216 boost::beast::http::fields& fields = res.fields();
217 std::string code = std::to_string(res.resultInt());
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800218 std::vector<nghttp2_nv> hdr;
Ed Tanous52e31622024-01-23 16:31:11 -0800219 hdr.emplace_back(
220 headerFromStringViews(":status", code, NGHTTP2_NV_FLAG_NONE));
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800221 for (const boost::beast::http::fields::value_type& header : fields)
222 {
Ed Tanous52e31622024-01-23 16:31:11 -0800223 hdr.emplace_back(headerFromStringViews(
Ed Tanousd0882182024-01-26 23:45:25 -0800224 header.name_string(), header.value(), NGHTTP2_NV_FLAG_NONE));
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800225 }
Ed Tanousb2896142024-01-31 15:25:47 -0800226 http::response<bmcweb::HttpBody>& fbody = res.response;
Ed Tanous52e31622024-01-23 16:31:11 -0800227 stream.writer.emplace(fbody.base(), fbody.body());
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800228
229 nghttp2_data_provider dataPrd{
Ed Tanousf42e8592023-08-25 10:47:44 -0700230 .source = {.fd = 0},
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800231 .read_callback = fileReadCallback,
232 };
233
234 int rv = ngSession.submitResponse(streamId, hdr, &dataPrd);
235 if (rv != 0)
236 {
Ed Tanous62598e32023-07-17 17:06:25 -0700237 BMCWEB_LOG_ERROR("Fatal error: {}", nghttp2_strerror(rv));
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800238 close();
239 return -1;
240 }
Ed Tanousd0882182024-01-26 23:45:25 -0800241 writeBuffer();
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800242
243 return 0;
244 }
245
246 nghttp2_session initializeNghttp2Session()
247 {
248 nghttp2_session_callbacks callbacks;
249 callbacks.setOnFrameRecvCallback(onFrameRecvCallbackStatic);
250 callbacks.setOnStreamCloseCallback(onStreamCloseCallbackStatic);
251 callbacks.setOnHeaderCallback(onHeaderCallbackStatic);
252 callbacks.setOnBeginHeadersCallback(onBeginHeadersCallbackStatic);
Ed Tanous325310d2024-03-15 09:05:04 -0700253 callbacks.setOnDataChunkRecvCallback(onDataChunkRecvStatic);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800254
255 nghttp2_session session(callbacks);
256 session.setUserData(this);
257
258 return session;
259 }
260
261 int onRequestRecv(int32_t streamId)
262 {
Ed Tanous62598e32023-07-17 17:06:25 -0700263 BMCWEB_LOG_DEBUG("on_request_recv");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800264
265 auto it = streams.find(streamId);
266 if (it == streams.end())
267 {
268 close();
269 return -1;
270 }
Ed Tanousd547d8d2024-03-16 18:04:41 -0700271 auto& reqReader = it->second.reqReader;
272 if (reqReader)
Ed Tanous325310d2024-03-15 09:05:04 -0700273 {
274 boost::beast::error_code ec;
Ed Tanousdaadfb22024-12-20 09:25:54 -0800275 bmcweb::HttpBody::reader::finish(ec);
Ed Tanous325310d2024-03-15 09:05:04 -0700276 if (ec)
277 {
278 BMCWEB_LOG_CRITICAL("Failed to finalize payload");
279 close();
280 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
281 }
282 }
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700283 crow::Request& thisReq = *it->second.req;
Ed Tanous8e8245d2024-04-11 22:21:38 -0700284 thisReq.ioService = static_cast<decltype(thisReq.ioService)>(
285 &adaptor.get_executor().context());
Ed Tanous89cda632024-04-16 08:45:54 -0700286
287 it->second.accept = thisReq.getHeaderValue("Accept");
288
Ed Tanous62598e32023-07-17 17:06:25 -0700289 BMCWEB_LOG_DEBUG("Handling {} \"{}\"", logPtr(&thisReq),
290 thisReq.url().encoded_path());
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800291
Ed Tanousf42e8592023-08-25 10:47:44 -0700292 crow::Response& thisRes = it->second.res;
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800293
294 thisRes.setCompleteRequestHandler(
295 [this, streamId](Response& completeRes) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400296 BMCWEB_LOG_DEBUG("res.completeRequestHandler called");
297 if (sendResponse(completeRes, streamId) != 0)
298 {
299 close();
300 return;
301 }
302 });
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800303 auto asyncResp =
Ed Tanousf42e8592023-08-25 10:47:44 -0700304 std::make_shared<bmcweb::AsyncResp>(std::move(it->second.res));
Ed Tanous83328312024-05-09 15:48:09 -0700305 if constexpr (!BMCWEB_INSECURE_DISABLE_AUTH)
Ed Tanous325310d2024-03-15 09:05:04 -0700306 {
Ed Tanous83328312024-05-09 15:48:09 -0700307 thisReq.session = crow::authentication::authenticate(
308 {}, asyncResp->res, thisReq.method(), thisReq.req, nullptr);
309 if (!crow::authentication::isOnAllowlist(thisReq.url().path(),
310 thisReq.method()) &&
311 thisReq.session == nullptr)
Ed Tanous499b5b42024-04-06 08:39:18 -0700312 {
Ed Tanous83328312024-05-09 15:48:09 -0700313 BMCWEB_LOG_WARNING("Authentication failed");
314 forward_unauthorized::sendUnauthorized(
315 thisReq.url().encoded_path(),
316 thisReq.getHeaderValue("X-Requested-With"),
317 thisReq.getHeaderValue("Accept"), asyncResp->res);
318 return 0;
Ed Tanous499b5b42024-04-06 08:39:18 -0700319 }
Ed Tanous325310d2024-03-15 09:05:04 -0700320 }
Ed Tanous83328312024-05-09 15:48:09 -0700321 std::string_view expected =
322 thisReq.getHeaderValue(boost::beast::http::field::if_none_match);
323 BMCWEB_LOG_DEBUG("Setting expected hash {}", expected);
324 if (!expected.empty())
325 {
326 asyncResp->res.setExpectedHash(expected);
327 }
328 handler->handle(it->second.req, asyncResp);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800329 return 0;
330 }
331
Ed Tanous325310d2024-03-15 09:05:04 -0700332 int onDataChunkRecvCallback(uint8_t /*flags*/, int32_t streamId,
333 const uint8_t* data, size_t len)
334 {
335 auto thisStream = streams.find(streamId);
336 if (thisStream == streams.end())
337 {
338 BMCWEB_LOG_ERROR("Unknown stream{}", streamId);
339 close();
340 return -1;
341 }
Ed Tanousd547d8d2024-03-16 18:04:41 -0700342
343 std::optional<bmcweb::HttpBody::reader>& reqReader =
344 thisStream->second.reqReader;
345 if (!reqReader)
Ed Tanous325310d2024-03-15 09:05:04 -0700346 {
Ed Tanous8e5cc7b2024-04-02 11:00:54 -0700347 reqReader.emplace(
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700348 bmcweb::HttpBody::reader(thisStream->second.req->req.base(),
349 thisStream->second.req->req.body()));
Ed Tanous325310d2024-03-15 09:05:04 -0700350 }
351 boost::beast::error_code ec;
Ed Tanousd547d8d2024-03-16 18:04:41 -0700352 reqReader->put(boost::asio::const_buffer(data, len), ec);
Ed Tanous325310d2024-03-15 09:05:04 -0700353 if (ec)
354 {
355 BMCWEB_LOG_CRITICAL("Failed to write payload");
356 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
357 }
358 return 0;
359 }
360
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400361 static int onDataChunkRecvStatic(
362 nghttp2_session* /* session */, uint8_t flags, int32_t streamId,
363 const uint8_t* data, size_t len, void* userData)
Ed Tanous325310d2024-03-15 09:05:04 -0700364 {
365 BMCWEB_LOG_DEBUG("on_frame_recv_callback");
366 if (userData == nullptr)
367 {
368 BMCWEB_LOG_CRITICAL("user data was null?");
369 return NGHTTP2_ERR_CALLBACK_FAILURE;
370 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400371 return userPtrToSelf(userData).onDataChunkRecvCallback(
372 flags, streamId, data, len);
Ed Tanous325310d2024-03-15 09:05:04 -0700373 }
374
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800375 int onFrameRecvCallback(const nghttp2_frame& frame)
376 {
Ed Tanous62598e32023-07-17 17:06:25 -0700377 BMCWEB_LOG_DEBUG("frame type {}", static_cast<int>(frame.hd.type));
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800378 switch (frame.hd.type)
379 {
380 case NGHTTP2_DATA:
381 case NGHTTP2_HEADERS:
382 // Check that the client request has finished
383 if ((frame.hd.flags & NGHTTP2_FLAG_END_STREAM) != 0)
384 {
385 return onRequestRecv(frame.hd.stream_id);
386 }
387 break;
388 default:
389 break;
390 }
391 return 0;
392 }
393
394 static int onFrameRecvCallbackStatic(nghttp2_session* /* session */,
395 const nghttp2_frame* frame,
396 void* userData)
397 {
Ed Tanous62598e32023-07-17 17:06:25 -0700398 BMCWEB_LOG_DEBUG("on_frame_recv_callback");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800399 if (userData == nullptr)
400 {
Ed Tanous62598e32023-07-17 17:06:25 -0700401 BMCWEB_LOG_CRITICAL("user data was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800402 return NGHTTP2_ERR_CALLBACK_FAILURE;
403 }
404 if (frame == nullptr)
405 {
Ed Tanous62598e32023-07-17 17:06:25 -0700406 BMCWEB_LOG_CRITICAL("frame was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800407 return NGHTTP2_ERR_CALLBACK_FAILURE;
408 }
409 return userPtrToSelf(userData).onFrameRecvCallback(*frame);
410 }
411
412 static self_type& userPtrToSelf(void* userData)
413 {
414 // This method exists to keep the unsafe reinterpret cast in one
415 // place.
416 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
417 return *reinterpret_cast<self_type*>(userData);
418 }
419
420 static int onStreamCloseCallbackStatic(nghttp2_session* /* session */,
421 int32_t streamId,
422 uint32_t /*unused*/, void* userData)
423 {
Ed Tanous62598e32023-07-17 17:06:25 -0700424 BMCWEB_LOG_DEBUG("on_stream_close_callback stream {}", streamId);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800425 if (userData == nullptr)
426 {
Ed Tanous62598e32023-07-17 17:06:25 -0700427 BMCWEB_LOG_CRITICAL("user data was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800428 return NGHTTP2_ERR_CALLBACK_FAILURE;
429 }
Ed Tanousf42e8592023-08-25 10:47:44 -0700430 if (userPtrToSelf(userData).streams.erase(streamId) <= 0)
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800431 {
432 return -1;
433 }
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800434 return 0;
435 }
436
437 int onHeaderCallback(const nghttp2_frame& frame,
438 std::span<const uint8_t> name,
439 std::span<const uint8_t> value)
440 {
441 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
442 std::string_view nameSv(reinterpret_cast<const char*>(name.data()),
443 name.size());
444 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
445 std::string_view valueSv(reinterpret_cast<const char*>(value.data()),
446 value.size());
447
Ed Tanous62598e32023-07-17 17:06:25 -0700448 BMCWEB_LOG_DEBUG("on_header_callback name: {} value {}", nameSv,
449 valueSv);
Ed Tanousa07e9812024-03-19 10:31:13 -0700450 if (frame.hd.type != NGHTTP2_HEADERS)
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800451 {
Ed Tanousa07e9812024-03-19 10:31:13 -0700452 return 0;
453 }
454 if (frame.headers.cat != NGHTTP2_HCAT_REQUEST)
455 {
456 return 0;
457 }
458 auto thisStream = streams.find(frame.hd.stream_id);
459 if (thisStream == streams.end())
460 {
461 BMCWEB_LOG_ERROR("Unknown stream{}", frame.hd.stream_id);
462 close();
463 return -1;
464 }
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800465
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700466 crow::Request& thisReq = *thisStream->second.req;
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800467
Ed Tanousa07e9812024-03-19 10:31:13 -0700468 if (nameSv == ":path")
469 {
470 thisReq.target(valueSv);
471 }
472 else if (nameSv == ":method")
473 {
474 boost::beast::http::verb verb =
475 boost::beast::http::string_to_verb(valueSv);
476 if (verb == boost::beast::http::verb::unknown)
477 {
478 BMCWEB_LOG_ERROR("Unknown http verb {}", valueSv);
Ed Tanous50bfc912024-07-29 14:20:50 -0700479 verb = boost::beast::http::verb::trace;
Ed Tanousa07e9812024-03-19 10:31:13 -0700480 }
Myung Bae1873a042024-04-01 09:27:39 -0500481 thisReq.method(verb);
Ed Tanousa07e9812024-03-19 10:31:13 -0700482 }
483 else if (nameSv == ":scheme")
484 {
485 // Nothing to check on scheme
486 }
487 else
488 {
Myung Bae1873a042024-04-01 09:27:39 -0500489 thisReq.addHeader(nameSv, valueSv);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800490 }
491 return 0;
492 }
493
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400494 static int onHeaderCallbackStatic(
495 nghttp2_session* /* session */, const nghttp2_frame* frame,
496 const uint8_t* name, size_t namelen, const uint8_t* value,
497 size_t vallen, uint8_t /* flags */, void* userData)
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800498 {
499 if (userData == nullptr)
500 {
Ed Tanous62598e32023-07-17 17:06:25 -0700501 BMCWEB_LOG_CRITICAL("user data was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800502 return NGHTTP2_ERR_CALLBACK_FAILURE;
503 }
504 if (frame == nullptr)
505 {
Ed Tanous62598e32023-07-17 17:06:25 -0700506 BMCWEB_LOG_CRITICAL("frame was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800507 return NGHTTP2_ERR_CALLBACK_FAILURE;
508 }
509 if (name == nullptr)
510 {
Ed Tanous62598e32023-07-17 17:06:25 -0700511 BMCWEB_LOG_CRITICAL("name was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800512 return NGHTTP2_ERR_CALLBACK_FAILURE;
513 }
514 if (value == nullptr)
515 {
Ed Tanous62598e32023-07-17 17:06:25 -0700516 BMCWEB_LOG_CRITICAL("value was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800517 return NGHTTP2_ERR_CALLBACK_FAILURE;
518 }
519 return userPtrToSelf(userData).onHeaderCallback(*frame, {name, namelen},
520 {value, vallen});
521 }
522
523 int onBeginHeadersCallback(const nghttp2_frame& frame)
524 {
525 if (frame.hd.type == NGHTTP2_HEADERS &&
526 frame.headers.cat == NGHTTP2_HCAT_REQUEST)
527 {
Ed Tanous62598e32023-07-17 17:06:25 -0700528 BMCWEB_LOG_DEBUG("create stream for id {}", frame.hd.stream_id);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800529
Ed Tanous3a499412024-06-05 16:05:35 -0700530 streams.emplace(frame.hd.stream_id, Http2StreamData());
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800531 }
532 return 0;
533 }
534
535 static int onBeginHeadersCallbackStatic(nghttp2_session* /* session */,
536 const nghttp2_frame* frame,
537 void* userData)
538 {
Ed Tanous62598e32023-07-17 17:06:25 -0700539 BMCWEB_LOG_DEBUG("on_begin_headers_callback");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800540 if (userData == nullptr)
541 {
Ed Tanous62598e32023-07-17 17:06:25 -0700542 BMCWEB_LOG_CRITICAL("user data was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800543 return NGHTTP2_ERR_CALLBACK_FAILURE;
544 }
545 if (frame == nullptr)
546 {
Ed Tanous62598e32023-07-17 17:06:25 -0700547 BMCWEB_LOG_CRITICAL("frame was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800548 return NGHTTP2_ERR_CALLBACK_FAILURE;
549 }
550 return userPtrToSelf(userData).onBeginHeadersCallback(*frame);
551 }
552
553 static void afterWriteBuffer(const std::shared_ptr<self_type>& self,
554 const boost::system::error_code& ec,
555 size_t sendLength)
556 {
557 self->isWriting = false;
Ed Tanous62598e32023-07-17 17:06:25 -0700558 BMCWEB_LOG_DEBUG("Sent {}", sendLength);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800559 if (ec)
560 {
561 self->close();
562 return;
563 }
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800564 self->writeBuffer();
565 }
566
567 void writeBuffer()
568 {
569 if (isWriting)
570 {
571 return;
572 }
Ed Tanousd0882182024-01-26 23:45:25 -0800573 std::span<const uint8_t> data = ngSession.memSend();
574 if (data.empty())
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800575 {
576 return;
577 }
578 isWriting = true;
Ed Tanous325310d2024-03-15 09:05:04 -0700579 boost::asio::async_write(
580 adaptor, boost::asio::const_buffer(data.data(), data.size()),
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800581 std::bind_front(afterWriteBuffer, shared_from_this()));
582 }
583
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800584 void close()
585 {
Ed Tanous796ba932020-08-02 04:29:21 +0000586 if constexpr (IsTls<Adaptor>::value)
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800587 {
588 adaptor.next_layer().close();
589 }
590 else
591 {
592 adaptor.close();
593 }
594 }
595
Ed Tanousd0882182024-01-26 23:45:25 -0800596 void afterDoRead(const std::shared_ptr<self_type>& /*self*/,
597 const boost::system::error_code& ec,
598 size_t bytesTransferred)
599 {
600 BMCWEB_LOG_DEBUG("{} async_read_some {} Bytes", logPtr(this),
601 bytesTransferred);
602
603 if (ec)
604 {
605 BMCWEB_LOG_ERROR("{} Error while reading: {}", logPtr(this),
606 ec.message());
607 close();
608 BMCWEB_LOG_DEBUG("{} from read(1)", logPtr(this));
609 return;
610 }
611 std::span<uint8_t> bufferSpan{inBuffer.data(), bytesTransferred};
612
613 ssize_t readLen = ngSession.memRecv(bufferSpan);
614 if (readLen < 0)
615 {
616 BMCWEB_LOG_ERROR("nghttp2_session_mem_recv returned {}", readLen);
617 close();
618 return;
619 }
620 writeBuffer();
621
622 doRead();
623 }
624
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800625 void doRead()
626 {
Ed Tanous62598e32023-07-17 17:06:25 -0700627 BMCWEB_LOG_DEBUG("{} doRead", logPtr(this));
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800628 adaptor.async_read_some(
Ed Tanousd0882182024-01-26 23:45:25 -0800629 boost::asio::buffer(inBuffer),
630 std::bind_front(&self_type::afterDoRead, this, shared_from_this()));
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800631 }
632
633 // A mapping from http2 stream ID to Stream Data
Ed Tanous52e31622024-01-23 16:31:11 -0800634 std::map<int32_t, Http2StreamData> streams;
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800635
Ed Tanousd0882182024-01-26 23:45:25 -0800636 std::array<uint8_t, 8192> inBuffer{};
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800637
638 Adaptor adaptor;
639 bool isWriting = false;
640
641 nghttp2_session ngSession;
642
643 Handler* handler;
644 std::function<std::string()>& getCachedDateStr;
645
646 using std::enable_shared_from_this<
647 HTTP2Connection<Adaptor, Handler>>::shared_from_this;
648
649 using std::enable_shared_from_this<
650 HTTP2Connection<Adaptor, Handler>>::weak_from_this;
651};
652} // namespace crow