From 292476366c43f228e1e0e3187b1902ad787847f5 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Tue, 30 Oct 2018 01:53:28 +0100 Subject: [PATCH] Make WebSockets benchmarkable --- Makefile | 4 +- misc/15.pro | 8 +- src/App.h | 28 ++--- src/WebSocket.h | 18 ++++ src/WebSocketContext.h | 45 +++++++- src/WebSocketData.h | 15 +++ src/{websocket => }/WebSocketProtocol.h | 24 ++--- src/{websocket => }/libwshandshake.hpp | 0 src/websocket/WebSocket.h | 69 ------------- src/websocket/WebSocketApp.h | 130 ------------------------ 10 files changed, 100 insertions(+), 241 deletions(-) rename src/{websocket => }/WebSocketProtocol.h (96%) rename src/{websocket => }/libwshandshake.hpp (100%) delete mode 100644 src/websocket/WebSocket.h delete mode 100644 src/websocket/WebSocketApp.h diff --git a/Makefile b/Makefile index 4b2703c..a2a2fae 100644 --- a/Makefile +++ b/Makefile @@ -16,8 +16,8 @@ examples: main: rm *.o clang -flto -O3 -c -IuSockets/src uSockets/src/*.c uSockets/src/eventing/*.c - clang++ -flto -O3 -c -std=c++17 -Isrc -IuSockets/src main.cpp - clang++ -flto -O3 -s *.o -o uWS_main -lssl -lcrypto -lpthread + clang++ -flto -O3 -c -std=c++17 -Isrc -IuSockets/src misc/main.cpp + clang++ -flto -O3 -s *.o -o Main -lssl -lcrypto -lpthread # I don't have any tests yet tests: diff --git a/misc/15.pro b/misc/15.pro index 56c4811..19d3f1f 100644 --- a/misc/15.pro +++ b/misc/15.pro @@ -15,10 +15,8 @@ SOURCES += \ HEADERS += \ ../src/HttpRouter.h \ ../src/HttpParser.h \ - ../src/websocket/libwshandshake.hpp \ - ../src/websocket/WebSocketProtocol.h \ - ../src/websocket/WebSocket.h \ - ../src/websocket/WebSocketApp.h \ + ../src/libwshandshake.hpp \ + ../src/WebSocketProtocol.h \ ../src/HttpContext.h \ ../src/HttpContextData.h \ ../src/HttpResponseData.h \ @@ -30,6 +28,8 @@ HEADERS += \ ../src/Loop.h \ ../src/App.h \ ../src/Utilities.h \ + ../src/WebSocket.h \ + ../src/WebSocketData.h \ ../src/WebSocketContext.h \ ../src/WebSocketContextData.h diff --git a/src/App.h b/src/App.h index 1c1cad0..2da2581 100644 --- a/src/App.h +++ b/src/App.h @@ -23,8 +23,9 @@ #include "HttpContext.h" #include "HttpResponse.h" #include "WebSocketContext.h" +#include "WebSocket.h" -#include "websocket/libwshandshake.hpp" +#include "libwshandshake.hpp" namespace uWS { template @@ -75,31 +76,20 @@ public: // adopting will immediately delete the socket! we cannot rely on reading anything on it // rely on http context data + // todo: sizeof websocket + WebSocket *webSocket = (WebSocket *) StaticDispatch::static_dispatch(us_ssl_socket_context_adopt_socket, us_socket_context_adopt_socket)( + (typename StaticDispatch::SOCKET_CONTEXT_TYPE *) webSocketContext, (typename StaticDispatch::SOCKET_TYPE *) res, 150); - //typename StaticDispatch::SOCKET_CONTEXT_TYPE *socketContext = (typename StaticDispatch::SOCKET_CONTEXT_TYPE *) StaticDispatch::static_dispatch(us_ssl_socket_get_context, us_socket_get_context)((typename StaticDispatch::SOCKET_TYPE *) res); - //StaticDispatch::static_dispatch(us_ssl_socket_context_ext, us_socket_context_ext)(socketContext); + webSocket->init(); - - - void *newSocket = StaticDispatch::static_dispatch(us_ssl_socket_context_adopt_socket, us_socket_context_adopt_socket)( - (typename StaticDispatch::SOCKET_CONTEXT_TYPE *) webSocketContext, (typename StaticDispatch::SOCKET_TYPE *) res, 15); + std::cout << "adopted" << std::endl; httpContext->upgradeToWebSocket( - newSocket + webSocket ); - std::cout << "Adopted!" << std::endl; - - // we should hand the new socket to the handler - connectHandler(newSocket, req); - - - /*res->upgradeToWebSocket( - StaticDispatch::static_dispatch(us_ssl_socket_context_adopt_socket, us_socket_context_adopt_socket)( - (typename StaticDispatch::SOCKET_CONTEXT_TYPE *) webSocketContext, (typename StaticDispatch::SOCKET_TYPE *) res, 15));*/ - - + connectHandler(webSocket, req); } else { diff --git a/src/WebSocket.h b/src/WebSocket.h index 872b71b..e04cbcf 100644 --- a/src/WebSocket.h +++ b/src/WebSocket.h @@ -1,4 +1,22 @@ #ifndef WEBSOCKET_H #define WEBSOCKET_H +#include "WebSocketData.h" + +namespace uWS { + +template +struct WebSocket { +private: + +public: + void init() { + // construct us + + new (us_socket_ext((us_socket *) this)) WebSocketData; + } +}; + +} + #endif // WEBSOCKET_H diff --git a/src/WebSocketContext.h b/src/WebSocketContext.h index 63b762a..8ecfd45 100644 --- a/src/WebSocketContext.h +++ b/src/WebSocketContext.h @@ -5,7 +5,9 @@ #include "WebSocketContextData.h" // the context depend on the PARSER but not the formatter! -#include "websocket/WebSocketProtocol.h" +#include "WebSocketProtocol.h" + +#include "WebSocketData.h" namespace uWS { @@ -21,6 +23,38 @@ private: return (SOCKET_CONTEXT_TYPE *) this; } + // I don't even.. merge this with the context itself! + template + struct WebSocketProtcolImplementation { + static bool setCompressed(uWS::WebSocketState *wState) { + std::cout << "set compressed" << std::endl; + return true; + } + + static void forceClose(uWS::WebSocketState *wState) { + std::cout << "force close" << std::endl; + } + + static bool handleFragment(char *data, size_t length, unsigned int remainingBytes, int opCode, bool fin, uWS::WebSocketState *webSocketState, void *s) { + + // this path should use AsyncSocket with cork and everything + + // format the response + char buf[100]; + int writeLength = WebSocketProtocol>::formatMessage(buf, data, length, (uWS::OpCode) opCode, length, false); + us_socket_write((SOCKET_TYPE *) s, buf, writeLength, false); + + // why does it not do anything immediately on true? + return false; + + } + + static bool refusePayloadLength(uint64_t length, uWS::WebSocketState *wState) { + //std::cout << "refusepayloadlength" << std::endl; + return false; + } + }; + WebSocketContext *init() { /* I guess open is never called */ @@ -36,10 +70,11 @@ private: /* Handle HTTP data streams */ static_dispatch(us_ssl_socket_context_on_data, us_socket_context_on_data)(getSocketContext(), [](auto *s, char *data, int length) { + // get the data + WebSocketData *wsState = (WebSocketData *) us_socket_ext(s); - // the socket is a websocket parser just like an http socket is an http parser - - std::cout << "data: " << std::endl; + // this parser requires almost no time -> 215k req/sec of 215k possible + uWS::WebSocketProtocol>::consume(data, length, wsState, s); return s; }); @@ -87,7 +122,7 @@ public: WebSocketContext *webSocketContext; // todo: sizeof - webSocketContext = (WebSocketContext *) static_dispatch(us_create_child_ssl_socket_context, us_create_child_socket_context)(parentSocketContext, 15); + webSocketContext = (WebSocketContext *) static_dispatch(us_create_child_ssl_socket_context, us_create_child_socket_context)(parentSocketContext, 100); if (!webSocketContext) { return nullptr; } diff --git a/src/WebSocketData.h b/src/WebSocketData.h index a8c3d3d..ed72956 100644 --- a/src/WebSocketData.h +++ b/src/WebSocketData.h @@ -1,4 +1,19 @@ #ifndef WEBSOCKETDATA_H #define WEBSOCKETDATA_H +#include "WebSocketProtocol.h" + +namespace uWS { + +struct WebSocketData : WebSocketState { +private: + +public: + WebSocketData() : WebSocketState() { + std::cout << "init websocket data!" << std::endl; + } +}; + +} + #endif // WEBSOCKETDATA_H diff --git a/src/websocket/WebSocketProtocol.h b/src/WebSocketProtocol.h similarity index 96% rename from src/websocket/WebSocketProtocol.h rename to src/WebSocketProtocol.h index 30ec104..2bd914d 100644 --- a/src/websocket/WebSocketProtocol.h +++ b/src/WebSocketProtocol.h @@ -138,7 +138,7 @@ protected: }; template - static inline bool consumeMessage(T payLength, char *&src, unsigned int &length, WebSocketState *wState) { + static inline bool consumeMessage(T payLength, char *&src, unsigned int &length, WebSocketState *wState, void *user) { if (getOpCode(src)) { if (wState->state.opStack == 1 || (!wState->state.lastFin && getOpCode(src) < 2)) { Impl::forceClose(wState); @@ -159,11 +159,11 @@ protected: if (payLength + MESSAGE_HEADER <= length) { if (isServer) { unmaskImpreciseCopyMask(src + MESSAGE_HEADER - 4, src + MESSAGE_HEADER, src + MESSAGE_HEADER - 4, (unsigned int) payLength); - if (Impl::handleFragment(src + MESSAGE_HEADER - 4, payLength, 0, wState->state.opCode[wState->state.opStack], isFin(src), wState)) { + if (Impl::handleFragment(src + MESSAGE_HEADER - 4, payLength, 0, wState->state.opCode[wState->state.opStack], isFin(src), wState, user)) { return true; } } else { - if (Impl::handleFragment(src + MESSAGE_HEADER, payLength, 0, wState->state.opCode[wState->state.opStack], isFin(src), wState)) { + if (Impl::handleFragment(src + MESSAGE_HEADER, payLength, 0, wState->state.opCode[wState->state.opStack], isFin(src), wState, user)) { return true; } } @@ -188,12 +188,12 @@ protected: } else { src += MESSAGE_HEADER; } - Impl::handleFragment(src, length - MESSAGE_HEADER, wState->remainingBytes, wState->state.opCode[wState->state.opStack], fin, wState); + Impl::handleFragment(src, length - MESSAGE_HEADER, wState->remainingBytes, wState->state.opCode[wState->state.opStack], fin, wState, user); return true; } } - static inline bool consumeContinuation(char *&src, unsigned int &length, WebSocketState *wState) { + static inline bool consumeContinuation(char *&src, unsigned int &length, WebSocketState *wState, void *user) { if (wState->remainingBytes <= length) { if (isServer) { int n = wState->remainingBytes >> 2; @@ -203,7 +203,7 @@ protected: } } - if (Impl::handleFragment(src, wState->remainingBytes, 0, wState->state.opCode[wState->state.opStack], wState->state.lastFin, wState)) { + if (Impl::handleFragment(src, wState->remainingBytes, 0, wState->state.opCode[wState->state.opStack], wState->state.lastFin, wState, user)) { return false; } @@ -221,7 +221,7 @@ protected: } wState->remainingBytes -= length; - if (Impl::handleFragment(src, length, wState->remainingBytes, wState->state.opCode[wState->state.opStack], wState->state.lastFin, wState)) { + if (Impl::handleFragment(src, length, wState->remainingBytes, wState->state.opCode[wState->state.opStack], wState->state.lastFin, wState, user)) { return false; } @@ -357,7 +357,7 @@ public: return messageLength; } - static inline void consume(char *src, unsigned int length, WebSocketState *wState) { + static inline void consume(char *src, unsigned int length, WebSocketState *wState, void *user) { if (wState->state.spillLength) { src -= wState->state.spillLength; length += wState->state.spillLength; @@ -375,18 +375,18 @@ public: } if (payloadLength(src) < 126) { - if (consumeMessage(payloadLength(src), src, length, wState)) { + if (consumeMessage(payloadLength(src), src, length, wState, user)) { return; } } else if (payloadLength(src) == 126) { if (length < MEDIUM_MESSAGE_HEADER) { break; - } else if(consumeMessage(ntohs(*(uint16_t *) &src[2]), src, length, wState)) { + } else if(consumeMessage(ntohs(*(uint16_t *) &src[2]), src, length, wState, user)) { return; } } else if (length < LONG_MESSAGE_HEADER) { break; - } else if (consumeMessage(be64toh(*(uint64_t *) &src[2]), src, length, wState)) { + } else if (consumeMessage(be64toh(*(uint64_t *) &src[2]), src, length, wState, user)) { return; } } @@ -394,7 +394,7 @@ public: memcpy(wState->state.spill, src, length); wState->state.spillLength = length; } - } else if (consumeContinuation(src, length, wState)) { + } else if (consumeContinuation(src, length, wState, user)) { goto parseNext; } } diff --git a/src/websocket/libwshandshake.hpp b/src/libwshandshake.hpp similarity index 100% rename from src/websocket/libwshandshake.hpp rename to src/libwshandshake.hpp diff --git a/src/websocket/WebSocket.h b/src/websocket/WebSocket.h deleted file mode 100644 index 26afd71..0000000 --- a/src/websocket/WebSocket.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2018 Alex Hultman and contributors. - - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - - * http://www.apache.org/licenses/LICENSE-2.0 - - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef WEBSOCKET_H -#define WEBSOCKET_H - -#include "libusockets.h" -#include "Socket.h" -#include "WebSocketProtocol.h" - -// client or server? -template -struct WebSocket : public Socket { - - // this needs to hold - struct Data : uWS::WebSocketState { - - }; - - static bool setCompressed(uWS::WebSocketState *wState) { - return true; - } - - static void forceClose(uWS::WebSocketState *wState) { - - } - - static bool handleFragment(char *data, size_t length, unsigned int remainingBytes, int opCode, bool fin, uWS::WebSocketState *webSocketState) { - - std::cout << std::string_view(data, length) << std::endl; - - //Data *webSocketData = (Data *) static_dispatch(us_ssl_socket_ext, us_socket_ext)((SOCKET_TYPE *) this); - - - //Socket::getSocketContextExt(); - - - - } - - static bool refusePayloadLength(uint64_t length, uWS::WebSocketState *wState) { - return false; - } - - //why is this here? events are handled and emitted from the app, the app depends on websocket, not two way deps! - void onData(char *data, int length) { - - Data *webSocketData = (Data *) Socket::static_dispatch(us_ssl_socket_ext, us_socket_ext)((typename Socket::SOCKET_TYPE *) this); - - uWS::WebSocketProtocol>::consume(data, length, webSocketData); - } - - WebSocket() = delete; -}; - -#endif // WEBSOCKET_H diff --git a/src/websocket/WebSocketApp.h b/src/websocket/WebSocketApp.h deleted file mode 100644 index 6b33895..0000000 --- a/src/websocket/WebSocketApp.h +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright 2018 Alex Hultman and contributors. - - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - - * http://www.apache.org/licenses/LICENSE-2.0 - - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifdef WEBSOCKETAPP_H -#define WEBSOCKETAPP_H - -#include "http/HttpApp.h" -#include - -// basically you have one of this for server, one for client!? -template -struct WebSocketApp : HttpApp> { - - // usings - using HttpApp>::static_dispatch; - typedef typename HttpApp>::SOCKET_CONTEXT_TYPE SOCKET_CONTEXT_TYPE; - typedef typename HttpApp>::SOCKET_TYPE SOCKET_TYPE; - - // constructor - WebSocketApp(SOCKET_CONTEXT_TYPE *httpServerContext) : HttpApp>(httpServerContext) { - - } - - // per-context "WebSocketApp" data - template - struct WebSocketServerContextData { - - WebSocketServerContextData() { - - } - - std::function *, std::string_view)> onMessage; - }; - - // all server contexts created with below functions - std::vector webSocketServerContexts; - bool lastContextIsServer; - - // register a new (server) protocol - template - WebSocketApp &onWebSocket(std::string pattern, std::function *, HttpRequest *, std::vector *)> handler) { - - // we are going to push a server context - lastContextIsServer = true; - - // create a new websocket child context - SOCKET_CONTEXT_TYPE *webSocketServerContext = static_dispatch(us_create_child_ssl_socket_context, us_create_child_socket_context)(HttpApp>::httpServerContext, sizeof(WebSocketServerContextData)); - new ((WebSocketServerContextData *) static_dispatch(us_ssl_socket_context_ext, us_socket_context_ext)(webSocketServerContext)) WebSocketServerContextData(); - WebSocketApp::webSocketServerContexts.push_back(webSocketServerContext); - - // add the behavior of it - static_dispatch(us_ssl_socket_context_on_data, us_socket_context_on_data)(webSocketServerContext, [](auto *s, char *data, int length) { - //WebSocketServerContextData *webSocketServerContextData = (WebSocketServerContextData *) static_dispatch(us_ssl_socket_context_ext, us_socket_context_ext)(static_dispatch(us_ssl_socket_get_context, us_socket_get_context)(s)); - - ((WebSocket *) s)->onData(data, length/*, webSocketServerContextData->onMessage*/); - - return s; - }); - - // todo: GET should probably be get since the parser only leaves lower case - HttpApp>::data->r.add("GET", pattern.c_str(), [webSocketServerContext, handler](typename HttpApp>::Data::UserData *user, auto *args) { - - std::string_view secWebSocketKey = user->httpRequest->getHeader("sec-websocket-key"); - if (secWebSocketKey.length()) { - - // note: OpenSSL can be used here to speed this up somewhat - char secWebSocketAccept[29] = {}; - WebSocketHandshake::generate(secWebSocketKey.data(), secWebSocketAccept); - - user->httpSocket->writeStatus("101 Switching Protocols") - ->writeHeader("Upgrade", "websocket") - ->writeHeader("Connection", "Upgrade") - ->writeHeader("Sec-WebSocket-Accept", secWebSocketAccept) - ->end(""); - - // todo: transform the socket into a websocket and hand it over - static_dispatch(us_ssl_socket_context_adopt_socket, us_socket_context_adopt_socket)(webSocketServerContext, (SOCKET_TYPE *) user->httpSocket, sizeof(typename WebSocket::Data) + sizeof(UserData)); - - // init the websocket data - - handler(user->httpSocket, user->httpRequest, args); - } else { - - // maybe pass this one to a HTTP handler on the websocket - - // note: this calls the http close handler inline - user->httpSocket->close(); - } - }); - - return *this; - } - - // this function does in fact determine whether we are client or not based on the websocket type passed! - template - WebSocketApp &onMessage(std::function *, std::string_view)> handler) { - - // pop last context on the stack - SOCKET_CONTEXT_TYPE *context = lastContextIsServer ? webSocketServerContexts.back() : nullptr; - - // get its data - if (lastContextIsServer) { - WebSocketServerContextData *data = (WebSocketServerContextData *) static_dispatch(us_ssl_socket_context_ext, us_socket_context_ext)(context); - - data->onMessage = handler; - } - - return *this; - } - - WebSocketApp &onClose(std::function) { - - return *this; - } -}; - -#endif // WEBSOCKETAPP_H