aboutsummaryrefslogtreecommitdiff
path: root/src/zenhttp/servers/wsframecodec.h
blob: 2d90b6fa1873c62429af7693c7bc90c1a4c6abfa (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include <zenhttp/websocket.h>

#include <cstddef>
#include <cstdint>
#include <optional>
#include <span>
#include <string>
#include <string_view>
#include <vector>

namespace zen {

/**
 * Result of attempting to parse a single WebSocket frame from a byte buffer
 */
struct WsFrameParseResult
{
	bool				 IsValid	   = false;	 // true if a complete frame was successfully parsed
	size_t				 BytesConsumed = 0;		 // number of bytes consumed from the input buffer
	WebSocketOpcode		 Opcode		   = WebSocketOpcode::kText;
	bool				 Fin		   = false;
	std::vector<uint8_t> Payload;
};

/**
 * RFC 6455 WebSocket frame codec
 *
 * Provides static helpers for parsing client-to-server frames (which are
 * always masked) and building server-to-client frames (which are never masked).
 */
struct WsFrameCodec
{
	/**
	 * Try to parse one complete frame from the front of the buffer.
	 *
	 * Returns a result with IsValid == false and BytesConsumed == 0 when
	 * there is not enough data yet.  The caller should accumulate more data
	 * and retry.
	 */
	static WsFrameParseResult TryParseFrame(const uint8_t* Data, size_t Size);

	/**
	 * Build a server-to-client frame (no masking)
	 */
	static std::vector<uint8_t> BuildFrame(WebSocketOpcode Opcode, std::span<const uint8_t> Payload);

	/**
	 * Build a close frame with a status code and optional reason string
	 */
	static std::vector<uint8_t> BuildCloseFrame(uint16_t Code, std::string_view Reason = {});

	/**
	 * Build a client-to-server frame (with masking per RFC 6455)
	 */
	static std::vector<uint8_t> BuildMaskedFrame(WebSocketOpcode Opcode, std::span<const uint8_t> Payload);

	/**
	 * Build a masked close frame with status code and optional reason
	 */
	static std::vector<uint8_t> BuildMaskedCloseFrame(uint16_t Code, std::string_view Reason = {});

	/**
	 * Compute the Sec-WebSocket-Accept value per RFC 6455 section 4.2.2
	 *
	 * accept = Base64(SHA1(clientKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"))
	 */
	static std::string ComputeAcceptKey(std::string_view ClientKey);
};

}  // namespace zen