BVB Source Codes

uWebSockets Show WebSocketProtocol.h Source code

Return Download uWebSockets: download WebSocketProtocol.h Source code - Download uWebSockets Source code - Type:.h
  1. #ifndef WEBSOCKETPROTOCOL_UWS_H
  2. #define WEBSOCKETPROTOCOL_UWS_H
  3.  
  4. // we do need to include this for htobe64, should be moved from networking!
  5. #include "Networking.h"
  6.  
  7. #include <cstring>
  8. #include <cstdlib>
  9.  
  10. namespace uWS {
  11.  
  12. enum OpCode : unsigned char {
  13.     TEXT = 1,
  14.     BINARY = 2,
  15.     CLOSE = 8,
  16.     PING = 9,
  17.     PONG = 10
  18. };
  19.  
  20. enum {
  21.     CLIENT,
  22.     SERVER
  23. };
  24.  
  25. // 24 bytes perfectly
  26. template <bool isServer>
  27. struct WebSocketState {
  28. public:
  29.     static const unsigned int SHORT_MESSAGE_HEADER = isServer ? 6 : 2;
  30.     static const unsigned int MEDIUM_MESSAGE_HEADER = isServer ? 8 : 4;
  31.     static const unsigned int LONG_MESSAGE_HEADER = isServer ? 14 : 10;
  32.  
  33.     // 16 bytes
  34.     struct State {
  35.         unsigned int wantsHead : 1;
  36.         unsigned int spillLength : 4;
  37.         int opStack : 2; // -1, 0, 1
  38.         unsigned int lastFin : 1;
  39.  
  40.         // 15 bytes
  41.         unsigned char spill[LONG_MESSAGE_HEADER - 1];
  42.         OpCode opCode[2];
  43.  
  44.         State() {
  45.             wantsHead = true;
  46.             spillLength = 0;
  47.             opStack = -1;
  48.             lastFin = true;
  49.         }
  50.  
  51.     } state;
  52.  
  53.     // 8 bytes
  54.     unsigned int remainingBytes = 0;
  55.     char mask[isServer ? 4 : 1];
  56. };
  57.  
  58. template <const bool isServer, class Impl>
  59. class WIN32_EXPORT WebSocketProtocol {
  60. public:
  61.     static const unsigned int SHORT_MESSAGE_HEADER = isServer ? 6 : 2;
  62.     static const unsigned int MEDIUM_MESSAGE_HEADER = isServer ? 8 : 4;
  63.     static const unsigned int LONG_MESSAGE_HEADER = isServer ? 14 : 10;
  64.  
  65. private:
  66.     static inline bool isFin(char *frame) {return *((unsigned char *) frame) & 128;}
  67.     static inline unsigned char getOpCode(char *frame) {return *((unsigned char *) frame) & 15;}
  68.     static inline unsigned char payloadLength(char *frame) {return ((unsigned char *) frame)[1] & 127;}
  69.     static inline bool rsv23(char *frame) {return *((unsigned char *) frame) & 48;}
  70.     static inline bool rsv1(char *frame) {return *((unsigned char *) frame) & 64;}
  71.  
  72.     static inline void unmaskImprecise(char *dst, char *src, char *mask, unsigned int length) {
  73.         for (unsigned int n = (length >> 2) + 1; n; n--) {
  74.             *(dst++) = *(src++) ^ mask[0];
  75.             *(dst++) = *(src++) ^ mask[1];
  76.             *(dst++) = *(src++) ^ mask[2];
  77.             *(dst++) = *(src++) ^ mask[3];
  78.         }
  79.     }
  80.  
  81.     static inline void unmaskImpreciseCopyMask(char *dst, char *src, char *maskPtr, unsigned int length) {
  82.         char mask[4] = {maskPtr[0], maskPtr[1], maskPtr[2], maskPtr[3]};
  83.         unmaskImprecise(dst, src, mask, length);
  84.     }
  85.  
  86.     static inline void rotateMask(unsigned int offset, char *mask) {
  87.         char originalMask[4] = {mask[0], mask[1], mask[2], mask[3]};
  88.         mask[(0 + offset) % 4] = originalMask[0];
  89.         mask[(1 + offset) % 4] = originalMask[1];
  90.         mask[(2 + offset) % 4] = originalMask[2];
  91.         mask[(3 + offset) % 4] = originalMask[3];
  92.     }
  93.  
  94.     static inline void unmaskInplace(char *data, char *stop, char *mask) {
  95.         while (data < stop) {
  96.             *(data++) ^= mask[0];
  97.             *(data++) ^= mask[1];
  98.             *(data++) ^= mask[2];
  99.             *(data++) ^= mask[3];
  100.         }
  101.     }
  102.  
  103.     enum {
  104.         SND_CONTINUATION = 1,
  105.         SND_NO_FIN = 2,
  106.         SND_COMPRESSED = 64
  107.     };
  108.  
  109.     template <unsigned int MESSAGE_HEADER, typename T>
  110.     static inline bool consumeMessage(T payLength, char *&src, unsigned int &length, WebSocketState<isServer> *wState) {
  111.         if (getOpCode(src)) {
  112.             if (wState->state.opStack == 1 || (!wState->state.lastFin && getOpCode(src) < 2)) {
  113.                 Impl::forceClose(wState);
  114.                 return true;
  115.             }
  116.             wState->state.opCode[++wState->state.opStack] = (OpCode) getOpCode(src);
  117.         } else if (wState->state.opStack == -1) {
  118.             Impl::forceClose(wState);
  119.             return true;
  120.         }
  121.         wState->state.lastFin = isFin(src);
  122.  
  123.         if (Impl::refusePayloadLength(payLength, wState)) {
  124.             Impl::forceClose(wState);
  125.             return true;
  126.         }
  127.  
  128.         if (payLength + MESSAGE_HEADER <= length) {
  129.             if (isServer) {
  130.                 unmaskImpreciseCopyMask(src + MESSAGE_HEADER - 4, src + MESSAGE_HEADER, src + MESSAGE_HEADER - 4, payLength);
  131.                 if (Impl::handleFragment(src + MESSAGE_HEADER - 4, payLength, 0, wState->state.opCode[wState->state.opStack], isFin(src), wState)) {
  132.                     return true;
  133.                 }
  134.             } else {
  135.                 if (Impl::handleFragment(src + MESSAGE_HEADER, payLength, 0, wState->state.opCode[wState->state.opStack], isFin(src), wState)) {
  136.                     return true;
  137.                 }
  138.             }
  139.  
  140.             if (isFin(src)) {
  141.                 wState->state.opStack--;
  142.             }
  143.  
  144.             src += payLength + MESSAGE_HEADER;
  145.             length -= payLength + MESSAGE_HEADER;
  146.             wState->state.spillLength = 0;
  147.             return false;
  148.         } else {
  149.             wState->state.spillLength = 0;
  150.             wState->state.wantsHead = false;
  151.             wState->remainingBytes = payLength - length + MESSAGE_HEADER;
  152.             bool fin = isFin(src);
  153.             if (isServer) {
  154.                 memcpy(wState->mask, src + MESSAGE_HEADER - 4, 4);
  155.                 unmaskImprecise(src, src + MESSAGE_HEADER, wState->mask, length - MESSAGE_HEADER);
  156.                 rotateMask(4 - (length - MESSAGE_HEADER) % 4, wState->mask);
  157.             } else {
  158.                 src += MESSAGE_HEADER;
  159.             }
  160.             Impl::handleFragment(src, length - MESSAGE_HEADER, wState->remainingBytes, wState->state.opCode[wState->state.opStack], fin, wState);
  161.             return true;
  162.         }
  163.     }
  164.  
  165.     static inline bool consumeContinuation(char *&src, unsigned int &length, WebSocketState<isServer> *wState) {
  166.         if (wState->remainingBytes <= length) {
  167.             if (isServer) {
  168.                 int n = wState->remainingBytes >> 2;
  169.                 unmaskInplace(src, src + n * 4, wState->mask);
  170.                 for (int i = 0, s = wState->remainingBytes % 4; i < s; i++) {
  171.                     src[n * 4 + i] ^= wState->mask[i];
  172.                 }
  173.             }
  174.  
  175.             if (Impl::handleFragment(src, wState->remainingBytes, 0, wState->state.opCode[wState->state.opStack], wState->state.lastFin, wState)) {
  176.                 return false;
  177.             }
  178.  
  179.             if (wState->state.lastFin) {
  180.                 wState->state.opStack--;
  181.             }
  182.  
  183.             src += wState->remainingBytes;
  184.             length -= wState->remainingBytes;
  185.             wState->state.wantsHead = true;
  186.             return true;
  187.         } else {
  188.             if (isServer) {
  189.                 unmaskInplace(src, src + ((length >> 2) + 1) * 4, wState->mask);
  190.             }
  191.  
  192.             wState->remainingBytes -= length;
  193.             if (Impl::handleFragment(src, length, wState->remainingBytes, wState->state.opCode[wState->state.opStack], wState->state.lastFin, wState)) {
  194.                 return false;
  195.             }
  196.  
  197.             if (isServer && length % 4) {
  198.                 rotateMask(4 - (length % 4), wState->mask);
  199.             }
  200.             return false;
  201.         }
  202.     }
  203.  
  204. public:
  205.     WebSocketProtocol() {
  206.  
  207.     }
  208.  
  209.     // Based on utf8_check.c by Markus Kuhn, 2005
  210.     // https://www.cl.cam.ac.uk/~mgk25/ucs/utf8_check.c
  211.     // Optimized for predominantly 7-bit content by Alex Hultman, 2016
  212.     // Licensed as Zlib, like the rest of this project
  213.     static bool isValidUtf8(unsigned char *s, size_t length)
  214.     {
  215.         for (unsigned char *e = s + length; s != e; ) {
  216.             if (s + 4 <= e && ((*(uint32_t *) s) & 0x80808080) == 0) {
  217.                 s += 4;
  218.             } else {
  219.                 while (!(*s & 0x80)) {
  220.                     if (++s == e) {
  221.                         return true;
  222.                     }
  223.                 }
  224.  
  225.                 if ((s[0] & 0x60) == 0x40) {
  226.                     if (s + 1 >= e || (s[1] & 0xc0) != 0x80 || (s[0] & 0xfe) == 0xc0) {
  227.                         return false;
  228.                     }
  229.                     s += 2;
  230.                 } else if ((s[0] & 0xf0) == 0xe0) {
  231.                     if (s + 2 >= e || (s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80 ||
  232.                             (s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) || (s[0] == 0xed && (s[1] & 0xe0) == 0xa0)) {
  233.                         return false;
  234.                     }
  235.                     s += 3;
  236.                 } else if ((s[0] & 0xf8) == 0xf0) {
  237.                     if (s + 3 >= e || (s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80 || (s[3] & 0xc0) != 0x80 ||
  238.                             (s[0] == 0xf0 && (s[1] & 0xf0) == 0x80) || (s[0] == 0xf4 && s[1] > 0x8f) || s[0] > 0xf4) {
  239.                         return false;
  240.                     }
  241.                     s += 4;
  242.                 } else {
  243.                     return false;
  244.                 }
  245.             }
  246.         }
  247.         return true;
  248.     }
  249.  
  250.     struct CloseFrame {
  251.         uint16_t code;
  252.         char *message;
  253.         size_t length;
  254.     };
  255.  
  256.     static inline CloseFrame parseClosePayload(char *src, size_t length) {
  257.         CloseFrame cf = {};
  258.         if (length >= 2) {
  259.             memcpy(&cf.code, src, 2);
  260.             cf = {ntohs(cf.code), src + 2, length - 2};
  261.             if (cf.code < 1000 || cf.code > 4999 || (cf.code > 1011 && cf.code < 4000) ||
  262.                 (cf.code >= 1004 && cf.code <= 1006) || !isValidUtf8((unsigned char *) cf.message, cf.length)) {
  263.                 return {};
  264.             }
  265.         }
  266.         return cf;
  267.     }
  268.  
  269.     static inline size_t formatClosePayload(char *dst, uint16_t code, const char *message, size_t length) {
  270.         if (code) {
  271.             code = htons(code);
  272.             memcpy(dst, &code, 2);
  273.             memcpy(dst + 2, message, length);
  274.             return length + 2;
  275.         }
  276.         return 0;
  277.     }
  278.  
  279.     static inline size_t formatMessage(char *dst, const char *src, size_t length, OpCode opCode, size_t reportedLength, bool compressed) {
  280.         size_t messageLength;
  281.         size_t headerLength;
  282.         if (reportedLength < 126) {
  283.             headerLength = 2;
  284.             dst[1] = reportedLength;
  285.         } else if (reportedLength <= UINT16_MAX) {
  286.             headerLength = 4;
  287.             dst[1] = 126;
  288.             *((uint16_t *) &dst[2]) = htons(reportedLength);
  289.         } else {
  290.             headerLength = 10;
  291.             dst[1] = 127;
  292.             *((uint64_t *) &dst[2]) = htobe64(reportedLength);
  293.         }
  294.  
  295.         int flags = 0;
  296.         dst[0] = (flags & SND_NO_FIN ? 0 : 128) | (compressed ? SND_COMPRESSED : 0);
  297.         if (!(flags & SND_CONTINUATION)) {
  298.             dst[0] |= opCode;
  299.         }
  300.  
  301.         char mask[4];
  302.         if (!isServer) {
  303.             dst[1] |= 0x80;
  304.             uint32_t random = rand();
  305.             memcpy(mask, &random, 4);
  306.             memcpy(dst + headerLength, &random, 4);
  307.             headerLength += 4;
  308.         }
  309.  
  310.         messageLength = headerLength + length;
  311.         memcpy(dst + headerLength, src, length);
  312.  
  313.         if (!isServer) {
  314.  
  315.             // overwrites up to 3 bytes outside of the given buffer!
  316.             //WebSocketProtocol<isServer>::unmaskInplace(dst + headerLength, dst + headerLength + length, mask);
  317.  
  318.             // this is not optimal
  319.             char *start = dst + headerLength;
  320.             char *stop = start + length;
  321.             int i = 0;
  322.             while (start != stop) {
  323.                 (*start++) ^= mask[i++ % 4];
  324.             }
  325.         }
  326.         return messageLength;
  327.     }
  328.  
  329.     static inline void consume(char *src, unsigned int length, WebSocketState<isServer> *wState) {
  330.         if (wState->state.spillLength) {
  331.             src -= wState->state.spillLength;
  332.             length += wState->state.spillLength;
  333.             memcpy(src, wState->state.spill, wState->state.spillLength);
  334.         }
  335.         if (wState->state.wantsHead) {
  336.             parseNext:
  337.             while (length >= SHORT_MESSAGE_HEADER) {
  338.  
  339.                 // invalid reserved bits / invalid opcodes / invalid control frames / set compressed frame
  340.                 if ((rsv1(src) && !Impl::setCompressed(wState)) || rsv23(src) || (getOpCode(src) > 2 && getOpCode(src) < 8) ||
  341.                     getOpCode(src) > 10 || (getOpCode(src) > 2 && (!isFin(src) || payloadLength(src) > 125))) {
  342.                     Impl::forceClose(wState);
  343.                     return;
  344.                 }
  345.  
  346.                 if (payloadLength(src) < 126) {
  347.                     if (consumeMessage<SHORT_MESSAGE_HEADER, uint8_t>(payloadLength(src), src, length, wState)) {
  348.                         return;
  349.                     }
  350.                 } else if (payloadLength(src) == 126) {
  351.                     if (length < MEDIUM_MESSAGE_HEADER) {
  352.                         break;
  353.                     } else if(consumeMessage<MEDIUM_MESSAGE_HEADER, uint16_t>(ntohs(*(uint16_t *) &src[2]), src, length, wState)) {
  354.                         return;
  355.                     }
  356.                 } else if (length < LONG_MESSAGE_HEADER) {
  357.                     break;
  358.                 } else if (consumeMessage<LONG_MESSAGE_HEADER, uint64_t>(be64toh(*(uint64_t *) &src[2]), src, length, wState)) {
  359.                     return;
  360.                 }
  361.             }
  362.             if (length) {
  363.                 memcpy(wState->state.spill, src, length);
  364.                 wState->state.spillLength = length;
  365.             }
  366.         } else if (consumeContinuation(src, length, wState)) {
  367.             goto parseNext;
  368.         }
  369.     }
  370.  
  371.     static const int CONSUME_POST_PADDING = 4;
  372.     static const int CONSUME_PRE_PADDING = LONG_MESSAGE_HEADER - 1;
  373. };
  374.  
  375. }
  376.  
  377. #endif // WEBSOCKETPROTOCOL_UWS_H
  378.  
downloadWebSocketProtocol.h Source code - Download uWebSockets Source code
Related Source Codes/Software:
realworld - TodoMVC for the RealWorld - Exemplary fullstack Me... 2017-06-11
goreplay - GoReplay is an open-source tool for capturing and ... 2017-06-10
pyenv - Simple Python version management 2017-06-10
redux-saga - An alternative side effect model for Redux apps ... 2017-06-10
angular-starter - 2017-06-10
rkt - rkt is a pod-native container engine for Linux. It... 2017-06-11
reactide - Reactide is the first dedicated IDE for React web ... 2017-06-11
postal - 2017-06-11
CRYENGINE - CRYENGINE is a powerful real-time game development... 2017-06-11
uWebSockets - Tiny WebSockets https://for... 2017-06-11

 Back to top