Make getUserData type safe

This commit is contained in:
Alex Hultman
2021-02-21 01:31:38 +01:00
parent c7805e2e48
commit 380b3a7134
7 changed files with 61 additions and 61 deletions
+12 -11
View File
@@ -53,7 +53,7 @@ struct TemplatedApp {
private:
/* The app always owns at least one http context, but creates websocket contexts on demand */
HttpContext<SSL> *httpContext;
std::vector<WebSocketContext<SSL, true> *> webSocketContexts;
std::vector<WebSocketContext<SSL, true, int> *> webSocketContexts;
public:
@@ -134,6 +134,7 @@ public:
return !httpContext;
}
template <typename UserData>
struct WebSocketBehavior {
/* Disabled compression by default - probably a bad default */
CompressOptions compression = DISABLED;
@@ -151,16 +152,16 @@ public:
/* Maximum socket lifetime in seconds before forced closure (defaults to disabled) */
unsigned short maxLifetime = 0;
MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *, struct us_socket_context_t *)> upgrade = nullptr;
MoveOnlyFunction<void(WebSocket<SSL, true> *)> open = nullptr;
MoveOnlyFunction<void(WebSocket<SSL, true> *, std::string_view, OpCode)> message = nullptr;
MoveOnlyFunction<void(WebSocket<SSL, true> *)> drain = nullptr;
MoveOnlyFunction<void(WebSocket<SSL, true> *)> ping = nullptr;
MoveOnlyFunction<void(WebSocket<SSL, true> *)> pong = nullptr;
MoveOnlyFunction<void(WebSocket<SSL, true> *, int, std::string_view)> close = nullptr;
MoveOnlyFunction<void(WebSocket<SSL, true, UserData> *)> open = nullptr;
MoveOnlyFunction<void(WebSocket<SSL, true, UserData> *, std::string_view, OpCode)> message = nullptr;
MoveOnlyFunction<void(WebSocket<SSL, true, UserData> *)> drain = nullptr;
MoveOnlyFunction<void(WebSocket<SSL, true, UserData> *)> ping = nullptr;
MoveOnlyFunction<void(WebSocket<SSL, true, UserData> *)> pong = nullptr;
MoveOnlyFunction<void(WebSocket<SSL, true, UserData> *, int, std::string_view)> close = nullptr;
};
template <typename UserData>
TemplatedApp &&ws(std::string pattern, WebSocketBehavior &&behavior) {
TemplatedApp &&ws(std::string pattern, WebSocketBehavior<UserData> &&behavior) {
/* Don't compile if alignment rules cannot be satisfied */
static_assert(alignof(UserData) <= LIBUS_EXT_ALIGNMENT,
"µWebSockets cannot satisfy UserData alignment requirements. You need to recompile µSockets with LIBUS_EXT_ALIGNMENT adjusted accordingly.");
@@ -180,10 +181,10 @@ public:
}
/* Every route has its own websocket context with its own behavior and user data type */
auto *webSocketContext = WebSocketContext<SSL, true>::create(Loop::get(), (us_socket_context_t *) httpContext);
auto *webSocketContext = WebSocketContext<SSL, true, UserData>::create(Loop::get(), (us_socket_context_t *) httpContext);
/* We need to clear this later on */
webSocketContexts.push_back(webSocketContext);
webSocketContexts.push_back((WebSocketContext<SSL, true, int> *) webSocketContext);
/* Quick fix to disable any compression if set */
#ifdef UWS_NO_ZLIB
@@ -206,7 +207,7 @@ public:
webSocketContext->getExt()->openHandler = std::move(behavior.open);
webSocketContext->getExt()->messageHandler = std::move(behavior.message);
webSocketContext->getExt()->drainHandler = std::move(behavior.drain);
webSocketContext->getExt()->closeHandler = std::move([closeHandler = std::move(behavior.close)](WebSocket<SSL, true> *ws, int code, std::string_view message) mutable {
webSocketContext->getExt()->closeHandler = std::move([closeHandler = std::move(behavior.close)](WebSocket<SSL, true, UserData> *ws, int code, std::string_view message) mutable {
if (closeHandler) {
closeHandler(ws, code, message);
}
+3 -3
View File
@@ -29,13 +29,13 @@
namespace uWS {
template <bool, bool> struct WebSocketContext;
template <bool, bool, typename> struct WebSocketContext;
template <bool SSL>
struct AsyncSocket {
template <bool> friend struct HttpContext;
template <bool, bool> friend struct WebSocketContext;
template <bool> friend struct WebSocketContextData;
template <bool, bool, typename> friend struct WebSocketContext;
template <bool, typename> friend struct WebSocketContextData;
friend struct TopicTree;
protected:
+2 -2
View File
@@ -214,7 +214,7 @@ public:
struct us_socket_context_t *webSocketContext) {
/* Extract needed parameters from WebSocketContextData */
WebSocketContextData<SSL> *webSocketContextData = (WebSocketContextData<SSL> *) us_socket_context_ext(SSL, webSocketContext);
WebSocketContextData<SSL, UserData> *webSocketContextData = (WebSocketContextData<SSL, UserData> *) us_socket_context_ext(SSL, webSocketContext);
/* Note: OpenSSL can be used here to speed this up somewhat */
char secWebSocketAccept[29] = {};
@@ -281,7 +281,7 @@ public:
bool wasCorked = Super::isCorked();
/* Adopting a socket invalidates it, do not rely on it directly to carry any data */
WebSocket<SSL, true> *webSocket = (WebSocket<SSL, true> *) us_socket_context_adopt_socket(SSL,
WebSocket<SSL, true, UserData> *webSocket = (WebSocket<SSL, true, UserData> *) us_socket_context_adopt_socket(SSL,
(us_socket_context_t *) webSocketContext, (us_socket_t *) this, sizeof(WebSocketData) + sizeof(UserData));
/* For whatever reason we were corked, update cork to the new socket */
+12 -13
View File
@@ -27,7 +27,7 @@
namespace uWS {
template <bool SSL, bool isServer>
template <bool SSL, bool isServer, typename USERDATA>
struct WebSocket : AsyncSocket<SSL> {
template <bool> friend struct TemplatedApp;
template <bool> friend struct HttpResponse;
@@ -41,10 +41,10 @@ private:
public:
/* Returns pointer to the per socket user data */
void *getUserData() {
USERDATA *getUserData() {
WebSocketData *webSocketData = (WebSocketData *) us_socket_ext(SSL, (us_socket_t *) this);
/* We just have it overallocated by sizeof type */
return (webSocketData + 1);
return (USERDATA *) (webSocketData + 1);
}
/* See AsyncSocket */
@@ -65,7 +65,7 @@ public:
/* Send or buffer a WebSocket frame, compressed or not. Returns BACKPRESSURE on increased user space backpressure,
* DROPPED on dropped message (due to backpressure) or SUCCCESS if you are free to send even more now. */
SendStatus send(std::string_view message, OpCode opCode = OpCode::BINARY, bool compress = false) {
WebSocketContextData<SSL> *webSocketContextData = (WebSocketContextData<SSL> *) us_socket_context_ext(SSL,
WebSocketContextData<SSL, USERDATA> *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL,
(us_socket_context_t *) us_socket_context(SSL, (us_socket_t *) this)
);
@@ -159,17 +159,16 @@ public:
bool ok = send(std::string_view(closePayload, closePayloadLength), OpCode::CLOSE);
/* FIN if we are ok and not corked */
WebSocket<SSL, true> *webSocket = (WebSocket<SSL, true> *) this;
if (!webSocket->isCorked()) {
if (!this->isCorked()) {
if (ok) {
/* If we are not corked, and we just sent off everything, we need to FIN right here.
* In all other cases, we need to fin either if uncork was successful, or when drainage is complete. */
webSocket->shutdown();
this->shutdown();
}
}
/* Emit close event */
WebSocketContextData<SSL> *webSocketContextData = (WebSocketContextData<SSL> *) us_socket_context_ext(SSL,
WebSocketContextData<SSL, USERDATA> *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL,
(us_socket_context_t *) us_socket_context(SSL, (us_socket_t *) this)
);
if (webSocketContextData->closeHandler) {
@@ -199,7 +198,7 @@ public:
/* Subscribe to a topic according to MQTT rules and syntax. Returns [numSubscribers, success]. */
std::pair<unsigned int, bool> subscribe(std::string_view topic, bool nonStrict = false) {
WebSocketContextData<SSL> *webSocketContextData = (WebSocketContextData<SSL> *) us_socket_context_ext(SSL,
WebSocketContextData<SSL, USERDATA> *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL,
(us_socket_context_t *) us_socket_context(SSL, (us_socket_t *) this)
);
@@ -214,7 +213,7 @@ public:
/* Unsubscribe from a topic, returns true if we were subscribed. Returns [numSubscribers, success]. */
std::pair<unsigned int, bool> unsubscribe(std::string_view topic, bool nonStrict = false) {
WebSocketContextData<SSL> *webSocketContextData = (WebSocketContextData<SSL> *) us_socket_context_ext(SSL,
WebSocketContextData<SSL, USERDATA> *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL,
(us_socket_context_t *) us_socket_context(SSL, (us_socket_t *) this)
);
@@ -225,7 +224,7 @@ public:
/* Returns whether this socket is subscribed to the specified topic */
bool isSubscribed(std::string_view topic) {
WebSocketContextData<SSL> *webSocketContextData = (WebSocketContextData<SSL> *) us_socket_context_ext(SSL,
WebSocketContextData<SSL, USERDATA> *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL,
(us_socket_context_t *) us_socket_context(SSL, (us_socket_t *) this)
);
@@ -239,7 +238,7 @@ public:
/* Returns number of subscribers for this topic, or 0 for failure */
unsigned int numSubscribers(std::string_view topic) {
WebSocketContextData<SSL> *webSocketContextData = (WebSocketContextData<SSL> *) us_socket_context_ext(SSL,
WebSocketContextData<SSL, USERDATA> *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL,
(us_socket_context_t *) us_socket_context(SSL, (us_socket_t *) this)
);
@@ -274,7 +273,7 @@ public:
* We, the WebSocket, must be subscribed to the topic itself and if so - no message will be sent to ourselves.
* Use App::publish for an unconditional publish that simply publishes to whomever might be subscribed. */
bool publish(std::string_view topic, std::string_view message, OpCode opCode = OpCode::TEXT, bool compress = false) {
WebSocketContextData<SSL> *webSocketContextData = (WebSocketContextData<SSL> *) us_socket_context_ext(SSL,
WebSocketContextData<SSL, USERDATA> *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL,
(us_socket_context_t *) us_socket_context(SSL, (us_socket_t *) this)
);
+20 -20
View File
@@ -25,7 +25,7 @@
namespace uWS {
template <bool SSL, bool isServer>
template <bool SSL, bool isServer, typename USERDATA>
struct WebSocketContext {
template <bool> friend struct TemplatedApp;
template <bool, typename> friend struct WebSocketProtocol;
@@ -36,8 +36,8 @@ private:
return (us_socket_context_t *) this;
}
WebSocketContextData<SSL> *getExt() {
return (WebSocketContextData<SSL> *) us_socket_context_ext(SSL, (us_socket_context_t *) this);
WebSocketContextData<SSL, USERDATA> *getExt() {
return (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, (us_socket_context_t *) this);
}
/* If we have negotiated compression, set this frame compressed */
@@ -59,7 +59,7 @@ private:
/* Returns true on breakage */
static bool handleFragment(char *data, size_t length, unsigned int remainingBytes, int opCode, bool fin, WebSocketState<isServer> *webSocketState, void *s) {
/* WebSocketData and WebSocketContextData */
WebSocketContextData<SSL> *webSocketContextData = (WebSocketContextData<SSL> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s));
WebSocketContextData<SSL, USERDATA> *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s));
WebSocketData *webSocketData = (WebSocketData *) us_socket_ext(SSL, (us_socket_t *) s);
/* Is this a non-control frame? */
@@ -90,7 +90,7 @@ private:
/* Emit message event & break if we are closed or shut down when returning */
if (webSocketContextData->messageHandler) {
webSocketContextData->messageHandler((WebSocket<SSL, isServer> *) s, std::string_view(data, length), (OpCode) opCode);
webSocketContextData->messageHandler((WebSocket<SSL, isServer, USERDATA> *) s, std::string_view(data, length), (OpCode) opCode);
if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) {
return true;
}
@@ -148,7 +148,7 @@ private:
/* Emit message and check for shutdown or close */
if (webSocketContextData->messageHandler) {
webSocketContextData->messageHandler((WebSocket<SSL, isServer> *) s, std::string_view(data, length), (OpCode) opCode);
webSocketContextData->messageHandler((WebSocket<SSL, isServer, USERDATA> *) s, std::string_view(data, length), (OpCode) opCode);
if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) {
return true;
}
@@ -160,7 +160,7 @@ private:
}
} else {
/* Control frames need the websocket to send pings, pongs and close */
WebSocket<SSL, isServer> *webSocket = (WebSocket<SSL, isServer> *) s;
WebSocket<SSL, isServer, USERDATA> *webSocket = (WebSocket<SSL, isServer, USERDATA> *) s;
if (!remainingBytes && fin && !webSocketData->controlTipLength) {
if (opCode == CLOSE) {
@@ -225,13 +225,13 @@ private:
}
static bool refusePayloadLength(uint64_t length, WebSocketState<isServer> */*wState*/, void *s) {
auto *webSocketContextData = (WebSocketContextData<SSL> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s));
auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s));
/* Return true for refuse, false for accept */
return webSocketContextData->maxPayloadLength < length;
}
WebSocketContext<SSL, isServer> *init() {
WebSocketContext<SSL, isServer, USERDATA> *init() {
/* Adopting a socket does not trigger open event.
* We arreive as WebSocket with timeout set and
* any backpressure from HTTP state kept. */
@@ -242,10 +242,10 @@ private:
WebSocketData *webSocketData = (WebSocketData *) (us_socket_ext(SSL, s));
if (!webSocketData->isShuttingDown) {
/* Emit close event */
auto *webSocketContextData = (WebSocketContextData<SSL> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s));
auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s));
if (webSocketContextData->closeHandler) {
webSocketContextData->closeHandler((WebSocket<SSL, true> *) s, 1006, {(char *) reason, (size_t) code});
webSocketContextData->closeHandler((WebSocket<SSL, isServer, USERDATA> *) s, 1006, {(char *) reason, (size_t) code});
}
/* Make sure to unsubscribe from any pub/sub node at exit */
@@ -272,7 +272,7 @@ private:
return s;
}
auto *webSocketContextData = (WebSocketContextData<SSL> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s));
auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s));
auto *asyncSocket = (AsyncSocket<SSL> *) s;
/* Every time we get data and not in shutdown state we simply reset the timeout */
@@ -283,7 +283,7 @@ private:
asyncSocket->cork();
/* This parser has virtually no overhead */
WebSocketProtocol<isServer, WebSocketContext<SSL, isServer>>::consume(data, (unsigned int) length, (WebSocketState<isServer> *) webSocketData, s);
WebSocketProtocol<isServer, WebSocketContext<SSL, isServer, USERDATA>>::consume(data, (unsigned int) length, (WebSocketState<isServer> *) webSocketData, s);
/* Uncorking a closed socekt is fine, in fact it is needed */
asyncSocket->uncork();
@@ -325,7 +325,7 @@ private:
/* Behavior: if we actively drain backpressure, always reset timeout (even if we are in shutdown) */
/* Also reset timeout if we came here with 0 backpressure */
if (!backpressure || backpressure > asyncSocket->getBufferedAmount()) {
auto *webSocketContextData = (WebSocketContextData<SSL> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s));
auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s));
asyncSocket->timeout(webSocketContextData->idleTimeoutComponents.first);
webSocketData->hasTimedOut = false;
}
@@ -339,9 +339,9 @@ private:
}
} else if (!backpressure || backpressure > asyncSocket->getBufferedAmount()) {
/* Only call drain if we actually drained backpressure or if we came here with 0 backpressure */
auto *webSocketContextData = (WebSocketContextData<SSL> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s));
auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s));
if (webSocketContextData->drainHandler) {
webSocketContextData->drainHandler((WebSocket<SSL, isServer> *) s);
webSocketContextData->drainHandler((WebSocket<SSL, isServer, USERDATA> *) s);
}
/* No need to check for closed here as we leave the handler immediately*/
}
@@ -362,7 +362,7 @@ private:
us_socket_context_on_timeout(SSL, getSocketContext(), [](auto *s) {
auto *webSocketData = (WebSocketData *)(us_socket_ext(SSL, s));
auto *webSocketContextData = (WebSocketContextData<SSL> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s));
auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s));
if (webSocketContextData->sendPingsAutomatically && !webSocketData->hasTimedOut) {
webSocketData->hasTimedOut = true;
@@ -383,7 +383,7 @@ private:
}
void free() {
WebSocketContextData<SSL> *webSocketContextData = (WebSocketContextData<SSL> *) us_socket_context_ext(SSL, (us_socket_context_t *) this);
WebSocketContextData<SSL, USERDATA> *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, (us_socket_context_t *) this);
webSocketContextData->~WebSocketContextData();
us_socket_context_free(SSL, (us_socket_context_t *) this);
@@ -392,13 +392,13 @@ private:
public:
/* WebSocket contexts are always child contexts to a HTTP context so no SSL options are needed as they are inherited */
static WebSocketContext *create(Loop */*loop*/, us_socket_context_t *parentSocketContext) {
WebSocketContext *webSocketContext = (WebSocketContext *) us_create_child_socket_context(SSL, parentSocketContext, sizeof(WebSocketContextData<SSL>));
WebSocketContext *webSocketContext = (WebSocketContext *) us_create_child_socket_context(SSL, parentSocketContext, sizeof(WebSocketContextData<SSL, USERDATA>));
if (!webSocketContext) {
return nullptr;
}
/* Init socket context data */
new ((WebSocketContextData<SSL> *) us_socket_context_ext(SSL, (us_socket_context_t *)webSocketContext)) WebSocketContextData<SSL>;
new ((WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, (us_socket_context_t *)webSocketContext)) WebSocketContextData<SSL, USERDATA>;
return webSocketContext->init();
}
};
+9 -9
View File
@@ -27,11 +27,11 @@
namespace uWS {
template <bool, bool> struct WebSocket;
template <bool, bool, typename> struct WebSocket;
/* todo: this looks identical to WebSocketBehavior, why not just std::move that entire thing in? */
template <bool SSL>
template <bool SSL, typename USERDATA>
struct WebSocketContextData {
private:
/* Used for prepending unframed messages when using dedicated compressors */
@@ -47,13 +47,13 @@ private:
public:
/* The callbacks for this context */
MoveOnlyFunction<void(WebSocket<SSL, true> *)> openHandler = nullptr;
MoveOnlyFunction<void(WebSocket<SSL, true> *, std::string_view, OpCode)> messageHandler = nullptr;
MoveOnlyFunction<void(WebSocket<SSL, true> *)> drainHandler = nullptr;
MoveOnlyFunction<void(WebSocket<SSL, true> *, int, std::string_view)> closeHandler = nullptr;
MoveOnlyFunction<void(WebSocket<SSL, true, USERDATA> *)> openHandler = nullptr;
MoveOnlyFunction<void(WebSocket<SSL, true, USERDATA> *, std::string_view, OpCode)> messageHandler = nullptr;
MoveOnlyFunction<void(WebSocket<SSL, true, USERDATA> *)> drainHandler = nullptr;
MoveOnlyFunction<void(WebSocket<SSL, true, USERDATA> *, int, std::string_view)> closeHandler = nullptr;
/* Todo: these should take message also; breaking change for v0.18 */
MoveOnlyFunction<void(WebSocket<SSL, true> *)> pingHandler = nullptr;
MoveOnlyFunction<void(WebSocket<SSL, true> *)> pongHandler = nullptr;
MoveOnlyFunction<void(WebSocket<SSL, true, USERDATA> *)> pingHandler = nullptr;
MoveOnlyFunction<void(WebSocket<SSL, true, USERDATA> *)> pongHandler = nullptr;
/* Settings for this context */
size_t maxPayloadLength = 0;
@@ -135,7 +135,7 @@ public:
/* However, dedicated compression has its own path */
if (compression != SHARED_COMPRESSOR) {
WebSocket<SSL, true> *ws = (WebSocket<SSL, true> *) asyncSocket;
WebSocket<SSL, true, int> *ws = (WebSocket<SSL, true, int> *) asyncSocket;
/* For performance reasons we always cork when in dedicated mode.
* Is this really the best? We already kind of cork things in Zlib?
+3 -3
View File
@@ -29,9 +29,9 @@ namespace uWS {
struct WebSocketData : AsyncSocketData<false>, WebSocketState<true> {
/* This guy has a lot of friends - why? */
template <bool, bool> friend struct WebSocketContext;
template <bool> friend struct WebSocketContextData;
template <bool, bool> friend struct WebSocket;
template <bool, bool, typename> friend struct WebSocketContext;
template <bool, typename> friend struct WebSocketContextData;
template <bool, bool, typename> friend struct WebSocket;
template <bool> friend struct HttpContext;
private:
std::string fragmentBuffer;