From a7597772f8eaf2612709509fc244df9af9a42e85 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Fri, 28 Dec 2018 14:16:28 +0100 Subject: [PATCH] Separate parser from formatter, make it build --- src/WebSocket.h | 6 +- src/WebSocketContext.h | 10 +- src/WebSocketProtocol.h | 276 ++++++++++++++++++++-------------------- 3 files changed, 149 insertions(+), 143 deletions(-) diff --git a/src/WebSocket.h b/src/WebSocket.h index 22bbe97..53e3653 100644 --- a/src/WebSocket.h +++ b/src/WebSocket.h @@ -71,9 +71,9 @@ public: } /* Get size, alloate size, write if needed */ - size_t messageFrameSize = WebSocketProtocol>::messageFrameSize(message.length()); + size_t messageFrameSize = protocol::messageFrameSize(message.length()); auto[sendBuffer, requiresWrite] = Super::getSendBuffer(messageFrameSize); - WebSocketProtocol>::formatMessage(sendBuffer, message.data(), message.length(), opCode, message.length(), compress); + protocol::formatMessage(sendBuffer, message.data(), message.length(), opCode, message.length(), compress); if (requiresWrite) { auto[written, failed] = Super::write(sendBuffer, messageFrameSize); @@ -102,7 +102,7 @@ public: /* Format and send the close frame */ char closePayload[MAX_CLOSE_PAYLOAD + 2]; - int closePayloadLength = (int) WebSocketProtocol>::formatClosePayload(closePayload, code, message.data(), length); + int closePayloadLength = protocol::formatClosePayload(closePayload, code, message.data(), length); // but what if we are NOT corked, THEN we can FIN here if we succeeded diff --git a/src/WebSocketContext.h b/src/WebSocketContext.h index 1d1b9d4..596ecbf 100644 --- a/src/WebSocketContext.h +++ b/src/WebSocketContext.h @@ -93,7 +93,7 @@ private: } /* Check text messages for Utf-8 validity */ - if (opCode == 1 && !WebSocketProtocol>::isValidUtf8((unsigned char *) data, length)) { + if (opCode == 1 && !protocol::isValidUtf8((unsigned char *) data, length)) { forceClose(webSocketState, s); return true; } @@ -146,7 +146,7 @@ private: } /* Check text messages for Utf-8 validity */ - if (opCode == 1 && !WebSocketProtocol>::isValidUtf8((unsigned char *) data, length)) { + if (opCode == 1 && !protocol::isValidUtf8((unsigned char *) data, length)) { forceClose(webSocketState, s); return true; } @@ -169,7 +169,7 @@ private: if (!remainingBytes && fin && !webSocketData->controlTipLength) { if (opCode == CLOSE) { - auto closeFrame = WebSocketProtocol>::parseClosePayload(data, length); + auto closeFrame = protocol::parseClosePayload(data, length); webSocket->close(closeFrame.code, std::string_view(closeFrame.message, closeFrame.length)); return true; } else { @@ -194,7 +194,7 @@ private: if (!remainingBytes && fin) { char *controlBuffer = (char *) webSocketData->fragmentBuffer.data() + webSocketData->fragmentBuffer.length() - webSocketData->controlTipLength; if (opCode == CLOSE) { - typename WebSocketProtocol>::CloseFrame closeFrame = WebSocketProtocol>::parseClosePayload(controlBuffer, webSocketData->controlTipLength); + protocol::CloseFrame closeFrame = protocol::parseClosePayload(controlBuffer, webSocketData->controlTipLength); webSocket->close(closeFrame.code, std::string_view(closeFrame.message, closeFrame.length)); return true; } else { @@ -237,7 +237,7 @@ private: /* Handle socket disconnections */ static_dispatch(us_ssl_socket_context_on_close, us_socket_context_on_close)(getSocketContext(), [](auto *s) { - std::cout << "close!" << std::endl; + //std::cout << "close!" << std::endl; diff --git a/src/WebSocketProtocol.h b/src/WebSocketProtocol.h index 94e6c92..f95519f 100644 --- a/src/WebSocketProtocol.h +++ b/src/WebSocketProtocol.h @@ -90,6 +90,147 @@ public: char mask[isServer ? 4 : 1]; }; +namespace protocol { + +// Based on utf8_check.c by Markus Kuhn, 2005 +// https://www.cl.cam.ac.uk/~mgk25/ucs/utf8_check.c +// Optimized for predominantly 7-bit content by Alex Hultman, 2016 +// Licensed as Zlib, like the rest of this project +static bool isValidUtf8(unsigned char *s, size_t length) +{ + for (unsigned char *e = s + length; s != e; ) { + if (s + 4 <= e && ((*(uint32_t *) s) & 0x80808080) == 0) { + s += 4; + } else { + while (!(*s & 0x80)) { + if (++s == e) { + return true; + } + } + + if ((s[0] & 0x60) == 0x40) { + if (s + 1 >= e || (s[1] & 0xc0) != 0x80 || (s[0] & 0xfe) == 0xc0) { + return false; + } + s += 2; + } else if ((s[0] & 0xf0) == 0xe0) { + if (s + 2 >= e || (s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80 || + (s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) || (s[0] == 0xed && (s[1] & 0xe0) == 0xa0)) { + return false; + } + s += 3; + } else if ((s[0] & 0xf8) == 0xf0) { + if (s + 3 >= e || (s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80 || (s[3] & 0xc0) != 0x80 || + (s[0] == 0xf0 && (s[1] & 0xf0) == 0x80) || (s[0] == 0xf4 && s[1] > 0x8f) || s[0] > 0xf4) { + return false; + } + s += 4; + } else { + return false; + } + } + } + return true; +} + +struct CloseFrame { + uint16_t code; + char *message; + size_t length; +}; + +static inline CloseFrame parseClosePayload(char *src, size_t length) { + CloseFrame cf = {}; + if (length >= 2) { + memcpy(&cf.code, src, 2); + cf = {ntohs(cf.code), src + 2, length - 2}; + if (cf.code < 1000 || cf.code > 4999 || (cf.code > 1011 && cf.code < 4000) || + (cf.code >= 1004 && cf.code <= 1006) || !isValidUtf8((unsigned char *) cf.message, cf.length)) { + return {}; + } + } + return cf; +} + +static inline size_t formatClosePayload(char *dst, uint16_t code, const char *message, size_t length) { + if (code) { + code = htons(code); + memcpy(dst, &code, 2); + memcpy(dst + 2, message, length); + return length + 2; + } + return 0; +} + +static inline size_t messageFrameSize(size_t messageSize) { + if (messageSize < 126) { + return 2 + messageSize; + } else if (messageSize <= UINT16_MAX) { + return 4 + messageSize; + } + return 10 + messageSize; +} + +enum { + SND_CONTINUATION = 1, + SND_NO_FIN = 2, + SND_COMPRESSED = 64 +}; + +template +static inline size_t formatMessage(char *dst, const char *src, size_t length, OpCode opCode, size_t reportedLength, bool compressed) { + size_t messageLength; + size_t headerLength; + if (reportedLength < 126) { + headerLength = 2; + dst[1] = reportedLength; + } else if (reportedLength <= UINT16_MAX) { + headerLength = 4; + dst[1] = 126; + *((uint16_t *) &dst[2]) = htons(reportedLength); + } else { + headerLength = 10; + dst[1] = 127; + *((uint64_t *) &dst[2]) = htobe64(reportedLength); + } + + int flags = 0; + dst[0] = (flags & SND_NO_FIN ? 0 : 128) | (compressed ? SND_COMPRESSED : 0); + if (!(flags & SND_CONTINUATION)) { + dst[0] |= opCode; + } + + char mask[4]; + if (!isServer) { + dst[1] |= 0x80; + uint32_t random = rand(); + memcpy(mask, &random, 4); + memcpy(dst + headerLength, &random, 4); + headerLength += 4; + } + + messageLength = headerLength + length; + memcpy(dst + headerLength, src, length); + + if (!isServer) { + + // overwrites up to 3 bytes outside of the given buffer! + //WebSocketProtocol::unmaskInplace(dst + headerLength, dst + headerLength + length, mask); + + // this is not optimal + char *start = dst + headerLength; + char *stop = start + length; + int i = 0; + while (start != stop) { + (*start++) ^= mask[i++ % 4]; + } + } + return messageLength; +} + +} + +// essentially this is only a parser template class WIN32_EXPORT WebSocketProtocol { public: @@ -135,12 +276,6 @@ protected: } } - enum { - SND_CONTINUATION = 1, - SND_NO_FIN = 2, - SND_COMPRESSED = 64 - }; - template static inline bool consumeMessage(T payLength, char *&src, unsigned int &length, WebSocketState *wState, void *user) { if (getOpCode(src)) { @@ -241,135 +376,6 @@ public: } - // Based on utf8_check.c by Markus Kuhn, 2005 - // https://www.cl.cam.ac.uk/~mgk25/ucs/utf8_check.c - // Optimized for predominantly 7-bit content by Alex Hultman, 2016 - // Licensed as Zlib, like the rest of this project - static bool isValidUtf8(unsigned char *s, size_t length) - { - for (unsigned char *e = s + length; s != e; ) { - if (s + 4 <= e && ((*(uint32_t *) s) & 0x80808080) == 0) { - s += 4; - } else { - while (!(*s & 0x80)) { - if (++s == e) { - return true; - } - } - - if ((s[0] & 0x60) == 0x40) { - if (s + 1 >= e || (s[1] & 0xc0) != 0x80 || (s[0] & 0xfe) == 0xc0) { - return false; - } - s += 2; - } else if ((s[0] & 0xf0) == 0xe0) { - if (s + 2 >= e || (s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80 || - (s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) || (s[0] == 0xed && (s[1] & 0xe0) == 0xa0)) { - return false; - } - s += 3; - } else if ((s[0] & 0xf8) == 0xf0) { - if (s + 3 >= e || (s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80 || (s[3] & 0xc0) != 0x80 || - (s[0] == 0xf0 && (s[1] & 0xf0) == 0x80) || (s[0] == 0xf4 && s[1] > 0x8f) || s[0] > 0xf4) { - return false; - } - s += 4; - } else { - return false; - } - } - } - return true; - } - - struct CloseFrame { - uint16_t code; - char *message; - size_t length; - }; - - static inline CloseFrame parseClosePayload(char *src, size_t length) { - CloseFrame cf = {}; - if (length >= 2) { - memcpy(&cf.code, src, 2); - cf = {ntohs(cf.code), src + 2, length - 2}; - if (cf.code < 1000 || cf.code > 4999 || (cf.code > 1011 && cf.code < 4000) || - (cf.code >= 1004 && cf.code <= 1006) || !isValidUtf8((unsigned char *) cf.message, cf.length)) { - return {}; - } - } - return cf; - } - - static inline size_t formatClosePayload(char *dst, uint16_t code, const char *message, size_t length) { - if (code) { - code = htons(code); - memcpy(dst, &code, 2); - memcpy(dst + 2, message, length); - return length + 2; - } - return 0; - } - - static inline size_t messageFrameSize(size_t messageSize) { - if (messageSize < 126) { - return 2 + messageSize; - } else if (messageSize <= UINT16_MAX) { - return 4 + messageSize; - } - return 10 + messageSize; - } - - static inline size_t formatMessage(char *dst, const char *src, size_t length, OpCode opCode, size_t reportedLength, bool compressed) { - size_t messageLength; - size_t headerLength; - if (reportedLength < 126) { - headerLength = 2; - dst[1] = reportedLength; - } else if (reportedLength <= UINT16_MAX) { - headerLength = 4; - dst[1] = 126; - *((uint16_t *) &dst[2]) = htons(reportedLength); - } else { - headerLength = 10; - dst[1] = 127; - *((uint64_t *) &dst[2]) = htobe64(reportedLength); - } - - int flags = 0; - dst[0] = (flags & SND_NO_FIN ? 0 : 128) | (compressed ? SND_COMPRESSED : 0); - if (!(flags & SND_CONTINUATION)) { - dst[0] |= opCode; - } - - char mask[4]; - if (!isServer) { - dst[1] |= 0x80; - uint32_t random = rand(); - memcpy(mask, &random, 4); - memcpy(dst + headerLength, &random, 4); - headerLength += 4; - } - - messageLength = headerLength + length; - memcpy(dst + headerLength, src, length); - - if (!isServer) { - - // overwrites up to 3 bytes outside of the given buffer! - //WebSocketProtocol::unmaskInplace(dst + headerLength, dst + headerLength + length, mask); - - // this is not optimal - char *start = dst + headerLength; - char *stop = start + length; - int i = 0; - while (start != stop) { - (*start++) ^= mask[i++ % 4]; - } - } - return messageLength; - } - static inline void consume(char *src, unsigned int length, WebSocketState *wState, void *user) { if (wState->state.spillLength) { src -= wState->state.spillLength;