blob: 730bdce51f5db63c1e7b915c55d5c5bf74ec2c7c [file] [log] [blame]
V-Sanjana88ada3b2023-04-13 15:18:31 +05301#pragma once
V-Sanjana88ada3b2023-04-13 15:18:31 +05302#include "http_request.hpp"
3#include "http_response.hpp"
4
V-Sanjana88ada3b2023-04-13 15:18:31 +05305#include <boost/asio/buffer.hpp>
6#include <boost/asio/steady_timer.hpp>
7#include <boost/beast/core/multi_buffer.hpp>
V-Sanjana88ada3b2023-04-13 15:18:31 +05308#include <boost/beast/websocket.hpp>
9
10#include <array>
Ed Tanous8f79c5b2024-01-30 15:56:37 -080011#include <cstddef>
V-Sanjana88ada3b2023-04-13 15:18:31 +053012#include <functional>
Ed Tanous8f79c5b2024-01-30 15:56:37 -080013#include <optional>
V-Sanjana88ada3b2023-04-13 15:18:31 +053014
15namespace crow
16{
17
18namespace sse_socket
19{
V-Sanjana88ada3b2023-04-13 15:18:31 +053020struct Connection : std::enable_shared_from_this<Connection>
21{
22 public:
Ed Tanous6fde95f2023-06-01 07:33:34 -070023 Connection() = default;
V-Sanjana88ada3b2023-04-13 15:18:31 +053024
25 Connection(const Connection&) = delete;
26 Connection(Connection&&) = delete;
27 Connection& operator=(const Connection&) = delete;
28 Connection& operator=(const Connection&&) = delete;
29 virtual ~Connection() = default;
30
31 virtual boost::asio::io_context& getIoContext() = 0;
V-Sanjana88ada3b2023-04-13 15:18:31 +053032 virtual void close(std::string_view msg = "quit") = 0;
33 virtual void sendEvent(std::string_view id, std::string_view msg) = 0;
V-Sanjana88ada3b2023-04-13 15:18:31 +053034};
35
36template <typename Adaptor>
37class ConnectionImpl : public Connection
38{
39 public:
Ed Tanous8f79c5b2024-01-30 15:56:37 -080040 ConnectionImpl(boost::asio::io_context& ioIn, Adaptor&& adaptorIn,
Ed Tanous6fde95f2023-06-01 07:33:34 -070041 std::function<void(Connection&)> openHandlerIn,
42 std::function<void(Connection&)> closeHandlerIn) :
43 adaptor(std::move(adaptorIn)),
Ed Tanous8f79c5b2024-01-30 15:56:37 -080044 ioc(ioIn), timer(ioc), openHandler(std::move(openHandlerIn)),
V-Sanjana88ada3b2023-04-13 15:18:31 +053045 closeHandler(std::move(closeHandlerIn))
Ed Tanous8f79c5b2024-01-30 15:56:37 -080046
V-Sanjana88ada3b2023-04-13 15:18:31 +053047 {
Ed Tanous62598e32023-07-17 17:06:25 -070048 BMCWEB_LOG_DEBUG("SseConnectionImpl: SSE constructor {}", logPtr(this));
V-Sanjana88ada3b2023-04-13 15:18:31 +053049 }
50
51 ConnectionImpl(const ConnectionImpl&) = delete;
52 ConnectionImpl(const ConnectionImpl&&) = delete;
53 ConnectionImpl& operator=(const ConnectionImpl&) = delete;
54 ConnectionImpl& operator=(const ConnectionImpl&&) = delete;
55
56 ~ConnectionImpl() override
57 {
Ed Tanous62598e32023-07-17 17:06:25 -070058 BMCWEB_LOG_DEBUG("SSE ConnectionImpl: SSE destructor {}", logPtr(this));
V-Sanjana88ada3b2023-04-13 15:18:31 +053059 }
60
61 boost::asio::io_context& getIoContext() override
62 {
63 return static_cast<boost::asio::io_context&>(
64 adaptor.get_executor().context());
65 }
66
67 void start()
68 {
Ed Tanous6fde95f2023-06-01 07:33:34 -070069 if (!openHandler)
V-Sanjana88ada3b2023-04-13 15:18:31 +053070 {
Ed Tanous62598e32023-07-17 17:06:25 -070071 BMCWEB_LOG_CRITICAL("No open handler???");
Ed Tanous6fde95f2023-06-01 07:33:34 -070072 return;
V-Sanjana88ada3b2023-04-13 15:18:31 +053073 }
Ed Tanous6fde95f2023-06-01 07:33:34 -070074 openHandler(*this);
Ed Tanous8f79c5b2024-01-30 15:56:37 -080075 sendSSEHeader();
V-Sanjana88ada3b2023-04-13 15:18:31 +053076 }
77
78 void close(const std::string_view msg) override
79 {
Ed Tanous8f79c5b2024-01-30 15:56:37 -080080 BMCWEB_LOG_DEBUG("Closing connection with reason {}", msg);
V-Sanjana88ada3b2023-04-13 15:18:31 +053081 // send notification to handler for cleanup
82 if (closeHandler)
83 {
Ed Tanous6fde95f2023-06-01 07:33:34 -070084 closeHandler(*this);
V-Sanjana88ada3b2023-04-13 15:18:31 +053085 }
Ed Tanous62598e32023-07-17 17:06:25 -070086 BMCWEB_LOG_DEBUG("Closing SSE connection {} - {}", logPtr(this), msg);
Ed Tanous6fde95f2023-06-01 07:33:34 -070087 boost::beast::get_lowest_layer(adaptor).close();
V-Sanjana88ada3b2023-04-13 15:18:31 +053088 }
89
Ed Tanous6fde95f2023-06-01 07:33:34 -070090 void sendSSEHeader()
V-Sanjana88ada3b2023-04-13 15:18:31 +053091 {
Ed Tanous62598e32023-07-17 17:06:25 -070092 BMCWEB_LOG_DEBUG("Starting SSE connection");
Ed Tanous8f79c5b2024-01-30 15:56:37 -080093
Ed Tanous6fde95f2023-06-01 07:33:34 -070094 res.set(boost::beast::http::field::content_type, "text/event-stream");
Ed Tanous8ece0e42024-01-02 13:16:50 -080095 boost::beast::http::response_serializer<BodyType>& serial =
Ed Tanous8f79c5b2024-01-30 15:56:37 -080096 serializer.emplace(res);
V-Sanjana88ada3b2023-04-13 15:18:31 +053097
98 boost::beast::http::async_write_header(
Ed Tanous8ece0e42024-01-02 13:16:50 -080099 adaptor, serial,
V-Sanjana88ada3b2023-04-13 15:18:31 +0530100 std::bind_front(&ConnectionImpl::sendSSEHeaderCallback, this,
101 shared_from_this()));
102 }
103
104 void sendSSEHeaderCallback(const std::shared_ptr<Connection>& /*self*/,
Ed Tanous6fde95f2023-06-01 07:33:34 -0700105 const boost::system::error_code& ec,
106 size_t /*bytesSent*/)
V-Sanjana88ada3b2023-04-13 15:18:31 +0530107 {
Ed Tanous6fde95f2023-06-01 07:33:34 -0700108 serializer.reset();
V-Sanjana88ada3b2023-04-13 15:18:31 +0530109 if (ec)
110 {
Ed Tanous62598e32023-07-17 17:06:25 -0700111 BMCWEB_LOG_ERROR("Error sending header{}", ec);
V-Sanjana88ada3b2023-04-13 15:18:31 +0530112 close("async_write_header failed");
113 return;
114 }
Ed Tanous62598e32023-07-17 17:06:25 -0700115 BMCWEB_LOG_DEBUG("SSE header sent - Connection established");
V-Sanjana88ada3b2023-04-13 15:18:31 +0530116
V-Sanjana88ada3b2023-04-13 15:18:31 +0530117 // SSE stream header sent, So let us setup monitor.
118 // Any read data on this stream will be error in case of SSE.
Ed Tanous8f79c5b2024-01-30 15:56:37 -0800119 adaptor.async_read_some(boost::asio::buffer(buffer),
120 std::bind_front(&ConnectionImpl::afterReadError,
121 this, shared_from_this()));
V-Sanjana88ada3b2023-04-13 15:18:31 +0530122 }
123
Ed Tanous6fde95f2023-06-01 07:33:34 -0700124 void afterReadError(const std::shared_ptr<Connection>& /*self*/,
Ed Tanous8f79c5b2024-01-30 15:56:37 -0800125 const boost::system::error_code& ec, size_t bytesRead)
V-Sanjana88ada3b2023-04-13 15:18:31 +0530126 {
Ed Tanous8f79c5b2024-01-30 15:56:37 -0800127 BMCWEB_LOG_DEBUG("Read {}", bytesRead);
Ed Tanous6fde95f2023-06-01 07:33:34 -0700128 if (ec == boost::asio::error::operation_aborted)
129 {
130 return;
131 }
V-Sanjana88ada3b2023-04-13 15:18:31 +0530132 if (ec)
133 {
Ed Tanous62598e32023-07-17 17:06:25 -0700134 BMCWEB_LOG_ERROR("Read error: {}", ec);
V-Sanjana88ada3b2023-04-13 15:18:31 +0530135 }
136
Ed Tanous6fde95f2023-06-01 07:33:34 -0700137 close("Close SSE connection");
V-Sanjana88ada3b2023-04-13 15:18:31 +0530138 }
139
140 void doWrite()
141 {
V-Sanjana88ada3b2023-04-13 15:18:31 +0530142 if (doingWrite)
143 {
144 return;
145 }
146 if (inputBuffer.size() == 0)
147 {
Ed Tanous62598e32023-07-17 17:06:25 -0700148 BMCWEB_LOG_DEBUG("inputBuffer is empty... Bailing out");
V-Sanjana88ada3b2023-04-13 15:18:31 +0530149 return;
150 }
Ed Tanous6fde95f2023-06-01 07:33:34 -0700151 startTimeout();
V-Sanjana88ada3b2023-04-13 15:18:31 +0530152 doingWrite = true;
153
154 adaptor.async_write_some(
155 inputBuffer.data(),
156 std::bind_front(&ConnectionImpl::doWriteCallback, this,
Ed Tanous8f79c5b2024-01-30 15:56:37 -0800157 shared_from_this()));
V-Sanjana88ada3b2023-04-13 15:18:31 +0530158 }
159
Ed Tanous8f79c5b2024-01-30 15:56:37 -0800160 void doWriteCallback(const std::shared_ptr<Connection>& /*self*/,
V-Sanjana88ada3b2023-04-13 15:18:31 +0530161 const boost::beast::error_code& ec,
Ed Tanous6fde95f2023-06-01 07:33:34 -0700162 size_t bytesTransferred)
V-Sanjana88ada3b2023-04-13 15:18:31 +0530163 {
Ed Tanous6fde95f2023-06-01 07:33:34 -0700164 timer.cancel();
V-Sanjana88ada3b2023-04-13 15:18:31 +0530165 doingWrite = false;
166 inputBuffer.consume(bytesTransferred);
167
168 if (ec == boost::asio::error::eof)
169 {
Ed Tanous62598e32023-07-17 17:06:25 -0700170 BMCWEB_LOG_ERROR("async_write_some() SSE stream closed");
V-Sanjana88ada3b2023-04-13 15:18:31 +0530171 close("SSE stream closed");
172 return;
173 }
174
175 if (ec)
176 {
Ed Tanous62598e32023-07-17 17:06:25 -0700177 BMCWEB_LOG_ERROR("async_write_some() failed: {}", ec.message());
V-Sanjana88ada3b2023-04-13 15:18:31 +0530178 close("async_write_some failed");
179 return;
180 }
Ed Tanous62598e32023-07-17 17:06:25 -0700181 BMCWEB_LOG_DEBUG("async_write_some() bytes transferred: {}",
182 bytesTransferred);
V-Sanjana88ada3b2023-04-13 15:18:31 +0530183
184 doWrite();
185 }
186
V-Sanjana88ada3b2023-04-13 15:18:31 +0530187 void sendEvent(std::string_view id, std::string_view msg) override
188 {
189 if (msg.empty())
190 {
Ed Tanous62598e32023-07-17 17:06:25 -0700191 BMCWEB_LOG_DEBUG("Empty data, bailing out.");
V-Sanjana88ada3b2023-04-13 15:18:31 +0530192 return;
193 }
194
Ed Tanous6fde95f2023-06-01 07:33:34 -0700195 dataFormat(id, msg);
V-Sanjana88ada3b2023-04-13 15:18:31 +0530196
197 doWrite();
198 }
199
Ed Tanous6fde95f2023-06-01 07:33:34 -0700200 void dataFormat(std::string_view id, std::string_view msg)
V-Sanjana88ada3b2023-04-13 15:18:31 +0530201 {
Ed Tanous8f79c5b2024-01-30 15:56:37 -0800202 constexpr size_t bufferLimit = 10485760U; // 10MB
203 if (id.size() + msg.size() + inputBuffer.size() >= bufferLimit)
204 {
205 BMCWEB_LOG_ERROR("SSE Buffer overflow while waiting for client");
206 close("Buffer overflow");
207 return;
208 }
V-Sanjana88ada3b2023-04-13 15:18:31 +0530209 std::string rawData;
210 if (!id.empty())
211 {
212 rawData += "id: ";
Ed Tanous6fde95f2023-06-01 07:33:34 -0700213 rawData.append(id);
V-Sanjana88ada3b2023-04-13 15:18:31 +0530214 rawData += "\n";
215 }
216
217 rawData += "data: ";
218 for (char character : msg)
219 {
220 rawData += character;
221 if (character == '\n')
222 {
223 rawData += "data: ";
224 }
225 }
226 rawData += "\n\n";
227
228 boost::asio::buffer_copy(inputBuffer.prepare(rawData.size()),
229 boost::asio::buffer(rawData));
230 inputBuffer.commit(rawData.size());
231 }
232
Ed Tanous6fde95f2023-06-01 07:33:34 -0700233 void startTimeout()
V-Sanjana88ada3b2023-04-13 15:18:31 +0530234 {
V-Sanjana88ada3b2023-04-13 15:18:31 +0530235 std::weak_ptr<Connection> weakSelf = weak_from_this();
236 timer.expires_after(std::chrono::seconds(30));
237 timer.async_wait(std::bind_front(&ConnectionImpl::onTimeoutCallback,
238 this, weak_from_this()));
239 }
240
241 void onTimeoutCallback(const std::weak_ptr<Connection>& weakSelf,
Ed Tanous6fde95f2023-06-01 07:33:34 -0700242 const boost::system::error_code& ec)
V-Sanjana88ada3b2023-04-13 15:18:31 +0530243 {
244 std::shared_ptr<Connection> self = weakSelf.lock();
245 if (!self)
246 {
Ed Tanous62598e32023-07-17 17:06:25 -0700247 BMCWEB_LOG_CRITICAL("{} Failed to capture connection",
248 logPtr(self.get()));
V-Sanjana88ada3b2023-04-13 15:18:31 +0530249 return;
250 }
251
252 if (ec == boost::asio::error::operation_aborted)
253 {
Ed Tanous8f79c5b2024-01-30 15:56:37 -0800254 BMCWEB_LOG_DEBUG("Timer operation aborted");
Ed Tanous8ece0e42024-01-02 13:16:50 -0800255 // Canceled wait means the path succeeded.
V-Sanjana88ada3b2023-04-13 15:18:31 +0530256 return;
257 }
258 if (ec)
259 {
Ed Tanous62598e32023-07-17 17:06:25 -0700260 BMCWEB_LOG_CRITICAL("{} timer failed {}", logPtr(self.get()), ec);
V-Sanjana88ada3b2023-04-13 15:18:31 +0530261 }
262
Ed Tanous8f79c5b2024-01-30 15:56:37 -0800263 BMCWEB_LOG_WARNING("{} Connection timed out, closing",
Ed Tanous62598e32023-07-17 17:06:25 -0700264 logPtr(self.get()));
V-Sanjana88ada3b2023-04-13 15:18:31 +0530265
266 self->close("closing connection");
267 }
268
269 private:
Ed Tanous8f79c5b2024-01-30 15:56:37 -0800270 std::array<char, 1> buffer{};
V-Sanjana88ada3b2023-04-13 15:18:31 +0530271 boost::beast::multi_buffer inputBuffer;
272
Ed Tanous8f79c5b2024-01-30 15:56:37 -0800273 Adaptor adaptor;
274
275 using BodyType = bmcweb::FileBody;
276 boost::beast::http::response<BodyType> res;
277 std::optional<boost::beast::http::response_serializer<BodyType>> serializer;
278 boost::asio::io_context& ioc;
Ed Tanous6fde95f2023-06-01 07:33:34 -0700279 boost::asio::steady_timer timer;
V-Sanjana88ada3b2023-04-13 15:18:31 +0530280 bool doingWrite = false;
V-Sanjana88ada3b2023-04-13 15:18:31 +0530281
Ed Tanous6fde95f2023-06-01 07:33:34 -0700282 std::function<void(Connection&)> openHandler;
283 std::function<void(Connection&)> closeHandler;
V-Sanjana88ada3b2023-04-13 15:18:31 +0530284};
285} // namespace sse_socket
286} // namespace crow