Make WebSockets benchmarkable

This commit is contained in:
Alex Hultman
2018-10-30 01:53:28 +01:00
parent eb5cd17c78
commit 292476366c
10 changed files with 100 additions and 241 deletions
+2 -2
View File
@@ -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:
+4 -4
View File
@@ -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
+9 -19
View File
@@ -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 <bool SSL>
@@ -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<SSL> *webSocket = (WebSocket<SSL> *) StaticDispatch<SSL>::static_dispatch(us_ssl_socket_context_adopt_socket, us_socket_context_adopt_socket)(
(typename StaticDispatch<SSL>::SOCKET_CONTEXT_TYPE *) webSocketContext, (typename StaticDispatch<SSL>::SOCKET_TYPE *) res, 150);
//typename StaticDispatch<SSL>::SOCKET_CONTEXT_TYPE *socketContext = (typename StaticDispatch<SSL>::SOCKET_CONTEXT_TYPE *) StaticDispatch<SSL>::static_dispatch(us_ssl_socket_get_context, us_socket_get_context)((typename StaticDispatch<SSL>::SOCKET_TYPE *) res);
//StaticDispatch<SSL>::static_dispatch(us_ssl_socket_context_ext, us_socket_context_ext)(socketContext);
webSocket->init();
void *newSocket = StaticDispatch<SSL>::static_dispatch(us_ssl_socket_context_adopt_socket, us_socket_context_adopt_socket)(
(typename StaticDispatch<SSL>::SOCKET_CONTEXT_TYPE *) webSocketContext, (typename StaticDispatch<SSL>::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<SSL>::static_dispatch(us_ssl_socket_context_adopt_socket, us_socket_context_adopt_socket)(
(typename StaticDispatch<SSL>::SOCKET_CONTEXT_TYPE *) webSocketContext, (typename StaticDispatch<SSL>::SOCKET_TYPE *) res, 15));*/
connectHandler(webSocket, req);
} else {
+18
View File
@@ -1,4 +1,22 @@
#ifndef WEBSOCKET_H
#define WEBSOCKET_H
#include "WebSocketData.h"
namespace uWS {
template <bool SSL>
struct WebSocket {
private:
public:
void init() {
// construct us
new (us_socket_ext((us_socket *) this)) WebSocketData;
}
};
}
#endif // WEBSOCKET_H
+40 -5
View File
@@ -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 <bool isServer>
struct WebSocketProtcolImplementation {
static bool setCompressed(uWS::WebSocketState<isServer> *wState) {
std::cout << "set compressed" << std::endl;
return true;
}
static void forceClose(uWS::WebSocketState<isServer> *wState) {
std::cout << "force close" << std::endl;
}
static bool handleFragment(char *data, size_t length, unsigned int remainingBytes, int opCode, bool fin, uWS::WebSocketState<isServer> *webSocketState, void *s) {
// this path should use AsyncSocket with cork and everything
// format the response
char buf[100];
int writeLength = WebSocketProtocol<isServer, WebSocketProtcolImplementation<isServer>>::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<isServer> *wState) {
//std::cout << "refusepayloadlength" << std::endl;
return false;
}
};
WebSocketContext<SSL> *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<true, WebSocketProtcolImplementation<true>>::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;
}
+15
View File
@@ -1,4 +1,19 @@
#ifndef WEBSOCKETDATA_H
#define WEBSOCKETDATA_H
#include "WebSocketProtocol.h"
namespace uWS {
struct WebSocketData : WebSocketState<true> {
private:
public:
WebSocketData() : WebSocketState<true>() {
std::cout << "init websocket data!" << std::endl;
}
};
}
#endif // WEBSOCKETDATA_H
@@ -138,7 +138,7 @@ protected:
};
template <unsigned int MESSAGE_HEADER, typename T>
static inline bool consumeMessage(T payLength, char *&src, unsigned int &length, WebSocketState<isServer> *wState) {
static inline bool consumeMessage(T payLength, char *&src, unsigned int &length, WebSocketState<isServer> *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<isServer> *wState) {
static inline bool consumeContinuation(char *&src, unsigned int &length, WebSocketState<isServer> *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<isServer> *wState) {
static inline void consume(char *src, unsigned int length, WebSocketState<isServer> *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<SHORT_MESSAGE_HEADER, uint8_t>(payloadLength(src), src, length, wState)) {
if (consumeMessage<SHORT_MESSAGE_HEADER, uint8_t>(payloadLength(src), src, length, wState, user)) {
return;
}
} else if (payloadLength(src) == 126) {
if (length < MEDIUM_MESSAGE_HEADER) {
break;
} else if(consumeMessage<MEDIUM_MESSAGE_HEADER, uint16_t>(ntohs(*(uint16_t *) &src[2]), src, length, wState)) {
} else if(consumeMessage<MEDIUM_MESSAGE_HEADER, uint16_t>(ntohs(*(uint16_t *) &src[2]), src, length, wState, user)) {
return;
}
} else if (length < LONG_MESSAGE_HEADER) {
break;
} else if (consumeMessage<LONG_MESSAGE_HEADER, uint64_t>(be64toh(*(uint64_t *) &src[2]), src, length, wState)) {
} else if (consumeMessage<LONG_MESSAGE_HEADER, uint64_t>(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;
}
}
-69
View File
@@ -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 <bool SSL, bool isServer>
struct WebSocket : public Socket<SSL> {
// this needs to hold
struct Data : uWS::WebSocketState<isServer> {
};
static bool setCompressed(uWS::WebSocketState<isServer> *wState) {
return true;
}
static void forceClose(uWS::WebSocketState<isServer> *wState) {
}
static bool handleFragment(char *data, size_t length, unsigned int remainingBytes, int opCode, bool fin, uWS::WebSocketState<isServer> *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<SSL>::getSocketContextExt();
}
static bool refusePayloadLength(uint64_t length, uWS::WebSocketState<isServer> *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<SSL>::static_dispatch(us_ssl_socket_ext, us_socket_ext)((typename Socket<SSL>::SOCKET_TYPE *) this);
uWS::WebSocketProtocol<isServer, WebSocket<SSL, isServer>>::consume(data, length, webSocketData);
}
WebSocket() = delete;
};
#endif // WEBSOCKET_H
-130
View File
@@ -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 <vector>
// basically you have one of this for server, one for client!?
template <bool SSL>
struct WebSocketApp : HttpApp<SSL, WebSocketApp<SSL>> {
// usings
using HttpApp<SSL, WebSocketApp<SSL>>::static_dispatch;
typedef typename HttpApp<SSL, WebSocketApp<SSL>>::SOCKET_CONTEXT_TYPE SOCKET_CONTEXT_TYPE;
typedef typename HttpApp<SSL, WebSocketApp<SSL>>::SOCKET_TYPE SOCKET_TYPE;
// constructor
WebSocketApp(SOCKET_CONTEXT_TYPE *httpServerContext) : HttpApp<SSL, WebSocketApp<SSL>>(httpServerContext) {
}
// per-context "WebSocketApp" data
template <bool isServer>
struct WebSocketServerContextData {
WebSocketServerContextData() {
}
std::function<void(WebSocket<SSL, isServer> *, std::string_view)> onMessage;
};
// all server contexts created with below functions
std::vector<SOCKET_CONTEXT_TYPE *> webSocketServerContexts;
bool lastContextIsServer;
// register a new (server) protocol
template <class UserData>
WebSocketApp &onWebSocket(std::string pattern, std::function<void(HttpSocket<SSL> *, HttpRequest *, std::vector<std::string_view> *)> 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<SSL, WebSocketApp<SSL>>::httpServerContext, sizeof(WebSocketServerContextData<true>));
new ((WebSocketServerContextData<true> *) static_dispatch(us_ssl_socket_context_ext, us_socket_context_ext)(webSocketServerContext)) WebSocketServerContextData<true>();
WebSocketApp<SSL>::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<true> *webSocketServerContextData = (WebSocketServerContextData<true> *) static_dispatch(us_ssl_socket_context_ext, us_socket_context_ext)(static_dispatch(us_ssl_socket_get_context, us_socket_get_context)(s));
((WebSocket<SSL, true> *) s)->onData(data, length/*, webSocketServerContextData->onMessage*/);
return s;
});
// todo: GET should probably be get since the parser only leaves lower case
HttpApp<SSL, WebSocketApp<SSL>>::data->r.add("GET", pattern.c_str(), [webSocketServerContext, handler](typename HttpApp<SSL, WebSocketApp<SSL>>::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<SSL, true>::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 <bool isServer>
WebSocketApp &onMessage(std::function<void(WebSocket<SSL, isServer> *, std::string_view)> handler) {
// pop last context on the stack
SOCKET_CONTEXT_TYPE *context = lastContextIsServer ? webSocketServerContexts.back() : nullptr;
// get its data
if (lastContextIsServer) {
WebSocketServerContextData<true> *data = (WebSocketServerContextData<true> *) static_dispatch(us_ssl_socket_context_ext, us_socket_context_ext)(context);
data->onMessage = handler;
}
return *this;
}
WebSocketApp &onClose(std::function<void()>) {
return *this;
}
};
#endif // WEBSOCKETAPP_H