Simple WebSocket server library.

Dependents:   WebSocketServerTest

Committer:
flatbird
Date:
Mon Apr 06 12:55:41 2015 +0000
Revision:
2:db41b4e0d64b
Parent:
1:db4114d55f83
add LICENSE.txt (MIT License)

Who changed what in which revision?

UserRevisionLine numberNew 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 1:db4114d55f83 83 } else if (strncasecmp(line, UPGRADE_WEBSOCKET, strlen(UPGRADE_WEBSOCKET)) == 0) {
flatbird 0:a816c25e83ed 84 isUpgradeWebSocket = true;
flatbird 1:db4114d55f83 85 } else if (strncasecmp(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 }