diff options
| author | Per Larsson <[email protected]> | 2022-02-21 15:14:11 +0100 |
|---|---|---|
| committer | Per Larsson <[email protected]> | 2022-02-21 15:14:11 +0100 |
| commit | db1c9605e3afbaf86f4231ba4eb7976d896f286b (patch) | |
| tree | 54b451da4247c69575ff1a05ed006ecef3905c85 /zenhttp/include | |
| parent | If open(O_CREAT) is used then a file mode must be given (diff) | |
| parent | Removed optional offset for GetView. (diff) | |
| download | zen-db1c9605e3afbaf86f4231ba4eb7976d896f286b.tar.xz zen-db1c9605e3afbaf86f4231ba4eb7976d896f286b.zip | |
Initial support for websockets.
Diffstat (limited to 'zenhttp/include')
| -rw-r--r-- | zenhttp/include/zenhttp/websocket.h | 224 |
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 |