blob: 7903ec93b1698daa8b4eacae1cf0268c5c599dd3 [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>
25#include <boost/beast/ssl/ssl_stream.hpp>
26#include <boost/beast/websocket.hpp>
Ed Tanousd0882182024-01-26 23:45:25 -080027#include <boost/system/error_code.hpp>
Ed Tanousfca2cbe2021-01-28 14:49:59 -080028
Ed Tanousd0882182024-01-26 23:45:25 -080029#include <array>
Ed Tanousfca2cbe2021-01-28 14:49:59 -080030#include <atomic>
31#include <chrono>
Ed Tanousd0882182024-01-26 23:45:25 -080032#include <functional>
33#include <memory>
Ed Tanousfca2cbe2021-01-28 14:49:59 -080034#include <vector>
35
36namespace crow
37{
38
39struct Http2StreamData
40{
Ed Tanous52e31622024-01-23 16:31:11 -080041 Request req{};
Ed Tanous325310d2024-03-15 09:05:04 -070042 std::optional<bmcweb::HttpBody::reader> reqReader;
Ed Tanous52e31622024-01-23 16:31:11 -080043 Response res{};
Ed Tanousb2896142024-01-31 15:25:47 -080044 std::optional<bmcweb::HttpBody::writer> writer;
Ed Tanousfca2cbe2021-01-28 14:49:59 -080045};
46
47template <typename Adaptor, typename Handler>
48class HTTP2Connection :
49 public std::enable_shared_from_this<HTTP2Connection<Adaptor, Handler>>
50{
51 using self_type = HTTP2Connection<Adaptor, Handler>;
52
53 public:
54 HTTP2Connection(Adaptor&& adaptorIn, Handler* handlerIn,
Ed Tanous52e31622024-01-23 16:31:11 -080055 std::function<std::string()>& getCachedDateStrF) :
Ed Tanousfca2cbe2021-01-28 14:49:59 -080056 adaptor(std::move(adaptorIn)),
Ed Tanous52e31622024-01-23 16:31:11 -080057 ngSession(initializeNghttp2Session()), handler(handlerIn),
58 getCachedDateStr(getCachedDateStrF)
Ed Tanousfca2cbe2021-01-28 14:49:59 -080059 {}
60
61 void start()
62 {
63 // Create the control stream
Ed Tanousf42e8592023-08-25 10:47:44 -070064 streams[0];
Ed Tanousfca2cbe2021-01-28 14:49:59 -080065
66 if (sendServerConnectionHeader() != 0)
67 {
Ed Tanous62598e32023-07-17 17:06:25 -070068 BMCWEB_LOG_ERROR("send_server_connection_header failed");
Ed Tanousfca2cbe2021-01-28 14:49:59 -080069 return;
70 }
71 doRead();
72 }
73
74 int sendServerConnectionHeader()
75 {
Ed Tanous62598e32023-07-17 17:06:25 -070076 BMCWEB_LOG_DEBUG("send_server_connection_header()");
Ed Tanousfca2cbe2021-01-28 14:49:59 -080077
78 uint32_t maxStreams = 4;
79 std::array<nghttp2_settings_entry, 2> iv = {
80 {{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, maxStreams},
81 {NGHTTP2_SETTINGS_ENABLE_PUSH, 0}}};
82 int rv = ngSession.submitSettings(iv);
83 if (rv != 0)
84 {
Ed Tanous62598e32023-07-17 17:06:25 -070085 BMCWEB_LOG_ERROR("Fatal error: {}", nghttp2_strerror(rv));
Ed Tanousfca2cbe2021-01-28 14:49:59 -080086 return -1;
87 }
Ed Tanousd0882182024-01-26 23:45:25 -080088 writeBuffer();
Ed Tanousfca2cbe2021-01-28 14:49:59 -080089 return 0;
90 }
91
92 static ssize_t fileReadCallback(nghttp2_session* /* session */,
Ed Tanousf42e8592023-08-25 10:47:44 -070093 int32_t streamId, uint8_t* buf,
Ed Tanousfca2cbe2021-01-28 14:49:59 -080094 size_t length, uint32_t* dataFlags,
Ed Tanousf42e8592023-08-25 10:47:44 -070095 nghttp2_data_source* /*source*/,
96 void* userPtr)
Ed Tanousfca2cbe2021-01-28 14:49:59 -080097 {
Ed Tanousf42e8592023-08-25 10:47:44 -070098 self_type& self = userPtrToSelf(userPtr);
99
100 auto streamIt = self.streams.find(streamId);
101 if (streamIt == self.streams.end())
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800102 {
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800103 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
104 }
Ed Tanousf42e8592023-08-25 10:47:44 -0700105 Http2StreamData& stream = streamIt->second;
Ed Tanous62598e32023-07-17 17:06:25 -0700106 BMCWEB_LOG_DEBUG("File read callback length: {}", length);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800107
Ed Tanous52e31622024-01-23 16:31:11 -0800108 boost::beast::error_code ec;
109 boost::optional<std::pair<boost::asio::const_buffer, bool>> out =
110 stream.writer->getWithMaxSize(ec, length);
111 if (ec)
Ed Tanous27b0cf92023-08-07 12:02:40 -0700112 {
Ed Tanous325310d2024-03-15 09:05:04 -0700113 BMCWEB_LOG_CRITICAL("Failed to get buffer");
Ed Tanous27b0cf92023-08-07 12:02:40 -0700114 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
115 }
Ed Tanous52e31622024-01-23 16:31:11 -0800116 if (!out)
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800117 {
Ed Tanous325310d2024-03-15 09:05:04 -0700118 BMCWEB_LOG_ERROR("Empty file, setting EOF");
Ed Tanous52e31622024-01-23 16:31:11 -0800119 *dataFlags |= NGHTTP2_DATA_FLAG_EOF;
120 return 0;
121 }
122
123 BMCWEB_LOG_DEBUG("Send chunk of size: {}", out->first.size());
124 if (length < out->first.size())
125 {
Ed Tanous325310d2024-03-15 09:05:04 -0700126 BMCWEB_LOG_CRITICAL(
127 "Buffer overflow that should never happen happened");
Ed Tanous52e31622024-01-23 16:31:11 -0800128 // Should never happen because of length limit on get() above
129 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
130 }
Ed Tanous325310d2024-03-15 09:05:04 -0700131 boost::asio::mutable_buffer writeableBuf(buf, length);
Ed Tanous52e31622024-01-23 16:31:11 -0800132 BMCWEB_LOG_DEBUG("Copying {} bytes to buf", out->first.size());
Ed Tanous325310d2024-03-15 09:05:04 -0700133 size_t copied = boost::asio::buffer_copy(writeableBuf, out->first);
134 if (copied != out->first.size())
135 {
136 BMCWEB_LOG_ERROR(
137 "Couldn't copy all {} bytes into buffer, only copied {}",
138 out->first.size(), copied);
139 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
140 }
Ed Tanous52e31622024-01-23 16:31:11 -0800141
142 if (!out->second)
143 {
Ed Tanous325310d2024-03-15 09:05:04 -0700144 BMCWEB_LOG_DEBUG("Setting EOF flag");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800145 *dataFlags |= NGHTTP2_DATA_FLAG_EOF;
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800146 }
Ed Tanous325310d2024-03-15 09:05:04 -0700147 return static_cast<ssize_t>(copied);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800148 }
149
150 nghttp2_nv headerFromStringViews(std::string_view name,
Ed Tanous52e31622024-01-23 16:31:11 -0800151 std::string_view value, uint8_t flags)
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800152 {
153 uint8_t* nameData = std::bit_cast<uint8_t*>(name.data());
154 uint8_t* valueData = std::bit_cast<uint8_t*>(value.data());
Ed Tanous52e31622024-01-23 16:31:11 -0800155 return {nameData, valueData, name.size(), value.size(), flags};
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800156 }
157
158 int sendResponse(Response& completedRes, int32_t streamId)
159 {
Ed Tanous62598e32023-07-17 17:06:25 -0700160 BMCWEB_LOG_DEBUG("send_response stream_id:{}", streamId);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800161
162 auto it = streams.find(streamId);
163 if (it == streams.end())
164 {
165 close();
166 return -1;
167 }
Ed Tanousf42e8592023-08-25 10:47:44 -0700168 Response& thisRes = it->second.res;
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800169 thisRes = std::move(completedRes);
Ed Tanousf42e8592023-08-25 10:47:44 -0700170 crow::Request& thisReq = it->second.req;
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800171 std::vector<nghttp2_nv> hdr;
172
173 completeResponseFields(thisReq, thisRes);
174 thisRes.addHeader(boost::beast::http::field::date, getCachedDateStr());
Ed Tanous325310d2024-03-15 09:05:04 -0700175 thisRes.preparePayload();
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800176
Ed Tanous27b0cf92023-08-07 12:02:40 -0700177 boost::beast::http::fields& fields = thisRes.fields();
178 std::string code = std::to_string(thisRes.resultInt());
Ed Tanous52e31622024-01-23 16:31:11 -0800179 hdr.emplace_back(
180 headerFromStringViews(":status", code, NGHTTP2_NV_FLAG_NONE));
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800181 for (const boost::beast::http::fields::value_type& header : fields)
182 {
Ed Tanous52e31622024-01-23 16:31:11 -0800183 hdr.emplace_back(headerFromStringViews(
Ed Tanousd0882182024-01-26 23:45:25 -0800184 header.name_string(), header.value(), NGHTTP2_NV_FLAG_NONE));
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800185 }
Ed Tanousf42e8592023-08-25 10:47:44 -0700186 Http2StreamData& stream = it->second;
Ed Tanous52e31622024-01-23 16:31:11 -0800187 crow::Response& res = stream.res;
Ed Tanousb2896142024-01-31 15:25:47 -0800188 http::response<bmcweb::HttpBody>& fbody = res.response;
Ed Tanous52e31622024-01-23 16:31:11 -0800189 stream.writer.emplace(fbody.base(), fbody.body());
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800190
191 nghttp2_data_provider dataPrd{
Ed Tanousf42e8592023-08-25 10:47:44 -0700192 .source = {.fd = 0},
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800193 .read_callback = fileReadCallback,
194 };
195
196 int rv = ngSession.submitResponse(streamId, hdr, &dataPrd);
197 if (rv != 0)
198 {
Ed Tanous62598e32023-07-17 17:06:25 -0700199 BMCWEB_LOG_ERROR("Fatal error: {}", nghttp2_strerror(rv));
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800200 close();
201 return -1;
202 }
Ed Tanousd0882182024-01-26 23:45:25 -0800203 writeBuffer();
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800204
205 return 0;
206 }
207
208 nghttp2_session initializeNghttp2Session()
209 {
210 nghttp2_session_callbacks callbacks;
211 callbacks.setOnFrameRecvCallback(onFrameRecvCallbackStatic);
212 callbacks.setOnStreamCloseCallback(onStreamCloseCallbackStatic);
213 callbacks.setOnHeaderCallback(onHeaderCallbackStatic);
214 callbacks.setOnBeginHeadersCallback(onBeginHeadersCallbackStatic);
Ed Tanous325310d2024-03-15 09:05:04 -0700215 callbacks.setOnDataChunkRecvCallback(onDataChunkRecvStatic);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800216
217 nghttp2_session session(callbacks);
218 session.setUserData(this);
219
220 return session;
221 }
222
223 int onRequestRecv(int32_t streamId)
224 {
Ed Tanous62598e32023-07-17 17:06:25 -0700225 BMCWEB_LOG_DEBUG("on_request_recv");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800226
227 auto it = streams.find(streamId);
228 if (it == streams.end())
229 {
230 close();
231 return -1;
232 }
Ed Tanous325310d2024-03-15 09:05:04 -0700233 if (it->second.reqReader)
234 {
235 boost::beast::error_code ec;
236 it->second.reqReader->finish(ec);
237 if (ec)
238 {
239 BMCWEB_LOG_CRITICAL("Failed to finalize payload");
240 close();
241 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
242 }
243 }
Ed Tanousf42e8592023-08-25 10:47:44 -0700244 crow::Request& thisReq = it->second.req;
Ed Tanous62598e32023-07-17 17:06:25 -0700245 BMCWEB_LOG_DEBUG("Handling {} \"{}\"", logPtr(&thisReq),
246 thisReq.url().encoded_path());
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800247
Ed Tanousf42e8592023-08-25 10:47:44 -0700248 crow::Response& thisRes = it->second.res;
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800249
250 thisRes.setCompleteRequestHandler(
251 [this, streamId](Response& completeRes) {
Ed Tanous62598e32023-07-17 17:06:25 -0700252 BMCWEB_LOG_DEBUG("res.completeRequestHandler called");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800253 if (sendResponse(completeRes, streamId) != 0)
254 {
255 close();
256 return;
257 }
258 });
259 auto asyncResp =
Ed Tanousf42e8592023-08-25 10:47:44 -0700260 std::make_shared<bmcweb::AsyncResp>(std::move(it->second.res));
Ed Tanous325310d2024-03-15 09:05:04 -0700261#ifndef BMCWEB_INSECURE_DISABLE_AUTHX
262 thisReq.session = crow::authentication::authenticate(
263 {}, thisRes, thisReq.method(), thisReq.req, nullptr);
264 if (!crow::authentication::isOnAllowlist(thisReq.url().path(),
265 thisReq.method()) &&
266 thisReq.session == nullptr)
267 {
268 BMCWEB_LOG_WARNING("Authentication failed");
269 forward_unauthorized::sendUnauthorized(
270 thisReq.url().encoded_path(),
271 thisReq.getHeaderValue("X-Requested-With"),
272 thisReq.getHeaderValue("Accept"), thisRes);
273 }
274 else
275#endif // BMCWEB_INSECURE_DISABLE_AUTHX
276 {
277 handler->handle(thisReq, asyncResp);
278 }
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800279 return 0;
280 }
281
Ed Tanous325310d2024-03-15 09:05:04 -0700282 int onDataChunkRecvCallback(uint8_t /*flags*/, int32_t streamId,
283 const uint8_t* data, size_t len)
284 {
285 auto thisStream = streams.find(streamId);
286 if (thisStream == streams.end())
287 {
288 BMCWEB_LOG_ERROR("Unknown stream{}", streamId);
289 close();
290 return -1;
291 }
292 if (!thisStream->second.reqReader)
293 {
294 thisStream->second.reqReader.emplace(
295 thisStream->second.req.req.base(),
296 thisStream->second.req.req.body());
297 }
298 boost::beast::error_code ec;
299 thisStream->second.reqReader->put(boost::asio::const_buffer(data, len),
300 ec);
301 if (ec)
302 {
303 BMCWEB_LOG_CRITICAL("Failed to write payload");
304 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
305 }
306 return 0;
307 }
308
309 static int onDataChunkRecvStatic(nghttp2_session* /* session */,
310 uint8_t flags, int32_t streamId,
311 const uint8_t* data, size_t len,
312 void* userData)
313 {
314 BMCWEB_LOG_DEBUG("on_frame_recv_callback");
315 if (userData == nullptr)
316 {
317 BMCWEB_LOG_CRITICAL("user data was null?");
318 return NGHTTP2_ERR_CALLBACK_FAILURE;
319 }
320 return userPtrToSelf(userData).onDataChunkRecvCallback(flags, streamId,
321 data, len);
322 }
323
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800324 int onFrameRecvCallback(const nghttp2_frame& frame)
325 {
Ed Tanous62598e32023-07-17 17:06:25 -0700326 BMCWEB_LOG_DEBUG("frame type {}", static_cast<int>(frame.hd.type));
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800327 switch (frame.hd.type)
328 {
329 case NGHTTP2_DATA:
330 case NGHTTP2_HEADERS:
331 // Check that the client request has finished
332 if ((frame.hd.flags & NGHTTP2_FLAG_END_STREAM) != 0)
333 {
334 return onRequestRecv(frame.hd.stream_id);
335 }
336 break;
337 default:
338 break;
339 }
340 return 0;
341 }
342
343 static int onFrameRecvCallbackStatic(nghttp2_session* /* session */,
344 const nghttp2_frame* frame,
345 void* userData)
346 {
Ed Tanous62598e32023-07-17 17:06:25 -0700347 BMCWEB_LOG_DEBUG("on_frame_recv_callback");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800348 if (userData == nullptr)
349 {
Ed Tanous62598e32023-07-17 17:06:25 -0700350 BMCWEB_LOG_CRITICAL("user data was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800351 return NGHTTP2_ERR_CALLBACK_FAILURE;
352 }
353 if (frame == nullptr)
354 {
Ed Tanous62598e32023-07-17 17:06:25 -0700355 BMCWEB_LOG_CRITICAL("frame was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800356 return NGHTTP2_ERR_CALLBACK_FAILURE;
357 }
358 return userPtrToSelf(userData).onFrameRecvCallback(*frame);
359 }
360
361 static self_type& userPtrToSelf(void* userData)
362 {
363 // This method exists to keep the unsafe reinterpret cast in one
364 // place.
365 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
366 return *reinterpret_cast<self_type*>(userData);
367 }
368
369 static int onStreamCloseCallbackStatic(nghttp2_session* /* session */,
370 int32_t streamId,
371 uint32_t /*unused*/, void* userData)
372 {
Ed Tanous62598e32023-07-17 17:06:25 -0700373 BMCWEB_LOG_DEBUG("on_stream_close_callback stream {}", streamId);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800374 if (userData == nullptr)
375 {
Ed Tanous62598e32023-07-17 17:06:25 -0700376 BMCWEB_LOG_CRITICAL("user data was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800377 return NGHTTP2_ERR_CALLBACK_FAILURE;
378 }
Ed Tanousf42e8592023-08-25 10:47:44 -0700379 if (userPtrToSelf(userData).streams.erase(streamId) <= 0)
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800380 {
381 return -1;
382 }
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800383 return 0;
384 }
385
386 int onHeaderCallback(const nghttp2_frame& frame,
387 std::span<const uint8_t> name,
388 std::span<const uint8_t> value)
389 {
390 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
391 std::string_view nameSv(reinterpret_cast<const char*>(name.data()),
392 name.size());
393 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
394 std::string_view valueSv(reinterpret_cast<const char*>(value.data()),
395 value.size());
396
Ed Tanous62598e32023-07-17 17:06:25 -0700397 BMCWEB_LOG_DEBUG("on_header_callback name: {} value {}", nameSv,
398 valueSv);
Ed Tanousa07e9812024-03-19 10:31:13 -0700399 if (frame.hd.type != NGHTTP2_HEADERS)
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800400 {
Ed Tanousa07e9812024-03-19 10:31:13 -0700401 return 0;
402 }
403 if (frame.headers.cat != NGHTTP2_HCAT_REQUEST)
404 {
405 return 0;
406 }
407 auto thisStream = streams.find(frame.hd.stream_id);
408 if (thisStream == streams.end())
409 {
410 BMCWEB_LOG_ERROR("Unknown stream{}", frame.hd.stream_id);
411 close();
412 return -1;
413 }
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800414
Ed Tanousa07e9812024-03-19 10:31:13 -0700415 crow::Request& thisReq = thisStream->second.req;
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800416
Ed Tanousa07e9812024-03-19 10:31:13 -0700417 if (nameSv == ":path")
418 {
419 thisReq.target(valueSv);
420 }
421 else if (nameSv == ":method")
422 {
423 boost::beast::http::verb verb =
424 boost::beast::http::string_to_verb(valueSv);
425 if (verb == boost::beast::http::verb::unknown)
426 {
427 BMCWEB_LOG_ERROR("Unknown http verb {}", valueSv);
428 close();
429 return -1;
430 }
431 thisReq.req.method(verb);
432 }
433 else if (nameSv == ":scheme")
434 {
435 // Nothing to check on scheme
436 }
437 else
438 {
439 thisReq.req.set(nameSv, valueSv);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800440 }
441 return 0;
442 }
443
444 static int onHeaderCallbackStatic(nghttp2_session* /* session */,
445 const nghttp2_frame* frame,
446 const uint8_t* name, size_t namelen,
447 const uint8_t* value, size_t vallen,
448 uint8_t /* flags */, void* userData)
449 {
450 if (userData == nullptr)
451 {
Ed Tanous62598e32023-07-17 17:06:25 -0700452 BMCWEB_LOG_CRITICAL("user data was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800453 return NGHTTP2_ERR_CALLBACK_FAILURE;
454 }
455 if (frame == nullptr)
456 {
Ed Tanous62598e32023-07-17 17:06:25 -0700457 BMCWEB_LOG_CRITICAL("frame was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800458 return NGHTTP2_ERR_CALLBACK_FAILURE;
459 }
460 if (name == nullptr)
461 {
Ed Tanous62598e32023-07-17 17:06:25 -0700462 BMCWEB_LOG_CRITICAL("name was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800463 return NGHTTP2_ERR_CALLBACK_FAILURE;
464 }
465 if (value == nullptr)
466 {
Ed Tanous62598e32023-07-17 17:06:25 -0700467 BMCWEB_LOG_CRITICAL("value was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800468 return NGHTTP2_ERR_CALLBACK_FAILURE;
469 }
470 return userPtrToSelf(userData).onHeaderCallback(*frame, {name, namelen},
471 {value, vallen});
472 }
473
474 int onBeginHeadersCallback(const nghttp2_frame& frame)
475 {
476 if (frame.hd.type == NGHTTP2_HEADERS &&
477 frame.headers.cat == NGHTTP2_HCAT_REQUEST)
478 {
Ed Tanous62598e32023-07-17 17:06:25 -0700479 BMCWEB_LOG_DEBUG("create stream for id {}", frame.hd.stream_id);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800480
Ed Tanousf42e8592023-08-25 10:47:44 -0700481 Http2StreamData& stream = streams[frame.hd.stream_id];
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800482 // http2 is by definition always tls
Ed Tanousf42e8592023-08-25 10:47:44 -0700483 stream.req.isSecure = true;
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800484 }
485 return 0;
486 }
487
488 static int onBeginHeadersCallbackStatic(nghttp2_session* /* session */,
489 const nghttp2_frame* frame,
490 void* userData)
491 {
Ed Tanous62598e32023-07-17 17:06:25 -0700492 BMCWEB_LOG_DEBUG("on_begin_headers_callback");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800493 if (userData == nullptr)
494 {
Ed Tanous62598e32023-07-17 17:06:25 -0700495 BMCWEB_LOG_CRITICAL("user data was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800496 return NGHTTP2_ERR_CALLBACK_FAILURE;
497 }
498 if (frame == nullptr)
499 {
Ed Tanous62598e32023-07-17 17:06:25 -0700500 BMCWEB_LOG_CRITICAL("frame was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800501 return NGHTTP2_ERR_CALLBACK_FAILURE;
502 }
503 return userPtrToSelf(userData).onBeginHeadersCallback(*frame);
504 }
505
506 static void afterWriteBuffer(const std::shared_ptr<self_type>& self,
507 const boost::system::error_code& ec,
508 size_t sendLength)
509 {
510 self->isWriting = false;
Ed Tanous62598e32023-07-17 17:06:25 -0700511 BMCWEB_LOG_DEBUG("Sent {}", sendLength);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800512 if (ec)
513 {
514 self->close();
515 return;
516 }
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800517 self->writeBuffer();
518 }
519
520 void writeBuffer()
521 {
522 if (isWriting)
523 {
524 return;
525 }
Ed Tanousd0882182024-01-26 23:45:25 -0800526 std::span<const uint8_t> data = ngSession.memSend();
527 if (data.empty())
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800528 {
529 return;
530 }
531 isWriting = true;
Ed Tanous325310d2024-03-15 09:05:04 -0700532 boost::asio::async_write(
533 adaptor, boost::asio::const_buffer(data.data(), data.size()),
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800534 std::bind_front(afterWriteBuffer, shared_from_this()));
535 }
536
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800537 void close()
538 {
539 if constexpr (std::is_same_v<Adaptor,
540 boost::beast::ssl_stream<
541 boost::asio::ip::tcp::socket>>)
542 {
543 adaptor.next_layer().close();
544 }
545 else
546 {
547 adaptor.close();
548 }
549 }
550
Ed Tanousd0882182024-01-26 23:45:25 -0800551 void afterDoRead(const std::shared_ptr<self_type>& /*self*/,
552 const boost::system::error_code& ec,
553 size_t bytesTransferred)
554 {
555 BMCWEB_LOG_DEBUG("{} async_read_some {} Bytes", logPtr(this),
556 bytesTransferred);
557
558 if (ec)
559 {
560 BMCWEB_LOG_ERROR("{} Error while reading: {}", logPtr(this),
561 ec.message());
562 close();
563 BMCWEB_LOG_DEBUG("{} from read(1)", logPtr(this));
564 return;
565 }
566 std::span<uint8_t> bufferSpan{inBuffer.data(), bytesTransferred};
567
568 ssize_t readLen = ngSession.memRecv(bufferSpan);
569 if (readLen < 0)
570 {
571 BMCWEB_LOG_ERROR("nghttp2_session_mem_recv returned {}", readLen);
572 close();
573 return;
574 }
575 writeBuffer();
576
577 doRead();
578 }
579
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800580 void doRead()
581 {
Ed Tanous62598e32023-07-17 17:06:25 -0700582 BMCWEB_LOG_DEBUG("{} doRead", logPtr(this));
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800583 adaptor.async_read_some(
Ed Tanousd0882182024-01-26 23:45:25 -0800584 boost::asio::buffer(inBuffer),
585 std::bind_front(&self_type::afterDoRead, this, shared_from_this()));
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800586 }
587
588 // A mapping from http2 stream ID to Stream Data
Ed Tanous52e31622024-01-23 16:31:11 -0800589 std::map<int32_t, Http2StreamData> streams;
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800590
Ed Tanousd0882182024-01-26 23:45:25 -0800591 std::array<uint8_t, 8192> inBuffer{};
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800592
593 Adaptor adaptor;
594 bool isWriting = false;
595
596 nghttp2_session ngSession;
597
598 Handler* handler;
599 std::function<std::string()>& getCachedDateStr;
600
601 using std::enable_shared_from_this<
602 HTTP2Connection<Adaptor, Handler>>::shared_from_this;
603
604 using std::enable_shared_from_this<
605 HTTP2Connection<Adaptor, Handler>>::weak_from_this;
606};
607} // namespace crow