blob: 1b644fc84e66a31cde902c8dd51317928b7bbd7b [file] [log] [blame]
V-Sanjana88ada3b2023-04-13 15:18:31 +05301#pragma once
Ed Tanousb2896142024-01-31 15:25:47 -08002#include "http_body.hpp"
V-Sanjana88ada3b2023-04-13 15:18:31 +05303#include "http_request.hpp"
4#include "http_response.hpp"
5
V-Sanjana88ada3b2023-04-13 15:18:31 +05306#include <boost/asio/buffer.hpp>
7#include <boost/asio/steady_timer.hpp>
8#include <boost/beast/core/multi_buffer.hpp>
V-Sanjana88ada3b2023-04-13 15:18:31 +05309#include <boost/beast/websocket.hpp>
10
11#include <array>
Ed Tanous8f79c5b2024-01-30 15:56:37 -080012#include <cstddef>
V-Sanjana88ada3b2023-04-13 15:18:31 +053013#include <functional>
Ed Tanous8f79c5b2024-01-30 15:56:37 -080014#include <optional>
V-Sanjana88ada3b2023-04-13 15:18:31 +053015
16namespace crow
17{
18
19namespace sse_socket
20{
Ed Tanous93cf0ac2024-03-28 00:35:13 -070021struct Connection : public std::enable_shared_from_this<Connection>
V-Sanjana88ada3b2023-04-13 15:18:31 +053022{
23 public:
Ed Tanous6fde95f2023-06-01 07:33:34 -070024 Connection() = default;
V-Sanjana88ada3b2023-04-13 15:18:31 +053025
26 Connection(const Connection&) = delete;
27 Connection(Connection&&) = delete;
28 Connection& operator=(const Connection&) = delete;
29 Connection& operator=(const Connection&&) = delete;
30 virtual ~Connection() = default;
31
32 virtual boost::asio::io_context& getIoContext() = 0;
V-Sanjana88ada3b2023-04-13 15:18:31 +053033 virtual void close(std::string_view msg = "quit") = 0;
34 virtual void sendEvent(std::string_view id, std::string_view msg) = 0;
V-Sanjana88ada3b2023-04-13 15:18:31 +053035};
36
37template <typename Adaptor>
38class ConnectionImpl : public Connection
39{
40 public:
Ed Tanous93cf0ac2024-03-28 00:35:13 -070041 ConnectionImpl(Adaptor&& adaptorIn,
Ed Tanous6fde95f2023-06-01 07:33:34 -070042 std::function<void(Connection&)> openHandlerIn,
43 std::function<void(Connection&)> closeHandlerIn) :
44 adaptor(std::move(adaptorIn)),
Ed Tanous93cf0ac2024-03-28 00:35:13 -070045 timer(static_cast<boost::asio::io_context&>(
46 adaptor.get_executor().context())),
47 openHandler(std::move(openHandlerIn)),
V-Sanjana88ada3b2023-04-13 15:18:31 +053048 closeHandler(std::move(closeHandlerIn))
Ed Tanous8f79c5b2024-01-30 15:56:37 -080049
V-Sanjana88ada3b2023-04-13 15:18:31 +053050 {
Ed Tanous62598e32023-07-17 17:06:25 -070051 BMCWEB_LOG_DEBUG("SseConnectionImpl: SSE constructor {}", logPtr(this));
V-Sanjana88ada3b2023-04-13 15:18:31 +053052 }
53
54 ConnectionImpl(const ConnectionImpl&) = delete;
55 ConnectionImpl(const ConnectionImpl&&) = delete;
56 ConnectionImpl& operator=(const ConnectionImpl&) = delete;
57 ConnectionImpl& operator=(const ConnectionImpl&&) = delete;
58
59 ~ConnectionImpl() override
60 {
Ed Tanous62598e32023-07-17 17:06:25 -070061 BMCWEB_LOG_DEBUG("SSE ConnectionImpl: SSE destructor {}", logPtr(this));
V-Sanjana88ada3b2023-04-13 15:18:31 +053062 }
63
64 boost::asio::io_context& getIoContext() override
65 {
66 return static_cast<boost::asio::io_context&>(
67 adaptor.get_executor().context());
68 }
69
70 void start()
71 {
Ed Tanous6fde95f2023-06-01 07:33:34 -070072 if (!openHandler)
V-Sanjana88ada3b2023-04-13 15:18:31 +053073 {
Ed Tanous62598e32023-07-17 17:06:25 -070074 BMCWEB_LOG_CRITICAL("No open handler???");
Ed Tanous6fde95f2023-06-01 07:33:34 -070075 return;
V-Sanjana88ada3b2023-04-13 15:18:31 +053076 }
Ed Tanous6fde95f2023-06-01 07:33:34 -070077 openHandler(*this);
Ed Tanous8f79c5b2024-01-30 15:56:37 -080078 sendSSEHeader();
V-Sanjana88ada3b2023-04-13 15:18:31 +053079 }
80
81 void close(const std::string_view msg) override
82 {
Ed Tanous8f79c5b2024-01-30 15:56:37 -080083 BMCWEB_LOG_DEBUG("Closing connection with reason {}", msg);
V-Sanjana88ada3b2023-04-13 15:18:31 +053084 // send notification to handler for cleanup
85 if (closeHandler)
86 {
Ed Tanous6fde95f2023-06-01 07:33:34 -070087 closeHandler(*this);
V-Sanjana88ada3b2023-04-13 15:18:31 +053088 }
Ed Tanous62598e32023-07-17 17:06:25 -070089 BMCWEB_LOG_DEBUG("Closing SSE connection {} - {}", logPtr(this), msg);
Ed Tanous6fde95f2023-06-01 07:33:34 -070090 boost::beast::get_lowest_layer(adaptor).close();
V-Sanjana88ada3b2023-04-13 15:18:31 +053091 }
92
Ed Tanous6fde95f2023-06-01 07:33:34 -070093 void sendSSEHeader()
V-Sanjana88ada3b2023-04-13 15:18:31 +053094 {
Ed Tanous62598e32023-07-17 17:06:25 -070095 BMCWEB_LOG_DEBUG("Starting SSE connection");
Ed Tanous8f79c5b2024-01-30 15:56:37 -080096
Ed Tanous6fde95f2023-06-01 07:33:34 -070097 res.set(boost::beast::http::field::content_type, "text/event-stream");
Ed Tanous8ece0e42024-01-02 13:16:50 -080098 boost::beast::http::response_serializer<BodyType>& serial =
Ed Tanous8f79c5b2024-01-30 15:56:37 -080099 serializer.emplace(res);
V-Sanjana88ada3b2023-04-13 15:18:31 +0530100
101 boost::beast::http::async_write_header(
Ed Tanous8ece0e42024-01-02 13:16:50 -0800102 adaptor, serial,
V-Sanjana88ada3b2023-04-13 15:18:31 +0530103 std::bind_front(&ConnectionImpl::sendSSEHeaderCallback, this,
104 shared_from_this()));
105 }
106
107 void sendSSEHeaderCallback(const std::shared_ptr<Connection>& /*self*/,
Ed Tanous6fde95f2023-06-01 07:33:34 -0700108 const boost::system::error_code& ec,
109 size_t /*bytesSent*/)
V-Sanjana88ada3b2023-04-13 15:18:31 +0530110 {
Ed Tanous6fde95f2023-06-01 07:33:34 -0700111 serializer.reset();
V-Sanjana88ada3b2023-04-13 15:18:31 +0530112 if (ec)
113 {
Ed Tanous62598e32023-07-17 17:06:25 -0700114 BMCWEB_LOG_ERROR("Error sending header{}", ec);
V-Sanjana88ada3b2023-04-13 15:18:31 +0530115 close("async_write_header failed");
116 return;
117 }
Ed Tanous62598e32023-07-17 17:06:25 -0700118 BMCWEB_LOG_DEBUG("SSE header sent - Connection established");
V-Sanjana88ada3b2023-04-13 15:18:31 +0530119
V-Sanjana88ada3b2023-04-13 15:18:31 +0530120 // SSE stream header sent, So let us setup monitor.
121 // Any read data on this stream will be error in case of SSE.
Ed Tanous8f79c5b2024-01-30 15:56:37 -0800122 adaptor.async_read_some(boost::asio::buffer(buffer),
123 std::bind_front(&ConnectionImpl::afterReadError,
124 this, shared_from_this()));
V-Sanjana88ada3b2023-04-13 15:18:31 +0530125 }
126
Ed Tanous6fde95f2023-06-01 07:33:34 -0700127 void afterReadError(const std::shared_ptr<Connection>& /*self*/,
Ed Tanous8f79c5b2024-01-30 15:56:37 -0800128 const boost::system::error_code& ec, size_t bytesRead)
V-Sanjana88ada3b2023-04-13 15:18:31 +0530129 {
Ed Tanous8f79c5b2024-01-30 15:56:37 -0800130 BMCWEB_LOG_DEBUG("Read {}", bytesRead);
Ed Tanous6fde95f2023-06-01 07:33:34 -0700131 if (ec == boost::asio::error::operation_aborted)
132 {
133 return;
134 }
V-Sanjana88ada3b2023-04-13 15:18:31 +0530135 if (ec)
136 {
Ed Tanous62598e32023-07-17 17:06:25 -0700137 BMCWEB_LOG_ERROR("Read error: {}", ec);
V-Sanjana88ada3b2023-04-13 15:18:31 +0530138 }
139
Ed Tanous6fde95f2023-06-01 07:33:34 -0700140 close("Close SSE connection");
V-Sanjana88ada3b2023-04-13 15:18:31 +0530141 }
142
143 void doWrite()
144 {
V-Sanjana88ada3b2023-04-13 15:18:31 +0530145 if (doingWrite)
146 {
147 return;
148 }
149 if (inputBuffer.size() == 0)
150 {
Ed Tanous62598e32023-07-17 17:06:25 -0700151 BMCWEB_LOG_DEBUG("inputBuffer is empty... Bailing out");
V-Sanjana88ada3b2023-04-13 15:18:31 +0530152 return;
153 }
Ed Tanous6fde95f2023-06-01 07:33:34 -0700154 startTimeout();
V-Sanjana88ada3b2023-04-13 15:18:31 +0530155 doingWrite = true;
156
157 adaptor.async_write_some(
158 inputBuffer.data(),
159 std::bind_front(&ConnectionImpl::doWriteCallback, this,
Ed Tanous8f79c5b2024-01-30 15:56:37 -0800160 shared_from_this()));
V-Sanjana88ada3b2023-04-13 15:18:31 +0530161 }
162
Ed Tanous8f79c5b2024-01-30 15:56:37 -0800163 void doWriteCallback(const std::shared_ptr<Connection>& /*self*/,
V-Sanjana88ada3b2023-04-13 15:18:31 +0530164 const boost::beast::error_code& ec,
Ed Tanous6fde95f2023-06-01 07:33:34 -0700165 size_t bytesTransferred)
V-Sanjana88ada3b2023-04-13 15:18:31 +0530166 {
Ed Tanous6fde95f2023-06-01 07:33:34 -0700167 timer.cancel();
V-Sanjana88ada3b2023-04-13 15:18:31 +0530168 doingWrite = false;
169 inputBuffer.consume(bytesTransferred);
170
171 if (ec == boost::asio::error::eof)
172 {
Ed Tanous62598e32023-07-17 17:06:25 -0700173 BMCWEB_LOG_ERROR("async_write_some() SSE stream closed");
V-Sanjana88ada3b2023-04-13 15:18:31 +0530174 close("SSE stream closed");
175 return;
176 }
177
178 if (ec)
179 {
Ed Tanous62598e32023-07-17 17:06:25 -0700180 BMCWEB_LOG_ERROR("async_write_some() failed: {}", ec.message());
V-Sanjana88ada3b2023-04-13 15:18:31 +0530181 close("async_write_some failed");
182 return;
183 }
Ed Tanous62598e32023-07-17 17:06:25 -0700184 BMCWEB_LOG_DEBUG("async_write_some() bytes transferred: {}",
185 bytesTransferred);
V-Sanjana88ada3b2023-04-13 15:18:31 +0530186
187 doWrite();
188 }
189
V-Sanjana88ada3b2023-04-13 15:18:31 +0530190 void sendEvent(std::string_view id, std::string_view msg) override
191 {
192 if (msg.empty())
193 {
Ed Tanous62598e32023-07-17 17:06:25 -0700194 BMCWEB_LOG_DEBUG("Empty data, bailing out.");
V-Sanjana88ada3b2023-04-13 15:18:31 +0530195 return;
196 }
197
Ed Tanous6fde95f2023-06-01 07:33:34 -0700198 dataFormat(id, msg);
V-Sanjana88ada3b2023-04-13 15:18:31 +0530199
200 doWrite();
201 }
202
Ed Tanous6fde95f2023-06-01 07:33:34 -0700203 void dataFormat(std::string_view id, std::string_view msg)
V-Sanjana88ada3b2023-04-13 15:18:31 +0530204 {
Ed Tanous8f79c5b2024-01-30 15:56:37 -0800205 constexpr size_t bufferLimit = 10485760U; // 10MB
206 if (id.size() + msg.size() + inputBuffer.size() >= bufferLimit)
207 {
208 BMCWEB_LOG_ERROR("SSE Buffer overflow while waiting for client");
209 close("Buffer overflow");
210 return;
211 }
V-Sanjana88ada3b2023-04-13 15:18:31 +0530212 std::string rawData;
213 if (!id.empty())
214 {
215 rawData += "id: ";
Ed Tanous6fde95f2023-06-01 07:33:34 -0700216 rawData.append(id);
V-Sanjana88ada3b2023-04-13 15:18:31 +0530217 rawData += "\n";
218 }
219
220 rawData += "data: ";
221 for (char character : msg)
222 {
223 rawData += character;
224 if (character == '\n')
225 {
226 rawData += "data: ";
227 }
228 }
229 rawData += "\n\n";
230
Ed Tanous44106f32024-04-06 13:48:50 -0700231 size_t copied = boost::asio::buffer_copy(
232 inputBuffer.prepare(rawData.size()), boost::asio::buffer(rawData));
233 inputBuffer.commit(copied);
V-Sanjana88ada3b2023-04-13 15:18:31 +0530234 }
235
Ed Tanous6fde95f2023-06-01 07:33:34 -0700236 void startTimeout()
V-Sanjana88ada3b2023-04-13 15:18:31 +0530237 {
V-Sanjana88ada3b2023-04-13 15:18:31 +0530238 std::weak_ptr<Connection> weakSelf = weak_from_this();
239 timer.expires_after(std::chrono::seconds(30));
240 timer.async_wait(std::bind_front(&ConnectionImpl::onTimeoutCallback,
241 this, weak_from_this()));
242 }
243
244 void onTimeoutCallback(const std::weak_ptr<Connection>& weakSelf,
Ed Tanous6fde95f2023-06-01 07:33:34 -0700245 const boost::system::error_code& ec)
V-Sanjana88ada3b2023-04-13 15:18:31 +0530246 {
247 std::shared_ptr<Connection> self = weakSelf.lock();
248 if (!self)
249 {
Ed Tanous62598e32023-07-17 17:06:25 -0700250 BMCWEB_LOG_CRITICAL("{} Failed to capture connection",
251 logPtr(self.get()));
V-Sanjana88ada3b2023-04-13 15:18:31 +0530252 return;
253 }
254
255 if (ec == boost::asio::error::operation_aborted)
256 {
Ed Tanous8f79c5b2024-01-30 15:56:37 -0800257 BMCWEB_LOG_DEBUG("Timer operation aborted");
Ed Tanous8ece0e42024-01-02 13:16:50 -0800258 // Canceled wait means the path succeeded.
V-Sanjana88ada3b2023-04-13 15:18:31 +0530259 return;
260 }
261 if (ec)
262 {
Ed Tanous62598e32023-07-17 17:06:25 -0700263 BMCWEB_LOG_CRITICAL("{} timer failed {}", logPtr(self.get()), ec);
V-Sanjana88ada3b2023-04-13 15:18:31 +0530264 }
265
Ed Tanous8f79c5b2024-01-30 15:56:37 -0800266 BMCWEB_LOG_WARNING("{} Connection timed out, closing",
Ed Tanous62598e32023-07-17 17:06:25 -0700267 logPtr(self.get()));
V-Sanjana88ada3b2023-04-13 15:18:31 +0530268
269 self->close("closing connection");
270 }
271
272 private:
Ed Tanous8f79c5b2024-01-30 15:56:37 -0800273 std::array<char, 1> buffer{};
V-Sanjana88ada3b2023-04-13 15:18:31 +0530274 boost::beast::multi_buffer inputBuffer;
275
Ed Tanous8f79c5b2024-01-30 15:56:37 -0800276 Adaptor adaptor;
277
Ed Tanousb2896142024-01-31 15:25:47 -0800278 using BodyType = bmcweb::HttpBody;
Ed Tanous8f79c5b2024-01-30 15:56:37 -0800279 boost::beast::http::response<BodyType> res;
280 std::optional<boost::beast::http::response_serializer<BodyType>> serializer;
Ed Tanous6fde95f2023-06-01 07:33:34 -0700281 boost::asio::steady_timer timer;
V-Sanjana88ada3b2023-04-13 15:18:31 +0530282 bool doingWrite = false;
V-Sanjana88ada3b2023-04-13 15:18:31 +0530283
Ed Tanous6fde95f2023-06-01 07:33:34 -0700284 std::function<void(Connection&)> openHandler;
285 std::function<void(Connection&)> closeHandler;
V-Sanjana88ada3b2023-04-13 15:18:31 +0530286};
287} // namespace sse_socket
288} // namespace crow