blob: 3e0c3943d5b6c84248bbb83e464a8fcb0297b0c6 [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 Tanous47f29342024-03-19 12:18:06 -070041 Request req;
Ed Tanous325310d2024-03-15 09:05:04 -070042 std::optional<bmcweb::HttpBody::reader> reqReader;
Ed Tanous47f29342024-03-19 12:18:06 -070043 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 Tanousd547d8d2024-03-16 18:04:41 -0700107 if (!stream.writer)
108 {
109 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
110 }
Ed Tanous52e31622024-01-23 16:31:11 -0800111 boost::beast::error_code ec;
112 boost::optional<std::pair<boost::asio::const_buffer, bool>> out =
113 stream.writer->getWithMaxSize(ec, length);
114 if (ec)
Ed Tanous27b0cf92023-08-07 12:02:40 -0700115 {
Ed Tanous325310d2024-03-15 09:05:04 -0700116 BMCWEB_LOG_CRITICAL("Failed to get buffer");
Ed Tanous27b0cf92023-08-07 12:02:40 -0700117 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
118 }
Ed Tanous52e31622024-01-23 16:31:11 -0800119 if (!out)
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800120 {
Ed Tanous325310d2024-03-15 09:05:04 -0700121 BMCWEB_LOG_ERROR("Empty file, setting EOF");
Ed Tanous52e31622024-01-23 16:31:11 -0800122 *dataFlags |= NGHTTP2_DATA_FLAG_EOF;
123 return 0;
124 }
125
126 BMCWEB_LOG_DEBUG("Send chunk of size: {}", out->first.size());
127 if (length < out->first.size())
128 {
Ed Tanous325310d2024-03-15 09:05:04 -0700129 BMCWEB_LOG_CRITICAL(
130 "Buffer overflow that should never happen happened");
Ed Tanous52e31622024-01-23 16:31:11 -0800131 // Should never happen because of length limit on get() above
132 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
133 }
Ed Tanous325310d2024-03-15 09:05:04 -0700134 boost::asio::mutable_buffer writeableBuf(buf, length);
Ed Tanous52e31622024-01-23 16:31:11 -0800135 BMCWEB_LOG_DEBUG("Copying {} bytes to buf", out->first.size());
Ed Tanous325310d2024-03-15 09:05:04 -0700136 size_t copied = boost::asio::buffer_copy(writeableBuf, out->first);
137 if (copied != out->first.size())
138 {
139 BMCWEB_LOG_ERROR(
140 "Couldn't copy all {} bytes into buffer, only copied {}",
141 out->first.size(), copied);
142 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
143 }
Ed Tanous52e31622024-01-23 16:31:11 -0800144
145 if (!out->second)
146 {
Ed Tanous325310d2024-03-15 09:05:04 -0700147 BMCWEB_LOG_DEBUG("Setting EOF flag");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800148 *dataFlags |= NGHTTP2_DATA_FLAG_EOF;
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800149 }
Ed Tanous325310d2024-03-15 09:05:04 -0700150 return static_cast<ssize_t>(copied);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800151 }
152
153 nghttp2_nv headerFromStringViews(std::string_view name,
Ed Tanous52e31622024-01-23 16:31:11 -0800154 std::string_view value, uint8_t flags)
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800155 {
156 uint8_t* nameData = std::bit_cast<uint8_t*>(name.data());
157 uint8_t* valueData = std::bit_cast<uint8_t*>(value.data());
Ed Tanous52e31622024-01-23 16:31:11 -0800158 return {nameData, valueData, name.size(), value.size(), flags};
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800159 }
160
161 int sendResponse(Response& completedRes, int32_t streamId)
162 {
Ed Tanous62598e32023-07-17 17:06:25 -0700163 BMCWEB_LOG_DEBUG("send_response stream_id:{}", streamId);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800164
165 auto it = streams.find(streamId);
166 if (it == streams.end())
167 {
168 close();
169 return -1;
170 }
Ed Tanousf42e8592023-08-25 10:47:44 -0700171 Response& thisRes = it->second.res;
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800172 thisRes = std::move(completedRes);
Ed Tanousf42e8592023-08-25 10:47:44 -0700173 crow::Request& thisReq = it->second.req;
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800174 std::vector<nghttp2_nv> hdr;
175
176 completeResponseFields(thisReq, thisRes);
177 thisRes.addHeader(boost::beast::http::field::date, getCachedDateStr());
Ed Tanous325310d2024-03-15 09:05:04 -0700178 thisRes.preparePayload();
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800179
Ed Tanous27b0cf92023-08-07 12:02:40 -0700180 boost::beast::http::fields& fields = thisRes.fields();
181 std::string code = std::to_string(thisRes.resultInt());
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 Tanousf42e8592023-08-25 10:47:44 -0700189 Http2StreamData& stream = it->second;
Ed Tanous52e31622024-01-23 16:31:11 -0800190 crow::Response& res = stream.res;
Ed Tanousb2896142024-01-31 15:25:47 -0800191 http::response<bmcweb::HttpBody>& fbody = res.response;
Ed Tanous52e31622024-01-23 16:31:11 -0800192 stream.writer.emplace(fbody.base(), fbody.body());
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800193
194 nghttp2_data_provider dataPrd{
Ed Tanousf42e8592023-08-25 10:47:44 -0700195 .source = {.fd = 0},
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800196 .read_callback = fileReadCallback,
197 };
198
199 int rv = ngSession.submitResponse(streamId, hdr, &dataPrd);
200 if (rv != 0)
201 {
Ed Tanous62598e32023-07-17 17:06:25 -0700202 BMCWEB_LOG_ERROR("Fatal error: {}", nghttp2_strerror(rv));
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800203 close();
204 return -1;
205 }
Ed Tanousd0882182024-01-26 23:45:25 -0800206 writeBuffer();
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800207
208 return 0;
209 }
210
211 nghttp2_session initializeNghttp2Session()
212 {
213 nghttp2_session_callbacks callbacks;
214 callbacks.setOnFrameRecvCallback(onFrameRecvCallbackStatic);
215 callbacks.setOnStreamCloseCallback(onStreamCloseCallbackStatic);
216 callbacks.setOnHeaderCallback(onHeaderCallbackStatic);
217 callbacks.setOnBeginHeadersCallback(onBeginHeadersCallbackStatic);
Ed Tanous325310d2024-03-15 09:05:04 -0700218 callbacks.setOnDataChunkRecvCallback(onDataChunkRecvStatic);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800219
220 nghttp2_session session(callbacks);
221 session.setUserData(this);
222
223 return session;
224 }
225
226 int onRequestRecv(int32_t streamId)
227 {
Ed Tanous62598e32023-07-17 17:06:25 -0700228 BMCWEB_LOG_DEBUG("on_request_recv");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800229
230 auto it = streams.find(streamId);
231 if (it == streams.end())
232 {
233 close();
234 return -1;
235 }
Ed Tanousd547d8d2024-03-16 18:04:41 -0700236 auto& reqReader = it->second.reqReader;
237 if (reqReader)
Ed Tanous325310d2024-03-15 09:05:04 -0700238 {
239 boost::beast::error_code ec;
Ed Tanousd547d8d2024-03-16 18:04:41 -0700240 reqReader->finish(ec);
Ed Tanous325310d2024-03-15 09:05:04 -0700241 if (ec)
242 {
243 BMCWEB_LOG_CRITICAL("Failed to finalize payload");
244 close();
245 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
246 }
247 }
Ed Tanousf42e8592023-08-25 10:47:44 -0700248 crow::Request& thisReq = it->second.req;
Ed Tanous8e8245d2024-04-11 22:21:38 -0700249 thisReq.ioService = static_cast<decltype(thisReq.ioService)>(
250 &adaptor.get_executor().context());
Ed Tanous62598e32023-07-17 17:06:25 -0700251 BMCWEB_LOG_DEBUG("Handling {} \"{}\"", logPtr(&thisReq),
252 thisReq.url().encoded_path());
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800253
Ed Tanousf42e8592023-08-25 10:47:44 -0700254 crow::Response& thisRes = it->second.res;
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800255
256 thisRes.setCompleteRequestHandler(
257 [this, streamId](Response& completeRes) {
Ed Tanous62598e32023-07-17 17:06:25 -0700258 BMCWEB_LOG_DEBUG("res.completeRequestHandler called");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800259 if (sendResponse(completeRes, streamId) != 0)
260 {
261 close();
262 return;
263 }
264 });
265 auto asyncResp =
Ed Tanousf42e8592023-08-25 10:47:44 -0700266 std::make_shared<bmcweb::AsyncResp>(std::move(it->second.res));
Ed Tanous325310d2024-03-15 09:05:04 -0700267#ifndef BMCWEB_INSECURE_DISABLE_AUTHX
268 thisReq.session = crow::authentication::authenticate(
269 {}, thisRes, thisReq.method(), thisReq.req, nullptr);
270 if (!crow::authentication::isOnAllowlist(thisReq.url().path(),
271 thisReq.method()) &&
272 thisReq.session == nullptr)
273 {
274 BMCWEB_LOG_WARNING("Authentication failed");
275 forward_unauthorized::sendUnauthorized(
276 thisReq.url().encoded_path(),
277 thisReq.getHeaderValue("X-Requested-With"),
278 thisReq.getHeaderValue("Accept"), thisRes);
279 }
280 else
281#endif // BMCWEB_INSECURE_DISABLE_AUTHX
282 {
283 handler->handle(thisReq, asyncResp);
284 }
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800285 return 0;
286 }
287
Ed Tanous325310d2024-03-15 09:05:04 -0700288 int onDataChunkRecvCallback(uint8_t /*flags*/, int32_t streamId,
289 const uint8_t* data, size_t len)
290 {
291 auto thisStream = streams.find(streamId);
292 if (thisStream == streams.end())
293 {
294 BMCWEB_LOG_ERROR("Unknown stream{}", streamId);
295 close();
296 return -1;
297 }
Ed Tanousd547d8d2024-03-16 18:04:41 -0700298
299 std::optional<bmcweb::HttpBody::reader>& reqReader =
300 thisStream->second.reqReader;
301 if (!reqReader)
Ed Tanous325310d2024-03-15 09:05:04 -0700302 {
Ed Tanous8e5cc7b2024-04-02 11:00:54 -0700303 reqReader.emplace(
304 bmcweb::HttpBody::reader(thisStream->second.req.req.base(),
305 thisStream->second.req.req.body()));
Ed Tanous325310d2024-03-15 09:05:04 -0700306 }
307 boost::beast::error_code ec;
Ed Tanousd547d8d2024-03-16 18:04:41 -0700308 reqReader->put(boost::asio::const_buffer(data, len), ec);
Ed Tanous325310d2024-03-15 09:05:04 -0700309 if (ec)
310 {
311 BMCWEB_LOG_CRITICAL("Failed to write payload");
312 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
313 }
314 return 0;
315 }
316
317 static int onDataChunkRecvStatic(nghttp2_session* /* session */,
318 uint8_t flags, int32_t streamId,
319 const uint8_t* data, size_t len,
320 void* userData)
321 {
322 BMCWEB_LOG_DEBUG("on_frame_recv_callback");
323 if (userData == nullptr)
324 {
325 BMCWEB_LOG_CRITICAL("user data was null?");
326 return NGHTTP2_ERR_CALLBACK_FAILURE;
327 }
328 return userPtrToSelf(userData).onDataChunkRecvCallback(flags, streamId,
329 data, len);
330 }
331
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800332 int onFrameRecvCallback(const nghttp2_frame& frame)
333 {
Ed Tanous62598e32023-07-17 17:06:25 -0700334 BMCWEB_LOG_DEBUG("frame type {}", static_cast<int>(frame.hd.type));
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800335 switch (frame.hd.type)
336 {
337 case NGHTTP2_DATA:
338 case NGHTTP2_HEADERS:
339 // Check that the client request has finished
340 if ((frame.hd.flags & NGHTTP2_FLAG_END_STREAM) != 0)
341 {
342 return onRequestRecv(frame.hd.stream_id);
343 }
344 break;
345 default:
346 break;
347 }
348 return 0;
349 }
350
351 static int onFrameRecvCallbackStatic(nghttp2_session* /* session */,
352 const nghttp2_frame* frame,
353 void* userData)
354 {
Ed Tanous62598e32023-07-17 17:06:25 -0700355 BMCWEB_LOG_DEBUG("on_frame_recv_callback");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800356 if (userData == nullptr)
357 {
Ed Tanous62598e32023-07-17 17:06:25 -0700358 BMCWEB_LOG_CRITICAL("user data was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800359 return NGHTTP2_ERR_CALLBACK_FAILURE;
360 }
361 if (frame == nullptr)
362 {
Ed Tanous62598e32023-07-17 17:06:25 -0700363 BMCWEB_LOG_CRITICAL("frame was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800364 return NGHTTP2_ERR_CALLBACK_FAILURE;
365 }
366 return userPtrToSelf(userData).onFrameRecvCallback(*frame);
367 }
368
369 static self_type& userPtrToSelf(void* userData)
370 {
371 // This method exists to keep the unsafe reinterpret cast in one
372 // place.
373 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
374 return *reinterpret_cast<self_type*>(userData);
375 }
376
377 static int onStreamCloseCallbackStatic(nghttp2_session* /* session */,
378 int32_t streamId,
379 uint32_t /*unused*/, void* userData)
380 {
Ed Tanous62598e32023-07-17 17:06:25 -0700381 BMCWEB_LOG_DEBUG("on_stream_close_callback stream {}", streamId);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800382 if (userData == nullptr)
383 {
Ed Tanous62598e32023-07-17 17:06:25 -0700384 BMCWEB_LOG_CRITICAL("user data was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800385 return NGHTTP2_ERR_CALLBACK_FAILURE;
386 }
Ed Tanousf42e8592023-08-25 10:47:44 -0700387 if (userPtrToSelf(userData).streams.erase(streamId) <= 0)
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800388 {
389 return -1;
390 }
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800391 return 0;
392 }
393
394 int onHeaderCallback(const nghttp2_frame& frame,
395 std::span<const uint8_t> name,
396 std::span<const uint8_t> value)
397 {
398 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
399 std::string_view nameSv(reinterpret_cast<const char*>(name.data()),
400 name.size());
401 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
402 std::string_view valueSv(reinterpret_cast<const char*>(value.data()),
403 value.size());
404
Ed Tanous62598e32023-07-17 17:06:25 -0700405 BMCWEB_LOG_DEBUG("on_header_callback name: {} value {}", nameSv,
406 valueSv);
Ed Tanousa07e9812024-03-19 10:31:13 -0700407 if (frame.hd.type != NGHTTP2_HEADERS)
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800408 {
Ed Tanousa07e9812024-03-19 10:31:13 -0700409 return 0;
410 }
411 if (frame.headers.cat != NGHTTP2_HCAT_REQUEST)
412 {
413 return 0;
414 }
415 auto thisStream = streams.find(frame.hd.stream_id);
416 if (thisStream == streams.end())
417 {
418 BMCWEB_LOG_ERROR("Unknown stream{}", frame.hd.stream_id);
419 close();
420 return -1;
421 }
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800422
Ed Tanousa07e9812024-03-19 10:31:13 -0700423 crow::Request& thisReq = thisStream->second.req;
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800424
Ed Tanousa07e9812024-03-19 10:31:13 -0700425 if (nameSv == ":path")
426 {
427 thisReq.target(valueSv);
428 }
429 else if (nameSv == ":method")
430 {
431 boost::beast::http::verb verb =
432 boost::beast::http::string_to_verb(valueSv);
433 if (verb == boost::beast::http::verb::unknown)
434 {
435 BMCWEB_LOG_ERROR("Unknown http verb {}", valueSv);
436 close();
437 return -1;
438 }
Myung Bae1873a042024-04-01 09:27:39 -0500439 thisReq.method(verb);
Ed Tanousa07e9812024-03-19 10:31:13 -0700440 }
441 else if (nameSv == ":scheme")
442 {
443 // Nothing to check on scheme
444 }
445 else
446 {
Myung Bae1873a042024-04-01 09:27:39 -0500447 thisReq.addHeader(nameSv, valueSv);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800448 }
449 return 0;
450 }
451
452 static int onHeaderCallbackStatic(nghttp2_session* /* session */,
453 const nghttp2_frame* frame,
454 const uint8_t* name, size_t namelen,
455 const uint8_t* value, size_t vallen,
456 uint8_t /* flags */, void* userData)
457 {
458 if (userData == nullptr)
459 {
Ed Tanous62598e32023-07-17 17:06:25 -0700460 BMCWEB_LOG_CRITICAL("user data was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800461 return NGHTTP2_ERR_CALLBACK_FAILURE;
462 }
463 if (frame == nullptr)
464 {
Ed Tanous62598e32023-07-17 17:06:25 -0700465 BMCWEB_LOG_CRITICAL("frame was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800466 return NGHTTP2_ERR_CALLBACK_FAILURE;
467 }
468 if (name == nullptr)
469 {
Ed Tanous62598e32023-07-17 17:06:25 -0700470 BMCWEB_LOG_CRITICAL("name was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800471 return NGHTTP2_ERR_CALLBACK_FAILURE;
472 }
473 if (value == nullptr)
474 {
Ed Tanous62598e32023-07-17 17:06:25 -0700475 BMCWEB_LOG_CRITICAL("value was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800476 return NGHTTP2_ERR_CALLBACK_FAILURE;
477 }
478 return userPtrToSelf(userData).onHeaderCallback(*frame, {name, namelen},
479 {value, vallen});
480 }
481
482 int onBeginHeadersCallback(const nghttp2_frame& frame)
483 {
484 if (frame.hd.type == NGHTTP2_HEADERS &&
485 frame.headers.cat == NGHTTP2_HCAT_REQUEST)
486 {
Ed Tanous62598e32023-07-17 17:06:25 -0700487 BMCWEB_LOG_DEBUG("create stream for id {}", frame.hd.stream_id);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800488
Ed Tanousf42e8592023-08-25 10:47:44 -0700489 Http2StreamData& stream = streams[frame.hd.stream_id];
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800490 // http2 is by definition always tls
Ed Tanousf42e8592023-08-25 10:47:44 -0700491 stream.req.isSecure = true;
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800492 }
493 return 0;
494 }
495
496 static int onBeginHeadersCallbackStatic(nghttp2_session* /* session */,
497 const nghttp2_frame* frame,
498 void* userData)
499 {
Ed Tanous62598e32023-07-17 17:06:25 -0700500 BMCWEB_LOG_DEBUG("on_begin_headers_callback");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800501 if (userData == nullptr)
502 {
Ed Tanous62598e32023-07-17 17:06:25 -0700503 BMCWEB_LOG_CRITICAL("user data was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800504 return NGHTTP2_ERR_CALLBACK_FAILURE;
505 }
506 if (frame == nullptr)
507 {
Ed Tanous62598e32023-07-17 17:06:25 -0700508 BMCWEB_LOG_CRITICAL("frame was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800509 return NGHTTP2_ERR_CALLBACK_FAILURE;
510 }
511 return userPtrToSelf(userData).onBeginHeadersCallback(*frame);
512 }
513
514 static void afterWriteBuffer(const std::shared_ptr<self_type>& self,
515 const boost::system::error_code& ec,
516 size_t sendLength)
517 {
518 self->isWriting = false;
Ed Tanous62598e32023-07-17 17:06:25 -0700519 BMCWEB_LOG_DEBUG("Sent {}", sendLength);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800520 if (ec)
521 {
522 self->close();
523 return;
524 }
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800525 self->writeBuffer();
526 }
527
528 void writeBuffer()
529 {
530 if (isWriting)
531 {
532 return;
533 }
Ed Tanousd0882182024-01-26 23:45:25 -0800534 std::span<const uint8_t> data = ngSession.memSend();
535 if (data.empty())
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800536 {
537 return;
538 }
539 isWriting = true;
Ed Tanous325310d2024-03-15 09:05:04 -0700540 boost::asio::async_write(
541 adaptor, boost::asio::const_buffer(data.data(), data.size()),
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800542 std::bind_front(afterWriteBuffer, shared_from_this()));
543 }
544
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800545 void close()
546 {
547 if constexpr (std::is_same_v<Adaptor,
548 boost::beast::ssl_stream<
549 boost::asio::ip::tcp::socket>>)
550 {
551 adaptor.next_layer().close();
552 }
553 else
554 {
555 adaptor.close();
556 }
557 }
558
Ed Tanousd0882182024-01-26 23:45:25 -0800559 void afterDoRead(const std::shared_ptr<self_type>& /*self*/,
560 const boost::system::error_code& ec,
561 size_t bytesTransferred)
562 {
563 BMCWEB_LOG_DEBUG("{} async_read_some {} Bytes", logPtr(this),
564 bytesTransferred);
565
566 if (ec)
567 {
568 BMCWEB_LOG_ERROR("{} Error while reading: {}", logPtr(this),
569 ec.message());
570 close();
571 BMCWEB_LOG_DEBUG("{} from read(1)", logPtr(this));
572 return;
573 }
574 std::span<uint8_t> bufferSpan{inBuffer.data(), bytesTransferred};
575
576 ssize_t readLen = ngSession.memRecv(bufferSpan);
577 if (readLen < 0)
578 {
579 BMCWEB_LOG_ERROR("nghttp2_session_mem_recv returned {}", readLen);
580 close();
581 return;
582 }
583 writeBuffer();
584
585 doRead();
586 }
587
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800588 void doRead()
589 {
Ed Tanous62598e32023-07-17 17:06:25 -0700590 BMCWEB_LOG_DEBUG("{} doRead", logPtr(this));
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800591 adaptor.async_read_some(
Ed Tanousd0882182024-01-26 23:45:25 -0800592 boost::asio::buffer(inBuffer),
593 std::bind_front(&self_type::afterDoRead, this, shared_from_this()));
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800594 }
595
596 // A mapping from http2 stream ID to Stream Data
Ed Tanous52e31622024-01-23 16:31:11 -0800597 std::map<int32_t, Http2StreamData> streams;
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800598
Ed Tanousd0882182024-01-26 23:45:25 -0800599 std::array<uint8_t, 8192> inBuffer{};
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800600
601 Adaptor adaptor;
602 bool isWriting = false;
603
604 nghttp2_session ngSession;
605
606 Handler* handler;
607 std::function<std::string()>& getCachedDateStr;
608
609 using std::enable_shared_from_this<
610 HTTP2Connection<Adaptor, Handler>>::shared_from_this;
611
612 using std::enable_shared_from_this<
613 HTTP2Connection<Adaptor, Handler>>::weak_from_this;
614};
615} // namespace crow