96 lines
2.4 KiB
C
96 lines
2.4 KiB
C
|
#pragma once
|
||
|
|
||
|
#include <functional>
|
||
|
#include <string>
|
||
|
#include "net/http_server.h"
|
||
|
#include "net/sinks.h"
|
||
|
|
||
|
namespace net {
|
||
|
|
||
|
enum class WebSocketClose : uint16_t {
|
||
|
NORMAL = 1000,
|
||
|
GOING_AWAY = 1001,
|
||
|
PROTOCOL_ERROR = 1002,
|
||
|
UNSUPPORTED_DATA = 1003,
|
||
|
INVALID_DATA = 1007,
|
||
|
POLICY_VIOLATION = 1008,
|
||
|
MESSAGE_TOO_LONG = 1009,
|
||
|
MISSING_EXTENSION = 1010,
|
||
|
INTERNAL_ERROR = 1011,
|
||
|
SERVICE_RESTART = 1012,
|
||
|
TRY_AGAIN_LATER = 1013,
|
||
|
BAD_GATEWAY = 1014,
|
||
|
|
||
|
NO_STATUS = 1005,
|
||
|
ABNORMAL = 1006,
|
||
|
};
|
||
|
|
||
|
// RFC 6455
|
||
|
class WebSocketServer {
|
||
|
public:
|
||
|
static WebSocketServer *CreateAsUpgrade(const http::Request &request);
|
||
|
|
||
|
// TODO: Doesn't support fragmented data yet.
|
||
|
void Send(const std::string &str);
|
||
|
void Send(const std::vector<uint8_t> &payload);
|
||
|
void Ping(const std::vector<uint8_t> &payload = {});
|
||
|
void Pong(const std::vector<uint8_t> &payload = {});
|
||
|
void Close(WebSocketClose reason = WebSocketClose::GOING_AWAY);
|
||
|
|
||
|
// Note: may interrupt early. Call in a loop.
|
||
|
bool Process(float timeout = -1.0f);
|
||
|
|
||
|
void SetTextHandler(std::function<void(const std::string &)> func) {
|
||
|
text_ = func;
|
||
|
}
|
||
|
void SetBinaryHandler(std::function<void(const std::vector<uint8_t> &)> func) {
|
||
|
binary_ = func;
|
||
|
}
|
||
|
// Doesn't need to send a Pong.
|
||
|
void SetPingHandler(std::function<void(const std::vector<uint8_t> &)> func) {
|
||
|
ping_ = func;
|
||
|
}
|
||
|
void SetPongHandler(std::function<void(const std::vector<uint8_t> &)> func) {
|
||
|
pong_ = func;
|
||
|
}
|
||
|
|
||
|
bool IsOpen() {
|
||
|
return open_;
|
||
|
}
|
||
|
WebSocketClose CloseReason() {
|
||
|
return closeReason_;
|
||
|
}
|
||
|
|
||
|
protected:
|
||
|
WebSocketServer(size_t fd, InputSink *in, OutputSink *out) : fd_(fd), in_(in), out_(out) {
|
||
|
}
|
||
|
|
||
|
void SendHeader(bool fin, int opcode, size_t sz);
|
||
|
bool ReadFrames();
|
||
|
bool ReadFrame();
|
||
|
bool ReadPending();
|
||
|
bool ReadControlFrame(int opcode, size_t sz);
|
||
|
|
||
|
bool open_ = true;
|
||
|
bool sentClose_ = false;
|
||
|
size_t fd_ = 0;
|
||
|
InputSink *in_ = nullptr;
|
||
|
OutputSink *out_ = nullptr;
|
||
|
WebSocketClose closeReason_ = WebSocketClose::NO_STATUS;
|
||
|
|
||
|
std::vector<uint8_t> pendingBuf_;
|
||
|
uint8_t pendingMask_[4]{};
|
||
|
// Bytes left to read in the frame (in case of a partial frame read.)
|
||
|
uint64_t pendingLeft_ = 0;
|
||
|
int pendingOpcode_ = -1;
|
||
|
// Waiting for a frame with FIN.
|
||
|
bool pendingFin_ = false;
|
||
|
|
||
|
std::function<void(const std::string &)> text_;
|
||
|
std::function<void(const std::vector<uint8_t> &)> binary_;
|
||
|
std::function<void(const std::vector<uint8_t> &)> ping_;
|
||
|
std::function<void(const std::vector<uint8_t> &)> pong_;
|
||
|
};
|
||
|
|
||
|
};
|