aboutsummaryrefslogtreecommitdiff
path: root/zenhttp/include
diff options
context:
space:
mode:
authorPer Larsson <[email protected]>2022-02-21 15:14:11 +0100
committerPer Larsson <[email protected]>2022-02-21 15:14:11 +0100
commitdb1c9605e3afbaf86f4231ba4eb7976d896f286b (patch)
tree54b451da4247c69575ff1a05ed006ecef3905c85 /zenhttp/include
parentIf open(O_CREAT) is used then a file mode must be given (diff)
parentRemoved optional offset for GetView. (diff)
downloadzen-db1c9605e3afbaf86f4231ba4eb7976d896f286b.tar.xz
zen-db1c9605e3afbaf86f4231ba4eb7976d896f286b.zip
Initial support for websockets.
Diffstat (limited to 'zenhttp/include')
-rw-r--r--zenhttp/include/zenhttp/websocket.h224
1 files changed, 224 insertions, 0 deletions
diff --git a/zenhttp/include/zenhttp/websocket.h b/zenhttp/include/zenhttp/websocket.h
new file mode 100644
index 000000000..a514e6002
--- /dev/null
+++ b/zenhttp/include/zenhttp/websocket.h
@@ -0,0 +1,224 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#include <zencore/compactbinarypackage.h>
+#include <zencore/memory.h>
+
+#include <compare>
+#include <functional>
+#include <future>
+#include <memory>
+#include <optional>
+
+#pragma once
+
+namespace asio {
+class io_context;
+}
+
+namespace zen {
+
+class BinaryWriter;
+
+/**
+ * A unique socket ID.
+ */
+class WebSocketId
+{
+ static std::atomic_uint32_t NextId;
+
+public:
+ WebSocketId() = default;
+
+ uint32_t Value() const { return m_Value; }
+
+ auto operator<=>(const WebSocketId&) const = default;
+
+ static WebSocketId New() { return WebSocketId(NextId.fetch_add(1)); }
+
+private:
+ WebSocketId(uint32_t Value) : m_Value(Value) {}
+
+ uint32_t m_Value{};
+};
+
+/**
+ * Type of web socket message.
+ */
+enum class WebSocketMessageType : uint8_t
+{
+ kInvalid,
+ kNotification,
+ kRequest,
+ kResponse
+};
+
+/**
+ * Web socket message.
+ */
+class WebSocketMessage
+{
+ struct Header
+ {
+ static constexpr uint32_t HeaderMagic = 0x7a776d68; // zwmh
+
+ uint64_t MessageSize{};
+ uint32_t Magic{HeaderMagic};
+ uint32_t CorrelationId{};
+ uint32_t Crc32{};
+ WebSocketMessageType MessageType{};
+ uint8_t Reserved[3] = {0};
+
+ bool IsValid() const;
+ };
+
+ static_assert(sizeof Header == 24);
+
+ static std::atomic_uint32_t NextCorrelationId;
+
+public:
+ static constexpr size_t HeaderSize = sizeof(Header);
+
+ WebSocketMessage() = default;
+
+ WebSocketId SocketId() const { return m_SocketId; }
+ void SetSocketId(WebSocketId Id) { m_SocketId = Id; }
+ void SetMessageType(WebSocketMessageType MessageType);
+ WebSocketMessageType MessageType() const { return m_Header.MessageType; }
+ uint64_t MessageSize() const { return m_Header.MessageSize; }
+ void SetCorrelationId(uint32_t Id) { m_Header.CorrelationId = Id; }
+ uint32_t CorrelationId() const { return m_Header.CorrelationId; }
+
+ const CbPackage& Body() const { return m_Body.value(); }
+ void SetBody(CbPackage&& Body);
+ void SetBody(CbObject&& Body);
+ bool HasBody() const { return m_Body.has_value(); }
+
+ void Save(BinaryWriter& Writer);
+ bool TryLoadHeader(MemoryView Memory);
+
+ bool IsValid() const { return m_Header.MessageType != WebSocketMessageType::kInvalid; }
+
+private:
+ Header m_Header{};
+ WebSocketId m_SocketId{};
+ std::optional<CbPackage> m_Body;
+};
+
+class WebSocketServer;
+
+/**
+ * Base class for handling web socket requests and notifications from connected client(s).
+ */
+class WebSocketService
+{
+public:
+ virtual ~WebSocketService() = default;
+
+ void Configure(WebSocketServer& Server);
+
+ virtual bool HandleRequest(const WebSocketMessage&) { ZEN_ASSERT(false); }
+ virtual void HandleNotification(const WebSocketMessage&) { ZEN_ASSERT(false); }
+
+protected:
+ WebSocketService() = default;
+
+ virtual void RegisterHandlers(WebSocketServer& Server) = 0;
+
+ WebSocketServer& SocketServer()
+ {
+ ZEN_ASSERT(m_SocketServer);
+ return *m_SocketServer;
+ }
+
+private:
+ WebSocketServer* m_SocketServer{};
+};
+
+/**
+ * Server options.
+ */
+struct WebSocketServerOptions
+{
+ uint16_t Port = 2337;
+ uint32_t ThreadCount = 1;
+};
+
+/**
+ * The web socket server manages client connections and routing of requests and notifications.
+ */
+class WebSocketServer
+{
+public:
+ virtual ~WebSocketServer() = default;
+
+ virtual bool Run() = 0;
+ virtual void Shutdown() = 0;
+
+ virtual void RegisterService(WebSocketService& Service) = 0;
+ virtual void RegisterNotificationHandler(std::string_view Key, WebSocketService& Service) = 0;
+ virtual void RegisterRequestHandler(std::string_view Key, WebSocketService& Service) = 0;
+
+ virtual void SendNotification(WebSocketMessage&& Notification) = 0;
+ virtual void SendResponse(WebSocketMessage&& Response) = 0;
+
+ static std::unique_ptr<WebSocketServer> Create(const WebSocketServerOptions& Options);
+};
+
+/**
+ * The state of the web socket.
+ */
+enum class WebSocketState : uint32_t
+{
+ kNone,
+ kHandshaking,
+ kConnected,
+ kDisconnected,
+ kError
+};
+
+/**
+ * Type of web socket client event.
+ */
+enum class WebSocketEvent : uint32_t
+{
+ kConnected,
+ kDisconnected,
+ kError
+};
+
+/**
+ * Web socket client connection info.
+ */
+struct WebSocketConnectInfo
+{
+ std::string Host;
+ int16_t Port{8848};
+ std::string Endpoint;
+ std::vector<std::string> Protocols;
+ uint16_t Version{13};
+};
+
+/**
+ * A connection to a web socket server for sending requests and listening for notifications.
+ */
+class WebSocketClient
+{
+public:
+ using EventCallback = std::function<void()>;
+ using NotificationCallback = std::function<void(WebSocketMessage&&)>;
+
+ virtual ~WebSocketClient() = default;
+
+ virtual std::future<bool> Connect(const WebSocketConnectInfo& Info) = 0;
+ virtual void Disconnect() = 0;
+ virtual bool IsConnected() const = 0;
+ virtual WebSocketState State() const = 0;
+
+ virtual std::future<WebSocketMessage> SendRequest(WebSocketMessage&& Request) = 0;
+ virtual void OnNotification(NotificationCallback&& Cb) = 0;
+ virtual void OnEvent(WebSocketEvent Evt, EventCallback&& Cb) = 0;
+
+ static std::shared_ptr<WebSocketClient> Create(asio::io_context& IoCtx);
+};
+
+} // namespace zen