Simple WebSocket server library.
Dependents: WebSocketServerTest
WebSocketConnection.cpp@0:a816c25e83ed, 2015-03-16 (annotated)
- Committer:
- flatbird
- Date:
- Mon Mar 16 10:13:30 2015 +0000
- Revision:
- 0:a816c25e83ed
- Child:
- 1:db4114d55f83
WebSocketServer library
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
flatbird | 0:a816c25e83ed | 1 | #include "WebSocketConnection.h" |
flatbird | 0:a816c25e83ed | 2 | #include "WebSocketServer.h" |
flatbird | 0:a816c25e83ed | 3 | #include "sha1.h" |
flatbird | 0:a816c25e83ed | 4 | |
flatbird | 0:a816c25e83ed | 5 | #define UPGRADE_WEBSOCKET "Upgrade: websocket" |
flatbird | 0:a816c25e83ed | 6 | #define SEC_WEBSOCKET_KEY "Sec-WebSocket-Key:" |
flatbird | 0:a816c25e83ed | 7 | #define MAGIC_NUMBER "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" |
flatbird | 0:a816c25e83ed | 8 | #define OP_CONT 0x0 |
flatbird | 0:a816c25e83ed | 9 | #define OP_TEXT 0x1 |
flatbird | 0:a816c25e83ed | 10 | #define OP_BINARY 0x2 |
flatbird | 0:a816c25e83ed | 11 | #define OP_CLOSE 0x8 |
flatbird | 0:a816c25e83ed | 12 | #define OP_PING 0x9 |
flatbird | 0:a816c25e83ed | 13 | #define OP_PONG 0xA |
flatbird | 0:a816c25e83ed | 14 | |
flatbird | 0:a816c25e83ed | 15 | WebSocketConnection::WebSocketConnection(WebSocketServer* server) |
flatbird | 0:a816c25e83ed | 16 | { |
flatbird | 0:a816c25e83ed | 17 | mServer = server; |
flatbird | 0:a816c25e83ed | 18 | } |
flatbird | 0:a816c25e83ed | 19 | |
flatbird | 0:a816c25e83ed | 20 | WebSocketConnection::~WebSocketConnection() |
flatbird | 0:a816c25e83ed | 21 | { |
flatbird | 0:a816c25e83ed | 22 | } |
flatbird | 0:a816c25e83ed | 23 | |
flatbird | 0:a816c25e83ed | 24 | void WebSocketConnection::run() |
flatbird | 0:a816c25e83ed | 25 | { |
flatbird | 0:a816c25e83ed | 26 | char buf[1024]; |
flatbird | 0:a816c25e83ed | 27 | bool isWebSocket = false; |
flatbird | 0:a816c25e83ed | 28 | |
flatbird | 0:a816c25e83ed | 29 | // it doesn't work... |
flatbird | 0:a816c25e83ed | 30 | // mConnection.set_blocking(true); |
flatbird | 0:a816c25e83ed | 31 | |
flatbird | 0:a816c25e83ed | 32 | while (mConnection.is_connected()) { |
flatbird | 0:a816c25e83ed | 33 | int ret = mConnection.receive(buf, sizeof(buf) - 1); |
flatbird | 0:a816c25e83ed | 34 | if (ret == 0) { |
flatbird | 0:a816c25e83ed | 35 | // printf("No data to receive\r\n"); |
flatbird | 0:a816c25e83ed | 36 | continue; |
flatbird | 0:a816c25e83ed | 37 | } |
flatbird | 0:a816c25e83ed | 38 | if (ret < 0) { |
flatbird | 0:a816c25e83ed | 39 | printf("ERROR: Failed to receive %d\r\n", ret); |
flatbird | 0:a816c25e83ed | 40 | break; |
flatbird | 0:a816c25e83ed | 41 | } |
flatbird | 0:a816c25e83ed | 42 | if (!isWebSocket) { |
flatbird | 0:a816c25e83ed | 43 | if (this->handleHTTP(buf, ret)) { |
flatbird | 0:a816c25e83ed | 44 | isWebSocket = true; |
flatbird | 0:a816c25e83ed | 45 | } else { |
flatbird | 0:a816c25e83ed | 46 | printf("ERROR: Non websocket\r\n"); |
flatbird | 0:a816c25e83ed | 47 | break; |
flatbird | 0:a816c25e83ed | 48 | } |
flatbird | 0:a816c25e83ed | 49 | } else { |
flatbird | 0:a816c25e83ed | 50 | if (!this->handleWebSocket(buf, ret)) { |
flatbird | 0:a816c25e83ed | 51 | break; |
flatbird | 0:a816c25e83ed | 52 | } |
flatbird | 0:a816c25e83ed | 53 | } |
flatbird | 0:a816c25e83ed | 54 | } |
flatbird | 0:a816c25e83ed | 55 | // printf("Closed\r\n"); |
flatbird | 0:a816c25e83ed | 56 | mConnection.close(); |
flatbird | 0:a816c25e83ed | 57 | } |
flatbird | 0:a816c25e83ed | 58 | |
flatbird | 0:a816c25e83ed | 59 | bool WebSocketConnection::handleHTTP(char* buf, int size) |
flatbird | 0:a816c25e83ed | 60 | { |
flatbird | 0:a816c25e83ed | 61 | char* line = &buf[0]; |
flatbird | 0:a816c25e83ed | 62 | char key[128]; |
flatbird | 0:a816c25e83ed | 63 | bool isUpgradeWebSocket = false; |
flatbird | 0:a816c25e83ed | 64 | bool isSecWebSocketKeyFound = false; |
flatbird | 0:a816c25e83ed | 65 | |
flatbird | 0:a816c25e83ed | 66 | for (int i = 0; i < size; i++) { |
flatbird | 0:a816c25e83ed | 67 | if (buf[i] == '\r' && i+1 < size && buf[i+1] == '\n') { |
flatbird | 0:a816c25e83ed | 68 | buf[i] = '\0'; |
flatbird | 0:a816c25e83ed | 69 | if (strlen(buf) <= 0) { |
flatbird | 0:a816c25e83ed | 70 | break; |
flatbird | 0:a816c25e83ed | 71 | } |
flatbird | 0:a816c25e83ed | 72 | printf("[%s]\r\n", line); |
flatbird | 0:a816c25e83ed | 73 | if (line == &buf[0]) { |
flatbird | 0:a816c25e83ed | 74 | char* method = strtok(buf, " "); |
flatbird | 0:a816c25e83ed | 75 | char* path = strtok(NULL, " "); |
flatbird | 0:a816c25e83ed | 76 | char* version = strtok(NULL, " "); |
flatbird | 0:a816c25e83ed | 77 | // printf("[%s] [%s] [%s]\r\n", method, path, version); |
flatbird | 0:a816c25e83ed | 78 | mHandler = mServer->getHandler(path); |
flatbird | 0:a816c25e83ed | 79 | if (!mHandler) { |
flatbird | 0:a816c25e83ed | 80 | printf("ERROR: Handler not found for %s\r\n", path); |
flatbird | 0:a816c25e83ed | 81 | return false; |
flatbird | 0:a816c25e83ed | 82 | } |
flatbird | 0:a816c25e83ed | 83 | } else if (strncmp(line, UPGRADE_WEBSOCKET, strlen(UPGRADE_WEBSOCKET)) == 0) { |
flatbird | 0:a816c25e83ed | 84 | isUpgradeWebSocket = true; |
flatbird | 0:a816c25e83ed | 85 | } else if (strncmp(line, SEC_WEBSOCKET_KEY, strlen(SEC_WEBSOCKET_KEY)) == 0) { |
flatbird | 0:a816c25e83ed | 86 | isSecWebSocketKeyFound = true; |
flatbird | 0:a816c25e83ed | 87 | char* ptr = line + strlen(SEC_WEBSOCKET_KEY); |
flatbird | 0:a816c25e83ed | 88 | while (*ptr == ' ') ++ptr; |
flatbird | 0:a816c25e83ed | 89 | strcpy(key, ptr); |
flatbird | 0:a816c25e83ed | 90 | } |
flatbird | 0:a816c25e83ed | 91 | i += 2; |
flatbird | 0:a816c25e83ed | 92 | line = &buf[i]; |
flatbird | 0:a816c25e83ed | 93 | } |
flatbird | 0:a816c25e83ed | 94 | } |
flatbird | 0:a816c25e83ed | 95 | |
flatbird | 0:a816c25e83ed | 96 | if (isUpgradeWebSocket && isSecWebSocketKeyFound) { |
flatbird | 0:a816c25e83ed | 97 | this->sendUpgradeResponse(key); |
flatbird | 0:a816c25e83ed | 98 | if (mHandler) { |
flatbird | 0:a816c25e83ed | 99 | mHandler->onOpen(); |
flatbird | 0:a816c25e83ed | 100 | } |
flatbird | 0:a816c25e83ed | 101 | mPrevFin = true; |
flatbird | 0:a816c25e83ed | 102 | return true; |
flatbird | 0:a816c25e83ed | 103 | } |
flatbird | 0:a816c25e83ed | 104 | |
flatbird | 0:a816c25e83ed | 105 | return false; |
flatbird | 0:a816c25e83ed | 106 | } |
flatbird | 0:a816c25e83ed | 107 | |
flatbird | 0:a816c25e83ed | 108 | bool WebSocketConnection::handleWebSocket(char* buf, int size) |
flatbird | 0:a816c25e83ed | 109 | { |
flatbird | 0:a816c25e83ed | 110 | uint8_t* ptr = (uint8_t*)buf; |
flatbird | 0:a816c25e83ed | 111 | |
flatbird | 0:a816c25e83ed | 112 | bool fin = (*ptr & 0x80) == 0x80; |
flatbird | 0:a816c25e83ed | 113 | uint8_t opcode = *ptr & 0xF; |
flatbird | 0:a816c25e83ed | 114 | |
flatbird | 0:a816c25e83ed | 115 | if (opcode == OP_PING) { |
flatbird | 0:a816c25e83ed | 116 | *ptr = ((*ptr & 0xF0) | OP_PONG); |
flatbird | 0:a816c25e83ed | 117 | mConnection.send_all(buf, size); |
flatbird | 0:a816c25e83ed | 118 | return true; |
flatbird | 0:a816c25e83ed | 119 | } |
flatbird | 0:a816c25e83ed | 120 | if (opcode == OP_CLOSE) { |
flatbird | 0:a816c25e83ed | 121 | if (mHandler) { |
flatbird | 0:a816c25e83ed | 122 | mHandler->onClose(); |
flatbird | 0:a816c25e83ed | 123 | } |
flatbird | 0:a816c25e83ed | 124 | return false; |
flatbird | 0:a816c25e83ed | 125 | } |
flatbird | 0:a816c25e83ed | 126 | ptr++; |
flatbird | 0:a816c25e83ed | 127 | |
flatbird | 0:a816c25e83ed | 128 | if (!fin || !mPrevFin) { |
flatbird | 0:a816c25e83ed | 129 | printf("WARN: Data consists of multiple frame not supported\r\n"); |
flatbird | 0:a816c25e83ed | 130 | mPrevFin = fin; |
flatbird | 0:a816c25e83ed | 131 | return true; // not an error, just discard it |
flatbird | 0:a816c25e83ed | 132 | } |
flatbird | 0:a816c25e83ed | 133 | mPrevFin = fin; |
flatbird | 0:a816c25e83ed | 134 | |
flatbird | 0:a816c25e83ed | 135 | bool mask = (*ptr & 0x80) == 0x80; |
flatbird | 0:a816c25e83ed | 136 | uint8_t len = *ptr & 0x7F; |
flatbird | 0:a816c25e83ed | 137 | ptr++; |
flatbird | 0:a816c25e83ed | 138 | |
flatbird | 0:a816c25e83ed | 139 | if (len > 125) { |
flatbird | 0:a816c25e83ed | 140 | printf("WARN: Extended payload length not supported\r\n"); |
flatbird | 0:a816c25e83ed | 141 | return true; // not an error, just discard it |
flatbird | 0:a816c25e83ed | 142 | } |
flatbird | 0:a816c25e83ed | 143 | |
flatbird | 0:a816c25e83ed | 144 | char* data; |
flatbird | 0:a816c25e83ed | 145 | if (mask) { |
flatbird | 0:a816c25e83ed | 146 | char* maskingKey = (char*)ptr; |
flatbird | 0:a816c25e83ed | 147 | data = (char*)(ptr + 4); |
flatbird | 0:a816c25e83ed | 148 | for (int i = 0; i < len; i++) { |
flatbird | 0:a816c25e83ed | 149 | data[i] = data[i] ^ maskingKey[(i % 4)]; |
flatbird | 0:a816c25e83ed | 150 | } |
flatbird | 0:a816c25e83ed | 151 | } else { |
flatbird | 0:a816c25e83ed | 152 | data = (char*)ptr; |
flatbird | 0:a816c25e83ed | 153 | } |
flatbird | 0:a816c25e83ed | 154 | if (mHandler) { |
flatbird | 0:a816c25e83ed | 155 | if (opcode == OP_TEXT) { |
flatbird | 0:a816c25e83ed | 156 | data[len] = '\0'; |
flatbird | 0:a816c25e83ed | 157 | mHandler->onMessage(data); |
flatbird | 0:a816c25e83ed | 158 | } else if (opcode == OP_BINARY) { |
flatbird | 0:a816c25e83ed | 159 | mHandler->onMessage(data, len); |
flatbird | 0:a816c25e83ed | 160 | } |
flatbird | 0:a816c25e83ed | 161 | } |
flatbird | 0:a816c25e83ed | 162 | return true; |
flatbird | 0:a816c25e83ed | 163 | } |
flatbird | 0:a816c25e83ed | 164 | |
flatbird | 0:a816c25e83ed | 165 | char* base64Encode(const uint8_t* data, size_t size, |
flatbird | 0:a816c25e83ed | 166 | char* outputBuffer, size_t outputBufferSize) |
flatbird | 0:a816c25e83ed | 167 | { |
flatbird | 0:a816c25e83ed | 168 | static char encodingTable[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', |
flatbird | 0:a816c25e83ed | 169 | 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', |
flatbird | 0:a816c25e83ed | 170 | 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', |
flatbird | 0:a816c25e83ed | 171 | 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', |
flatbird | 0:a816c25e83ed | 172 | 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', |
flatbird | 0:a816c25e83ed | 173 | 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', |
flatbird | 0:a816c25e83ed | 174 | 'w', 'x', 'y', 'z', '0', '1', '2', '3', |
flatbird | 0:a816c25e83ed | 175 | '4', '5', '6', '7', '8', '9', '+', '/'}; |
flatbird | 0:a816c25e83ed | 176 | size_t outputLength = 4 * ((size + 2) / 3); |
flatbird | 0:a816c25e83ed | 177 | if (outputBufferSize - 1 < outputLength) { // -1 for NUL |
flatbird | 0:a816c25e83ed | 178 | return NULL; |
flatbird | 0:a816c25e83ed | 179 | } |
flatbird | 0:a816c25e83ed | 180 | |
flatbird | 0:a816c25e83ed | 181 | for (size_t i = 0, j = 0; i < size; /* nothing */) { |
flatbird | 0:a816c25e83ed | 182 | uint32_t octet1 = i < size ? (unsigned char)data[i++] : 0; |
flatbird | 0:a816c25e83ed | 183 | uint32_t octet2 = i < size ? (unsigned char)data[i++] : 0; |
flatbird | 0:a816c25e83ed | 184 | uint32_t octet3 = i < size ? (unsigned char)data[i++] : 0; |
flatbird | 0:a816c25e83ed | 185 | |
flatbird | 0:a816c25e83ed | 186 | uint32_t triple = (octet1 << 0x10) + (octet2 << 0x08) + octet3; |
flatbird | 0:a816c25e83ed | 187 | |
flatbird | 0:a816c25e83ed | 188 | outputBuffer[j++] = encodingTable[(triple >> 3 * 6) & 0x3F]; |
flatbird | 0:a816c25e83ed | 189 | outputBuffer[j++] = encodingTable[(triple >> 2 * 6) & 0x3F]; |
flatbird | 0:a816c25e83ed | 190 | outputBuffer[j++] = encodingTable[(triple >> 1 * 6) & 0x3F]; |
flatbird | 0:a816c25e83ed | 191 | outputBuffer[j++] = encodingTable[(triple >> 0 * 6) & 0x3F]; |
flatbird | 0:a816c25e83ed | 192 | } |
flatbird | 0:a816c25e83ed | 193 | |
flatbird | 0:a816c25e83ed | 194 | static int padTable[] = { 0, 2, 1 }; |
flatbird | 0:a816c25e83ed | 195 | int paddingCount = padTable[size % 3]; |
flatbird | 0:a816c25e83ed | 196 | |
flatbird | 0:a816c25e83ed | 197 | for (int i = 0; i < paddingCount; i++) { |
flatbird | 0:a816c25e83ed | 198 | outputBuffer[outputLength - 1 - i] = '='; |
flatbird | 0:a816c25e83ed | 199 | } |
flatbird | 0:a816c25e83ed | 200 | outputBuffer[outputLength] = '\0'; // NUL |
flatbird | 0:a816c25e83ed | 201 | |
flatbird | 0:a816c25e83ed | 202 | return outputBuffer; |
flatbird | 0:a816c25e83ed | 203 | } |
flatbird | 0:a816c25e83ed | 204 | |
flatbird | 0:a816c25e83ed | 205 | bool WebSocketConnection::sendUpgradeResponse(char* key) |
flatbird | 0:a816c25e83ed | 206 | { |
flatbird | 0:a816c25e83ed | 207 | char buf[128]; |
flatbird | 0:a816c25e83ed | 208 | |
flatbird | 0:a816c25e83ed | 209 | if (strlen(key) + sizeof(MAGIC_NUMBER) > sizeof(buf)) { |
flatbird | 0:a816c25e83ed | 210 | return false; |
flatbird | 0:a816c25e83ed | 211 | } |
flatbird | 0:a816c25e83ed | 212 | strcpy(buf, key); |
flatbird | 0:a816c25e83ed | 213 | strcat(buf, MAGIC_NUMBER); |
flatbird | 0:a816c25e83ed | 214 | |
flatbird | 0:a816c25e83ed | 215 | uint8_t hash[20]; |
flatbird | 0:a816c25e83ed | 216 | SHA1Context sha; |
flatbird | 0:a816c25e83ed | 217 | SHA1Reset(&sha); |
flatbird | 0:a816c25e83ed | 218 | SHA1Input(&sha, (unsigned char*)buf, strlen(buf)); |
flatbird | 0:a816c25e83ed | 219 | SHA1Result(&sha, (uint8_t*)hash); |
flatbird | 0:a816c25e83ed | 220 | |
flatbird | 0:a816c25e83ed | 221 | char encoded[30]; |
flatbird | 0:a816c25e83ed | 222 | base64Encode(hash, 20, encoded, sizeof(encoded)); |
flatbird | 0:a816c25e83ed | 223 | |
flatbird | 0:a816c25e83ed | 224 | char resp[] = "HTTP/1.1 101 Switching Protocols\r\n" \ |
flatbird | 0:a816c25e83ed | 225 | "Upgrade: websocket\r\n" \ |
flatbird | 0:a816c25e83ed | 226 | "Connection: Upgrade\r\n" \ |
flatbird | 0:a816c25e83ed | 227 | "Sec-WebSocket-Accept: XXXXXXXXXXXXXXXXXXXXXXXXXXXXX\r\n\r\n"; |
flatbird | 0:a816c25e83ed | 228 | char* ptr = strstr(resp, "XXXXX"); |
flatbird | 0:a816c25e83ed | 229 | strcpy(ptr, encoded); |
flatbird | 0:a816c25e83ed | 230 | strcpy(ptr+strlen(encoded), "\r\n\r\n"); |
flatbird | 0:a816c25e83ed | 231 | |
flatbird | 0:a816c25e83ed | 232 | int ret = mConnection.send_all(resp, strlen(resp)); |
flatbird | 0:a816c25e83ed | 233 | if (ret < 0) { |
flatbird | 0:a816c25e83ed | 234 | printf("ERROR: Failed to send response\r\n"); |
flatbird | 0:a816c25e83ed | 235 | return false; |
flatbird | 0:a816c25e83ed | 236 | } |
flatbird | 0:a816c25e83ed | 237 | |
flatbird | 0:a816c25e83ed | 238 | return true; |
flatbird | 0:a816c25e83ed | 239 | } |