blob: b3ba0125ee3003867c8be6e854b105348a182952 [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 Tanous325310d2024-03-15 09:05:04 -07009#include "http_body.hpp"
Ed Tanousfca2cbe2021-01-28 14:49:59 -080010#include "http_response.hpp"
11#include "http_utility.hpp"
12#include "logging.hpp"
13#include "mutual_tls.hpp"
14#include "nghttp2_adapters.hpp"
15#include "ssl_key_handler.hpp"
16#include "utility.hpp"
17
Ed Tanousfca2cbe2021-01-28 14:49:59 -080018#include <boost/asio/io_context.hpp>
19#include <boost/asio/ip/tcp.hpp>
20#include <boost/asio/ssl/stream.hpp>
21#include <boost/asio/steady_timer.hpp>
Ed Tanousfca2cbe2021-01-28 14:49:59 -080022#include <boost/beast/http/error.hpp>
23#include <boost/beast/http/parser.hpp>
24#include <boost/beast/http/read.hpp>
25#include <boost/beast/http/serializer.hpp>
Ed Tanousfca2cbe2021-01-28 14:49:59 -080026#include <boost/beast/http/write.hpp>
Ed Tanousfca2cbe2021-01-28 14:49:59 -080027#include <boost/beast/websocket.hpp>
Ed Tanousd0882182024-01-26 23:45:25 -080028#include <boost/system/error_code.hpp>
Ed Tanousfca2cbe2021-01-28 14:49:59 -080029
Ed Tanousd0882182024-01-26 23:45:25 -080030#include <array>
Ed Tanousfca2cbe2021-01-28 14:49:59 -080031#include <atomic>
32#include <chrono>
Ed Tanousd0882182024-01-26 23:45:25 -080033#include <functional>
34#include <memory>
Ed Tanous89cda632024-04-16 08:45:54 -070035#include <string>
Ed Tanousfca2cbe2021-01-28 14:49:59 -080036#include <vector>
37
38namespace crow
39{
40
41struct Http2StreamData
42{
Jonathan Doman102a4cd2024-04-15 16:56:23 -070043 std::shared_ptr<Request> req = std::make_shared<Request>();
Ed Tanous325310d2024-03-15 09:05:04 -070044 std::optional<bmcweb::HttpBody::reader> reqReader;
Ed Tanous89cda632024-04-16 08:45:54 -070045 std::string accept;
Ed Tanous47f29342024-03-19 12:18:06 -070046 Response res;
Ed Tanousb2896142024-01-31 15:25:47 -080047 std::optional<bmcweb::HttpBody::writer> writer;
Ed Tanousfca2cbe2021-01-28 14:49:59 -080048};
49
50template <typename Adaptor, typename Handler>
51class HTTP2Connection :
52 public std::enable_shared_from_this<HTTP2Connection<Adaptor, Handler>>
53{
54 using self_type = HTTP2Connection<Adaptor, Handler>;
55
56 public:
57 HTTP2Connection(Adaptor&& adaptorIn, Handler* handlerIn,
Ed Tanous52e31622024-01-23 16:31:11 -080058 std::function<std::string()>& getCachedDateStrF) :
Patrick Williamsbd79bce2024-08-16 15:22:20 -040059 adaptor(std::move(adaptorIn)), ngSession(initializeNghttp2Session()),
60 handler(handlerIn), getCachedDateStr(getCachedDateStrF)
Ed Tanousfca2cbe2021-01-28 14:49:59 -080061 {}
62
63 void start()
64 {
65 // Create the control stream
Ed Tanousf42e8592023-08-25 10:47:44 -070066 streams[0];
Ed Tanousfca2cbe2021-01-28 14:49:59 -080067
68 if (sendServerConnectionHeader() != 0)
69 {
Ed Tanous62598e32023-07-17 17:06:25 -070070 BMCWEB_LOG_ERROR("send_server_connection_header failed");
Ed Tanousfca2cbe2021-01-28 14:49:59 -080071 return;
72 }
73 doRead();
74 }
75
76 int sendServerConnectionHeader()
77 {
Ed Tanous62598e32023-07-17 17:06:25 -070078 BMCWEB_LOG_DEBUG("send_server_connection_header()");
Ed Tanousfca2cbe2021-01-28 14:49:59 -080079
80 uint32_t maxStreams = 4;
81 std::array<nghttp2_settings_entry, 2> iv = {
82 {{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, maxStreams},
83 {NGHTTP2_SETTINGS_ENABLE_PUSH, 0}}};
84 int rv = ngSession.submitSettings(iv);
85 if (rv != 0)
86 {
Ed Tanous62598e32023-07-17 17:06:25 -070087 BMCWEB_LOG_ERROR("Fatal error: {}", nghttp2_strerror(rv));
Ed Tanousfca2cbe2021-01-28 14:49:59 -080088 return -1;
89 }
Ed Tanousd0882182024-01-26 23:45:25 -080090 writeBuffer();
Ed Tanousfca2cbe2021-01-28 14:49:59 -080091 return 0;
92 }
93
Patrick Williamsbd79bce2024-08-16 15:22:20 -040094 static ssize_t
95 fileReadCallback(nghttp2_session* /* session */, int32_t streamId,
96 uint8_t* buf, size_t length, uint32_t* dataFlags,
97 nghttp2_data_source* /*source*/, void* userPtr)
Ed Tanousfca2cbe2021-01-28 14:49:59 -080098 {
Ed Tanousf42e8592023-08-25 10:47:44 -070099 self_type& self = userPtrToSelf(userPtr);
100
101 auto streamIt = self.streams.find(streamId);
102 if (streamIt == self.streams.end())
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800103 {
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800104 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
105 }
Ed Tanousf42e8592023-08-25 10:47:44 -0700106 Http2StreamData& stream = streamIt->second;
Ed Tanous62598e32023-07-17 17:06:25 -0700107 BMCWEB_LOG_DEBUG("File read callback length: {}", length);
Ed Tanousd547d8d2024-03-16 18:04:41 -0700108 if (!stream.writer)
109 {
110 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
111 }
Ed Tanous52e31622024-01-23 16:31:11 -0800112 boost::beast::error_code ec;
113 boost::optional<std::pair<boost::asio::const_buffer, bool>> out =
114 stream.writer->getWithMaxSize(ec, length);
115 if (ec)
Ed Tanous27b0cf92023-08-07 12:02:40 -0700116 {
Ed Tanous325310d2024-03-15 09:05:04 -0700117 BMCWEB_LOG_CRITICAL("Failed to get buffer");
Ed Tanous27b0cf92023-08-07 12:02:40 -0700118 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
119 }
Ed Tanous52e31622024-01-23 16:31:11 -0800120 if (!out)
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800121 {
Ed Tanous325310d2024-03-15 09:05:04 -0700122 BMCWEB_LOG_ERROR("Empty file, setting EOF");
Ed Tanous52e31622024-01-23 16:31:11 -0800123 *dataFlags |= NGHTTP2_DATA_FLAG_EOF;
124 return 0;
125 }
126
127 BMCWEB_LOG_DEBUG("Send chunk of size: {}", out->first.size());
128 if (length < out->first.size())
129 {
Ed Tanous325310d2024-03-15 09:05:04 -0700130 BMCWEB_LOG_CRITICAL(
131 "Buffer overflow that should never happen happened");
Ed Tanous52e31622024-01-23 16:31:11 -0800132 // Should never happen because of length limit on get() above
133 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
134 }
Ed Tanous325310d2024-03-15 09:05:04 -0700135 boost::asio::mutable_buffer writeableBuf(buf, length);
Ed Tanous52e31622024-01-23 16:31:11 -0800136 BMCWEB_LOG_DEBUG("Copying {} bytes to buf", out->first.size());
Ed Tanous325310d2024-03-15 09:05:04 -0700137 size_t copied = boost::asio::buffer_copy(writeableBuf, out->first);
138 if (copied != out->first.size())
139 {
140 BMCWEB_LOG_ERROR(
141 "Couldn't copy all {} bytes into buffer, only copied {}",
142 out->first.size(), copied);
143 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
144 }
Ed Tanous52e31622024-01-23 16:31:11 -0800145
146 if (!out->second)
147 {
Ed Tanous325310d2024-03-15 09:05:04 -0700148 BMCWEB_LOG_DEBUG("Setting EOF flag");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800149 *dataFlags |= NGHTTP2_DATA_FLAG_EOF;
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800150 }
Ed Tanous325310d2024-03-15 09:05:04 -0700151 return static_cast<ssize_t>(copied);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800152 }
153
154 nghttp2_nv headerFromStringViews(std::string_view name,
Ed Tanous52e31622024-01-23 16:31:11 -0800155 std::string_view value, uint8_t flags)
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800156 {
157 uint8_t* nameData = std::bit_cast<uint8_t*>(name.data());
158 uint8_t* valueData = std::bit_cast<uint8_t*>(value.data());
Ed Tanous52e31622024-01-23 16:31:11 -0800159 return {nameData, valueData, name.size(), value.size(), flags};
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800160 }
161
162 int sendResponse(Response& completedRes, int32_t streamId)
163 {
Ed Tanous62598e32023-07-17 17:06:25 -0700164 BMCWEB_LOG_DEBUG("send_response stream_id:{}", streamId);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800165
166 auto it = streams.find(streamId);
167 if (it == streams.end())
168 {
169 close();
170 return -1;
171 }
Ed Tanous499b5b42024-04-06 08:39:18 -0700172 Http2StreamData& stream = it->second;
173 Response& res = stream.res;
174 res = std::move(completedRes);
Ed Tanous499b5b42024-04-06 08:39:18 -0700175
Ed Tanous89cda632024-04-16 08:45:54 -0700176 completeResponseFields(stream.accept, res);
Ed Tanous499b5b42024-04-06 08:39:18 -0700177 res.addHeader(boost::beast::http::field::date, getCachedDateStr());
178 res.preparePayload();
179
180 boost::beast::http::fields& fields = res.fields();
181 std::string code = std::to_string(res.resultInt());
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800182 std::vector<nghttp2_nv> hdr;
Ed Tanous52e31622024-01-23 16:31:11 -0800183 hdr.emplace_back(
184 headerFromStringViews(":status", code, NGHTTP2_NV_FLAG_NONE));
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800185 for (const boost::beast::http::fields::value_type& header : fields)
186 {
Ed Tanous52e31622024-01-23 16:31:11 -0800187 hdr.emplace_back(headerFromStringViews(
Ed Tanousd0882182024-01-26 23:45:25 -0800188 header.name_string(), header.value(), NGHTTP2_NV_FLAG_NONE));
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800189 }
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 Tanousdaadfb22024-12-20 09:25:54 -0800239 bmcweb::HttpBody::reader::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 }
Jonathan Doman102a4cd2024-04-15 16:56:23 -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 Tanous89cda632024-04-16 08:45:54 -0700250
251 it->second.accept = thisReq.getHeaderValue("Accept");
252
Ed Tanous62598e32023-07-17 17:06:25 -0700253 BMCWEB_LOG_DEBUG("Handling {} \"{}\"", logPtr(&thisReq),
254 thisReq.url().encoded_path());
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800255
Ed Tanousf42e8592023-08-25 10:47:44 -0700256 crow::Response& thisRes = it->second.res;
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800257
258 thisRes.setCompleteRequestHandler(
259 [this, streamId](Response& completeRes) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400260 BMCWEB_LOG_DEBUG("res.completeRequestHandler called");
261 if (sendResponse(completeRes, streamId) != 0)
262 {
263 close();
264 return;
265 }
266 });
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800267 auto asyncResp =
Ed Tanousf42e8592023-08-25 10:47:44 -0700268 std::make_shared<bmcweb::AsyncResp>(std::move(it->second.res));
Ed Tanous83328312024-05-09 15:48:09 -0700269 if constexpr (!BMCWEB_INSECURE_DISABLE_AUTH)
Ed Tanous325310d2024-03-15 09:05:04 -0700270 {
Ed Tanous83328312024-05-09 15:48:09 -0700271 thisReq.session = crow::authentication::authenticate(
272 {}, asyncResp->res, thisReq.method(), thisReq.req, nullptr);
273 if (!crow::authentication::isOnAllowlist(thisReq.url().path(),
274 thisReq.method()) &&
275 thisReq.session == nullptr)
Ed Tanous499b5b42024-04-06 08:39:18 -0700276 {
Ed Tanous83328312024-05-09 15:48:09 -0700277 BMCWEB_LOG_WARNING("Authentication failed");
278 forward_unauthorized::sendUnauthorized(
279 thisReq.url().encoded_path(),
280 thisReq.getHeaderValue("X-Requested-With"),
281 thisReq.getHeaderValue("Accept"), asyncResp->res);
282 return 0;
Ed Tanous499b5b42024-04-06 08:39:18 -0700283 }
Ed Tanous325310d2024-03-15 09:05:04 -0700284 }
Ed Tanous83328312024-05-09 15:48:09 -0700285 std::string_view expected =
286 thisReq.getHeaderValue(boost::beast::http::field::if_none_match);
287 BMCWEB_LOG_DEBUG("Setting expected hash {}", expected);
288 if (!expected.empty())
289 {
290 asyncResp->res.setExpectedHash(expected);
291 }
292 handler->handle(it->second.req, asyncResp);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800293 return 0;
294 }
295
Ed Tanous325310d2024-03-15 09:05:04 -0700296 int onDataChunkRecvCallback(uint8_t /*flags*/, int32_t streamId,
297 const uint8_t* data, size_t len)
298 {
299 auto thisStream = streams.find(streamId);
300 if (thisStream == streams.end())
301 {
302 BMCWEB_LOG_ERROR("Unknown stream{}", streamId);
303 close();
304 return -1;
305 }
Ed Tanousd547d8d2024-03-16 18:04:41 -0700306
307 std::optional<bmcweb::HttpBody::reader>& reqReader =
308 thisStream->second.reqReader;
309 if (!reqReader)
Ed Tanous325310d2024-03-15 09:05:04 -0700310 {
Ed Tanous8e5cc7b2024-04-02 11:00:54 -0700311 reqReader.emplace(
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700312 bmcweb::HttpBody::reader(thisStream->second.req->req.base(),
313 thisStream->second.req->req.body()));
Ed Tanous325310d2024-03-15 09:05:04 -0700314 }
315 boost::beast::error_code ec;
Ed Tanousd547d8d2024-03-16 18:04:41 -0700316 reqReader->put(boost::asio::const_buffer(data, len), ec);
Ed Tanous325310d2024-03-15 09:05:04 -0700317 if (ec)
318 {
319 BMCWEB_LOG_CRITICAL("Failed to write payload");
320 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
321 }
322 return 0;
323 }
324
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400325 static int onDataChunkRecvStatic(
326 nghttp2_session* /* session */, uint8_t flags, int32_t streamId,
327 const uint8_t* data, size_t len, void* userData)
Ed Tanous325310d2024-03-15 09:05:04 -0700328 {
329 BMCWEB_LOG_DEBUG("on_frame_recv_callback");
330 if (userData == nullptr)
331 {
332 BMCWEB_LOG_CRITICAL("user data was null?");
333 return NGHTTP2_ERR_CALLBACK_FAILURE;
334 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400335 return userPtrToSelf(userData).onDataChunkRecvCallback(
336 flags, streamId, data, len);
Ed Tanous325310d2024-03-15 09:05:04 -0700337 }
338
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800339 int onFrameRecvCallback(const nghttp2_frame& frame)
340 {
Ed Tanous62598e32023-07-17 17:06:25 -0700341 BMCWEB_LOG_DEBUG("frame type {}", static_cast<int>(frame.hd.type));
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800342 switch (frame.hd.type)
343 {
344 case NGHTTP2_DATA:
345 case NGHTTP2_HEADERS:
346 // Check that the client request has finished
347 if ((frame.hd.flags & NGHTTP2_FLAG_END_STREAM) != 0)
348 {
349 return onRequestRecv(frame.hd.stream_id);
350 }
351 break;
352 default:
353 break;
354 }
355 return 0;
356 }
357
358 static int onFrameRecvCallbackStatic(nghttp2_session* /* session */,
359 const nghttp2_frame* frame,
360 void* userData)
361 {
Ed Tanous62598e32023-07-17 17:06:25 -0700362 BMCWEB_LOG_DEBUG("on_frame_recv_callback");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800363 if (userData == nullptr)
364 {
Ed Tanous62598e32023-07-17 17:06:25 -0700365 BMCWEB_LOG_CRITICAL("user data was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800366 return NGHTTP2_ERR_CALLBACK_FAILURE;
367 }
368 if (frame == nullptr)
369 {
Ed Tanous62598e32023-07-17 17:06:25 -0700370 BMCWEB_LOG_CRITICAL("frame was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800371 return NGHTTP2_ERR_CALLBACK_FAILURE;
372 }
373 return userPtrToSelf(userData).onFrameRecvCallback(*frame);
374 }
375
376 static self_type& userPtrToSelf(void* userData)
377 {
378 // This method exists to keep the unsafe reinterpret cast in one
379 // place.
380 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
381 return *reinterpret_cast<self_type*>(userData);
382 }
383
384 static int onStreamCloseCallbackStatic(nghttp2_session* /* session */,
385 int32_t streamId,
386 uint32_t /*unused*/, void* userData)
387 {
Ed Tanous62598e32023-07-17 17:06:25 -0700388 BMCWEB_LOG_DEBUG("on_stream_close_callback stream {}", streamId);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800389 if (userData == nullptr)
390 {
Ed Tanous62598e32023-07-17 17:06:25 -0700391 BMCWEB_LOG_CRITICAL("user data was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800392 return NGHTTP2_ERR_CALLBACK_FAILURE;
393 }
Ed Tanousf42e8592023-08-25 10:47:44 -0700394 if (userPtrToSelf(userData).streams.erase(streamId) <= 0)
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800395 {
396 return -1;
397 }
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800398 return 0;
399 }
400
401 int onHeaderCallback(const nghttp2_frame& frame,
402 std::span<const uint8_t> name,
403 std::span<const uint8_t> value)
404 {
405 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
406 std::string_view nameSv(reinterpret_cast<const char*>(name.data()),
407 name.size());
408 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
409 std::string_view valueSv(reinterpret_cast<const char*>(value.data()),
410 value.size());
411
Ed Tanous62598e32023-07-17 17:06:25 -0700412 BMCWEB_LOG_DEBUG("on_header_callback name: {} value {}", nameSv,
413 valueSv);
Ed Tanousa07e9812024-03-19 10:31:13 -0700414 if (frame.hd.type != NGHTTP2_HEADERS)
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800415 {
Ed Tanousa07e9812024-03-19 10:31:13 -0700416 return 0;
417 }
418 if (frame.headers.cat != NGHTTP2_HCAT_REQUEST)
419 {
420 return 0;
421 }
422 auto thisStream = streams.find(frame.hd.stream_id);
423 if (thisStream == streams.end())
424 {
425 BMCWEB_LOG_ERROR("Unknown stream{}", frame.hd.stream_id);
426 close();
427 return -1;
428 }
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800429
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700430 crow::Request& thisReq = *thisStream->second.req;
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800431
Ed Tanousa07e9812024-03-19 10:31:13 -0700432 if (nameSv == ":path")
433 {
434 thisReq.target(valueSv);
435 }
436 else if (nameSv == ":method")
437 {
438 boost::beast::http::verb verb =
439 boost::beast::http::string_to_verb(valueSv);
440 if (verb == boost::beast::http::verb::unknown)
441 {
442 BMCWEB_LOG_ERROR("Unknown http verb {}", valueSv);
Ed Tanous50bfc912024-07-29 14:20:50 -0700443 verb = boost::beast::http::verb::trace;
Ed Tanousa07e9812024-03-19 10:31:13 -0700444 }
Myung Bae1873a042024-04-01 09:27:39 -0500445 thisReq.method(verb);
Ed Tanousa07e9812024-03-19 10:31:13 -0700446 }
447 else if (nameSv == ":scheme")
448 {
449 // Nothing to check on scheme
450 }
451 else
452 {
Myung Bae1873a042024-04-01 09:27:39 -0500453 thisReq.addHeader(nameSv, valueSv);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800454 }
455 return 0;
456 }
457
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400458 static int onHeaderCallbackStatic(
459 nghttp2_session* /* session */, const nghttp2_frame* frame,
460 const uint8_t* name, size_t namelen, const uint8_t* value,
461 size_t vallen, uint8_t /* flags */, void* userData)
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800462 {
463 if (userData == nullptr)
464 {
Ed Tanous62598e32023-07-17 17:06:25 -0700465 BMCWEB_LOG_CRITICAL("user data was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800466 return NGHTTP2_ERR_CALLBACK_FAILURE;
467 }
468 if (frame == nullptr)
469 {
Ed Tanous62598e32023-07-17 17:06:25 -0700470 BMCWEB_LOG_CRITICAL("frame was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800471 return NGHTTP2_ERR_CALLBACK_FAILURE;
472 }
473 if (name == nullptr)
474 {
Ed Tanous62598e32023-07-17 17:06:25 -0700475 BMCWEB_LOG_CRITICAL("name was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800476 return NGHTTP2_ERR_CALLBACK_FAILURE;
477 }
478 if (value == nullptr)
479 {
Ed Tanous62598e32023-07-17 17:06:25 -0700480 BMCWEB_LOG_CRITICAL("value was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800481 return NGHTTP2_ERR_CALLBACK_FAILURE;
482 }
483 return userPtrToSelf(userData).onHeaderCallback(*frame, {name, namelen},
484 {value, vallen});
485 }
486
487 int onBeginHeadersCallback(const nghttp2_frame& frame)
488 {
489 if (frame.hd.type == NGHTTP2_HEADERS &&
490 frame.headers.cat == NGHTTP2_HCAT_REQUEST)
491 {
Ed Tanous62598e32023-07-17 17:06:25 -0700492 BMCWEB_LOG_DEBUG("create stream for id {}", frame.hd.stream_id);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800493
Ed Tanous3a499412024-06-05 16:05:35 -0700494 streams.emplace(frame.hd.stream_id, Http2StreamData());
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800495 }
496 return 0;
497 }
498
499 static int onBeginHeadersCallbackStatic(nghttp2_session* /* session */,
500 const nghttp2_frame* frame,
501 void* userData)
502 {
Ed Tanous62598e32023-07-17 17:06:25 -0700503 BMCWEB_LOG_DEBUG("on_begin_headers_callback");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800504 if (userData == nullptr)
505 {
Ed Tanous62598e32023-07-17 17:06:25 -0700506 BMCWEB_LOG_CRITICAL("user data was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800507 return NGHTTP2_ERR_CALLBACK_FAILURE;
508 }
509 if (frame == nullptr)
510 {
Ed Tanous62598e32023-07-17 17:06:25 -0700511 BMCWEB_LOG_CRITICAL("frame was null?");
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800512 return NGHTTP2_ERR_CALLBACK_FAILURE;
513 }
514 return userPtrToSelf(userData).onBeginHeadersCallback(*frame);
515 }
516
517 static void afterWriteBuffer(const std::shared_ptr<self_type>& self,
518 const boost::system::error_code& ec,
519 size_t sendLength)
520 {
521 self->isWriting = false;
Ed Tanous62598e32023-07-17 17:06:25 -0700522 BMCWEB_LOG_DEBUG("Sent {}", sendLength);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800523 if (ec)
524 {
525 self->close();
526 return;
527 }
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800528 self->writeBuffer();
529 }
530
531 void writeBuffer()
532 {
533 if (isWriting)
534 {
535 return;
536 }
Ed Tanousd0882182024-01-26 23:45:25 -0800537 std::span<const uint8_t> data = ngSession.memSend();
538 if (data.empty())
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800539 {
540 return;
541 }
542 isWriting = true;
Ed Tanous325310d2024-03-15 09:05:04 -0700543 boost::asio::async_write(
544 adaptor, boost::asio::const_buffer(data.data(), data.size()),
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800545 std::bind_front(afterWriteBuffer, shared_from_this()));
546 }
547
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800548 void close()
549 {
550 if constexpr (std::is_same_v<Adaptor,
Ed Tanous003301a2024-04-16 09:59:19 -0700551 boost::asio::ssl::stream<
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800552 boost::asio::ip::tcp::socket>>)
553 {
554 adaptor.next_layer().close();
555 }
556 else
557 {
558 adaptor.close();
559 }
560 }
561
Ed Tanousd0882182024-01-26 23:45:25 -0800562 void afterDoRead(const std::shared_ptr<self_type>& /*self*/,
563 const boost::system::error_code& ec,
564 size_t bytesTransferred)
565 {
566 BMCWEB_LOG_DEBUG("{} async_read_some {} Bytes", logPtr(this),
567 bytesTransferred);
568
569 if (ec)
570 {
571 BMCWEB_LOG_ERROR("{} Error while reading: {}", logPtr(this),
572 ec.message());
573 close();
574 BMCWEB_LOG_DEBUG("{} from read(1)", logPtr(this));
575 return;
576 }
577 std::span<uint8_t> bufferSpan{inBuffer.data(), bytesTransferred};
578
579 ssize_t readLen = ngSession.memRecv(bufferSpan);
580 if (readLen < 0)
581 {
582 BMCWEB_LOG_ERROR("nghttp2_session_mem_recv returned {}", readLen);
583 close();
584 return;
585 }
586 writeBuffer();
587
588 doRead();
589 }
590
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800591 void doRead()
592 {
Ed Tanous62598e32023-07-17 17:06:25 -0700593 BMCWEB_LOG_DEBUG("{} doRead", logPtr(this));
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800594 adaptor.async_read_some(
Ed Tanousd0882182024-01-26 23:45:25 -0800595 boost::asio::buffer(inBuffer),
596 std::bind_front(&self_type::afterDoRead, this, shared_from_this()));
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800597 }
598
599 // A mapping from http2 stream ID to Stream Data
Ed Tanous52e31622024-01-23 16:31:11 -0800600 std::map<int32_t, Http2StreamData> streams;
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800601
Ed Tanousd0882182024-01-26 23:45:25 -0800602 std::array<uint8_t, 8192> inBuffer{};
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800603
604 Adaptor adaptor;
605 bool isWriting = false;
606
607 nghttp2_session ngSession;
608
609 Handler* handler;
610 std::function<std::string()>& getCachedDateStr;
611
612 using std::enable_shared_from_this<
613 HTTP2Connection<Adaptor, Handler>>::shared_from_this;
614
615 using std::enable_shared_from_this<
616 HTTP2Connection<Adaptor, Handler>>::weak_from_this;
617};
618} // namespace crow