blob: 7e8224eedf5563da01bcb63ab215afb848e42c8a [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{
Ed Tanous47f29342024-03-19 12:18:06 -070040 Request req;
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 Tanousf42e8592023-08-25 10:47:44 -0700170 Response& thisRes = it->second.res;
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800171 thisRes = std::move(completedRes);
Ed Tanousf42e8592023-08-25 10:47:44 -0700172 crow::Request& thisReq = it->second.req;
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800173 std::vector<nghttp2_nv> hdr;
174
175 completeResponseFields(thisReq, thisRes);
176 thisRes.addHeader(boost::beast::http::field::date, getCachedDateStr());
Ed Tanous325310d2024-03-15 09:05:04 -0700177 thisRes.preparePayload();
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800178
Ed Tanous27b0cf92023-08-07 12:02:40 -0700179 boost::beast::http::fields& fields = thisRes.fields();
180 std::string code = std::to_string(thisRes.resultInt());
Ed Tanous52e31622024-01-23 16:31:11 -0800181 hdr.emplace_back(
182 headerFromStringViews(":status", code, NGHTTP2_NV_FLAG_NONE));
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800183 for (const boost::beast::http::fields::value_type& header : fields)
184 {
Ed Tanous52e31622024-01-23 16:31:11 -0800185 hdr.emplace_back(headerFromStringViews(
Ed Tanousd0882182024-01-26 23:45:25 -0800186 header.name_string(), header.value(), NGHTTP2_NV_FLAG_NONE));
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800187 }
Ed Tanousf42e8592023-08-25 10:47:44 -0700188 Http2StreamData& stream = it->second;
Ed Tanous52e31622024-01-23 16:31:11 -0800189 crow::Response& res = stream.res;
Ed Tanousb2896142024-01-31 15:25:47 -0800190 http::response<bmcweb::HttpBody>& fbody = res.response;
Ed Tanous52e31622024-01-23 16:31:11 -0800191 stream.writer.emplace(fbody.base(), fbody.body());
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800192
193 nghttp2_data_provider dataPrd{
Ed Tanousf42e8592023-08-25 10:47:44 -0700194 .source = {.fd = 0},
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800195 .read_callback = fileReadCallback,
196 };
197
198 int rv = ngSession.submitResponse(streamId, hdr, &dataPrd);
199 if (rv != 0)
200 {
Ed Tanous62598e32023-07-17 17:06:25 -0700201 BMCWEB_LOG_ERROR("Fatal error: {}", nghttp2_strerror(rv));
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800202 close();
203 return -1;
204 }
Ed Tanousd0882182024-01-26 23:45:25 -0800205 writeBuffer();
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800206
207 return 0;
208 }
209
210 nghttp2_session initializeNghttp2Session()
211 {
212 nghttp2_session_callbacks callbacks;
213 callbacks.setOnFrameRecvCallback(onFrameRecvCallbackStatic);
214 callbacks.setOnStreamCloseCallback(onStreamCloseCallbackStatic);
215 callbacks.setOnHeaderCallback(onHeaderCallbackStatic);
216 callbacks.setOnBeginHeadersCallback(onBeginHeadersCallbackStatic);
Ed Tanous325310d2024-03-15 09:05:04 -0700217 callbacks.setOnDataChunkRecvCallback(onDataChunkRecvStatic);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800218
219 nghttp2_session session(callbacks);
220 session.setUserData(this);
221
222 return session;
223 }
224
225 int onRequestRecv(int32_t streamId)
226 {
Ed Tanous62598e32023-07-17 17:06:25 -0700227 BMCWEB_LOG_DEBUG("on_request_recv");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800228
229 auto it = streams.find(streamId);
230 if (it == streams.end())
231 {
232 close();
233 return -1;
234 }
Ed Tanousd547d8d2024-03-16 18:04:41 -0700235 auto& reqReader = it->second.reqReader;
236 if (reqReader)
Ed Tanous325310d2024-03-15 09:05:04 -0700237 {
238 boost::beast::error_code ec;
Ed Tanousd547d8d2024-03-16 18:04:41 -0700239 reqReader->finish(ec);
Ed Tanous325310d2024-03-15 09:05:04 -0700240 if (ec)
241 {
242 BMCWEB_LOG_CRITICAL("Failed to finalize payload");
243 close();
244 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
245 }
246 }
Ed Tanousf42e8592023-08-25 10:47:44 -0700247 crow::Request& thisReq = it->second.req;
Ed Tanous8e8245d2024-04-11 22:21:38 -0700248 thisReq.ioService = static_cast<decltype(thisReq.ioService)>(
249 &adaptor.get_executor().context());
Ed Tanous62598e32023-07-17 17:06:25 -0700250 BMCWEB_LOG_DEBUG("Handling {} \"{}\"", logPtr(&thisReq),
251 thisReq.url().encoded_path());
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800252
Ed Tanousf42e8592023-08-25 10:47:44 -0700253 crow::Response& thisRes = it->second.res;
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800254
255 thisRes.setCompleteRequestHandler(
256 [this, streamId](Response& completeRes) {
Ed Tanous62598e32023-07-17 17:06:25 -0700257 BMCWEB_LOG_DEBUG("res.completeRequestHandler called");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800258 if (sendResponse(completeRes, streamId) != 0)
259 {
260 close();
261 return;
262 }
263 });
264 auto asyncResp =
Ed Tanousf42e8592023-08-25 10:47:44 -0700265 std::make_shared<bmcweb::AsyncResp>(std::move(it->second.res));
Ed Tanous325310d2024-03-15 09:05:04 -0700266#ifndef BMCWEB_INSECURE_DISABLE_AUTHX
267 thisReq.session = crow::authentication::authenticate(
268 {}, thisRes, thisReq.method(), thisReq.req, nullptr);
269 if (!crow::authentication::isOnAllowlist(thisReq.url().path(),
270 thisReq.method()) &&
271 thisReq.session == nullptr)
272 {
273 BMCWEB_LOG_WARNING("Authentication failed");
274 forward_unauthorized::sendUnauthorized(
275 thisReq.url().encoded_path(),
276 thisReq.getHeaderValue("X-Requested-With"),
277 thisReq.getHeaderValue("Accept"), thisRes);
278 }
279 else
280#endif // BMCWEB_INSECURE_DISABLE_AUTHX
281 {
282 handler->handle(thisReq, asyncResp);
283 }
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800284 return 0;
285 }
286
Ed Tanous325310d2024-03-15 09:05:04 -0700287 int onDataChunkRecvCallback(uint8_t /*flags*/, int32_t streamId,
288 const uint8_t* data, size_t len)
289 {
290 auto thisStream = streams.find(streamId);
291 if (thisStream == streams.end())
292 {
293 BMCWEB_LOG_ERROR("Unknown stream{}", streamId);
294 close();
295 return -1;
296 }
Ed Tanousd547d8d2024-03-16 18:04:41 -0700297
298 std::optional<bmcweb::HttpBody::reader>& reqReader =
299 thisStream->second.reqReader;
300 if (!reqReader)
Ed Tanous325310d2024-03-15 09:05:04 -0700301 {
Ed Tanous8e5cc7b2024-04-02 11:00:54 -0700302 reqReader.emplace(
303 bmcweb::HttpBody::reader(thisStream->second.req.req.base(),
304 thisStream->second.req.req.body()));
Ed Tanous325310d2024-03-15 09:05:04 -0700305 }
306 boost::beast::error_code ec;
Ed Tanousd547d8d2024-03-16 18:04:41 -0700307 reqReader->put(boost::asio::const_buffer(data, len), ec);
Ed Tanous325310d2024-03-15 09:05:04 -0700308 if (ec)
309 {
310 BMCWEB_LOG_CRITICAL("Failed to write payload");
311 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
312 }
313 return 0;
314 }
315
316 static int onDataChunkRecvStatic(nghttp2_session* /* session */,
317 uint8_t flags, int32_t streamId,
318 const uint8_t* data, size_t len,
319 void* userData)
320 {
321 BMCWEB_LOG_DEBUG("on_frame_recv_callback");
322 if (userData == nullptr)
323 {
324 BMCWEB_LOG_CRITICAL("user data was null?");
325 return NGHTTP2_ERR_CALLBACK_FAILURE;
326 }
327 return userPtrToSelf(userData).onDataChunkRecvCallback(flags, streamId,
328 data, len);
329 }
330
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800331 int onFrameRecvCallback(const nghttp2_frame& frame)
332 {
Ed Tanous62598e32023-07-17 17:06:25 -0700333 BMCWEB_LOG_DEBUG("frame type {}", static_cast<int>(frame.hd.type));
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800334 switch (frame.hd.type)
335 {
336 case NGHTTP2_DATA:
337 case NGHTTP2_HEADERS:
338 // Check that the client request has finished
339 if ((frame.hd.flags & NGHTTP2_FLAG_END_STREAM) != 0)
340 {
341 return onRequestRecv(frame.hd.stream_id);
342 }
343 break;
344 default:
345 break;
346 }
347 return 0;
348 }
349
350 static int onFrameRecvCallbackStatic(nghttp2_session* /* session */,
351 const nghttp2_frame* frame,
352 void* userData)
353 {
Ed Tanous62598e32023-07-17 17:06:25 -0700354 BMCWEB_LOG_DEBUG("on_frame_recv_callback");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800355 if (userData == nullptr)
356 {
Ed Tanous62598e32023-07-17 17:06:25 -0700357 BMCWEB_LOG_CRITICAL("user data was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800358 return NGHTTP2_ERR_CALLBACK_FAILURE;
359 }
360 if (frame == nullptr)
361 {
Ed Tanous62598e32023-07-17 17:06:25 -0700362 BMCWEB_LOG_CRITICAL("frame was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800363 return NGHTTP2_ERR_CALLBACK_FAILURE;
364 }
365 return userPtrToSelf(userData).onFrameRecvCallback(*frame);
366 }
367
368 static self_type& userPtrToSelf(void* userData)
369 {
370 // This method exists to keep the unsafe reinterpret cast in one
371 // place.
372 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
373 return *reinterpret_cast<self_type*>(userData);
374 }
375
376 static int onStreamCloseCallbackStatic(nghttp2_session* /* session */,
377 int32_t streamId,
378 uint32_t /*unused*/, void* userData)
379 {
Ed Tanous62598e32023-07-17 17:06:25 -0700380 BMCWEB_LOG_DEBUG("on_stream_close_callback stream {}", streamId);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800381 if (userData == nullptr)
382 {
Ed Tanous62598e32023-07-17 17:06:25 -0700383 BMCWEB_LOG_CRITICAL("user data was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800384 return NGHTTP2_ERR_CALLBACK_FAILURE;
385 }
Ed Tanousf42e8592023-08-25 10:47:44 -0700386 if (userPtrToSelf(userData).streams.erase(streamId) <= 0)
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800387 {
388 return -1;
389 }
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800390 return 0;
391 }
392
393 int onHeaderCallback(const nghttp2_frame& frame,
394 std::span<const uint8_t> name,
395 std::span<const uint8_t> value)
396 {
397 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
398 std::string_view nameSv(reinterpret_cast<const char*>(name.data()),
399 name.size());
400 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
401 std::string_view valueSv(reinterpret_cast<const char*>(value.data()),
402 value.size());
403
Ed Tanous62598e32023-07-17 17:06:25 -0700404 BMCWEB_LOG_DEBUG("on_header_callback name: {} value {}", nameSv,
405 valueSv);
Ed Tanousa07e9812024-03-19 10:31:13 -0700406 if (frame.hd.type != NGHTTP2_HEADERS)
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800407 {
Ed Tanousa07e9812024-03-19 10:31:13 -0700408 return 0;
409 }
410 if (frame.headers.cat != NGHTTP2_HCAT_REQUEST)
411 {
412 return 0;
413 }
414 auto thisStream = streams.find(frame.hd.stream_id);
415 if (thisStream == streams.end())
416 {
417 BMCWEB_LOG_ERROR("Unknown stream{}", frame.hd.stream_id);
418 close();
419 return -1;
420 }
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800421
Ed Tanousa07e9812024-03-19 10:31:13 -0700422 crow::Request& thisReq = thisStream->second.req;
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800423
Ed Tanousa07e9812024-03-19 10:31:13 -0700424 if (nameSv == ":path")
425 {
426 thisReq.target(valueSv);
427 }
428 else if (nameSv == ":method")
429 {
430 boost::beast::http::verb verb =
431 boost::beast::http::string_to_verb(valueSv);
432 if (verb == boost::beast::http::verb::unknown)
433 {
434 BMCWEB_LOG_ERROR("Unknown http verb {}", valueSv);
435 close();
436 return -1;
437 }
Myung Bae1873a042024-04-01 09:27:39 -0500438 thisReq.method(verb);
Ed Tanousa07e9812024-03-19 10:31:13 -0700439 }
440 else if (nameSv == ":scheme")
441 {
442 // Nothing to check on scheme
443 }
444 else
445 {
Myung Bae1873a042024-04-01 09:27:39 -0500446 thisReq.addHeader(nameSv, valueSv);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800447 }
448 return 0;
449 }
450
451 static int onHeaderCallbackStatic(nghttp2_session* /* session */,
452 const nghttp2_frame* frame,
453 const uint8_t* name, size_t namelen,
454 const uint8_t* value, size_t vallen,
455 uint8_t /* flags */, void* userData)
456 {
457 if (userData == nullptr)
458 {
Ed Tanous62598e32023-07-17 17:06:25 -0700459 BMCWEB_LOG_CRITICAL("user data was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800460 return NGHTTP2_ERR_CALLBACK_FAILURE;
461 }
462 if (frame == nullptr)
463 {
Ed Tanous62598e32023-07-17 17:06:25 -0700464 BMCWEB_LOG_CRITICAL("frame was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800465 return NGHTTP2_ERR_CALLBACK_FAILURE;
466 }
467 if (name == nullptr)
468 {
Ed Tanous62598e32023-07-17 17:06:25 -0700469 BMCWEB_LOG_CRITICAL("name was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800470 return NGHTTP2_ERR_CALLBACK_FAILURE;
471 }
472 if (value == nullptr)
473 {
Ed Tanous62598e32023-07-17 17:06:25 -0700474 BMCWEB_LOG_CRITICAL("value was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800475 return NGHTTP2_ERR_CALLBACK_FAILURE;
476 }
477 return userPtrToSelf(userData).onHeaderCallback(*frame, {name, namelen},
478 {value, vallen});
479 }
480
481 int onBeginHeadersCallback(const nghttp2_frame& frame)
482 {
483 if (frame.hd.type == NGHTTP2_HEADERS &&
484 frame.headers.cat == NGHTTP2_HCAT_REQUEST)
485 {
Ed Tanous62598e32023-07-17 17:06:25 -0700486 BMCWEB_LOG_DEBUG("create stream for id {}", frame.hd.stream_id);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800487
Ed Tanousf42e8592023-08-25 10:47:44 -0700488 Http2StreamData& stream = streams[frame.hd.stream_id];
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800489 // http2 is by definition always tls
Ed Tanousf42e8592023-08-25 10:47:44 -0700490 stream.req.isSecure = true;
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800491 }
492 return 0;
493 }
494
495 static int onBeginHeadersCallbackStatic(nghttp2_session* /* session */,
496 const nghttp2_frame* frame,
497 void* userData)
498 {
Ed Tanous62598e32023-07-17 17:06:25 -0700499 BMCWEB_LOG_DEBUG("on_begin_headers_callback");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800500 if (userData == nullptr)
501 {
Ed Tanous62598e32023-07-17 17:06:25 -0700502 BMCWEB_LOG_CRITICAL("user data was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800503 return NGHTTP2_ERR_CALLBACK_FAILURE;
504 }
505 if (frame == nullptr)
506 {
Ed Tanous62598e32023-07-17 17:06:25 -0700507 BMCWEB_LOG_CRITICAL("frame was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800508 return NGHTTP2_ERR_CALLBACK_FAILURE;
509 }
510 return userPtrToSelf(userData).onBeginHeadersCallback(*frame);
511 }
512
513 static void afterWriteBuffer(const std::shared_ptr<self_type>& self,
514 const boost::system::error_code& ec,
515 size_t sendLength)
516 {
517 self->isWriting = false;
Ed Tanous62598e32023-07-17 17:06:25 -0700518 BMCWEB_LOG_DEBUG("Sent {}", sendLength);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800519 if (ec)
520 {
521 self->close();
522 return;
523 }
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800524 self->writeBuffer();
525 }
526
527 void writeBuffer()
528 {
529 if (isWriting)
530 {
531 return;
532 }
Ed Tanousd0882182024-01-26 23:45:25 -0800533 std::span<const uint8_t> data = ngSession.memSend();
534 if (data.empty())
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800535 {
536 return;
537 }
538 isWriting = true;
Ed Tanous325310d2024-03-15 09:05:04 -0700539 boost::asio::async_write(
540 adaptor, boost::asio::const_buffer(data.data(), data.size()),
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800541 std::bind_front(afterWriteBuffer, shared_from_this()));
542 }
543
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800544 void close()
545 {
546 if constexpr (std::is_same_v<Adaptor,
Ed Tanous003301a2024-04-16 09:59:19 -0700547 boost::asio::ssl::stream<
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800548 boost::asio::ip::tcp::socket>>)
549 {
550 adaptor.next_layer().close();
551 }
552 else
553 {
554 adaptor.close();
555 }
556 }
557
Ed Tanousd0882182024-01-26 23:45:25 -0800558 void afterDoRead(const std::shared_ptr<self_type>& /*self*/,
559 const boost::system::error_code& ec,
560 size_t bytesTransferred)
561 {
562 BMCWEB_LOG_DEBUG("{} async_read_some {} Bytes", logPtr(this),
563 bytesTransferred);
564
565 if (ec)
566 {
567 BMCWEB_LOG_ERROR("{} Error while reading: {}", logPtr(this),
568 ec.message());
569 close();
570 BMCWEB_LOG_DEBUG("{} from read(1)", logPtr(this));
571 return;
572 }
573 std::span<uint8_t> bufferSpan{inBuffer.data(), bytesTransferred};
574
575 ssize_t readLen = ngSession.memRecv(bufferSpan);
576 if (readLen < 0)
577 {
578 BMCWEB_LOG_ERROR("nghttp2_session_mem_recv returned {}", readLen);
579 close();
580 return;
581 }
582 writeBuffer();
583
584 doRead();
585 }
586
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800587 void doRead()
588 {
Ed Tanous62598e32023-07-17 17:06:25 -0700589 BMCWEB_LOG_DEBUG("{} doRead", logPtr(this));
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800590 adaptor.async_read_some(
Ed Tanousd0882182024-01-26 23:45:25 -0800591 boost::asio::buffer(inBuffer),
592 std::bind_front(&self_type::afterDoRead, this, shared_from_this()));
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800593 }
594
595 // A mapping from http2 stream ID to Stream Data
Ed Tanous52e31622024-01-23 16:31:11 -0800596 std::map<int32_t, Http2StreamData> streams;
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800597
Ed Tanousd0882182024-01-26 23:45:25 -0800598 std::array<uint8_t, 8192> inBuffer{};
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800599
600 Adaptor adaptor;
601 bool isWriting = false;
602
603 nghttp2_session ngSession;
604
605 Handler* handler;
606 std::function<std::string()>& getCachedDateStr;
607
608 using std::enable_shared_from_this<
609 HTTP2Connection<Adaptor, Handler>>::shared_from_this;
610
611 using std::enable_shared_from_this<
612 HTTP2Connection<Adaptor, Handler>>::weak_from_this;
613};
614} // namespace crow