HTTP Server, WebSocket support
Fork of HTTPD by
HTTPD_ws.cpp@0:d18dff347122, 2013-11-13 (annotated)
- Committer:
- okini3939
- Date:
- Wed Nov 13 01:58:04 2013 +0000
- Revision:
- 0:d18dff347122
- Child:
- 1:b724fdb741e7
1st build;
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
okini3939 | 0:d18dff347122 | 1 | /* Copyright (C) 2013 Hiroshi Suga, MIT License |
okini3939 | 0:d18dff347122 | 2 | * |
okini3939 | 0:d18dff347122 | 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software |
okini3939 | 0:d18dff347122 | 4 | * and associated documentation files (the "Software"), to deal in the Software without restriction, |
okini3939 | 0:d18dff347122 | 5 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, |
okini3939 | 0:d18dff347122 | 6 | * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is |
okini3939 | 0:d18dff347122 | 7 | * furnished to do so, subject to the following conditions: |
okini3939 | 0:d18dff347122 | 8 | * |
okini3939 | 0:d18dff347122 | 9 | * The above copyright notice and this permission notice shall be included in all copies or |
okini3939 | 0:d18dff347122 | 10 | * substantial portions of the Software. |
okini3939 | 0:d18dff347122 | 11 | * |
okini3939 | 0:d18dff347122 | 12 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING |
okini3939 | 0:d18dff347122 | 13 | * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
okini3939 | 0:d18dff347122 | 14 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, |
okini3939 | 0:d18dff347122 | 15 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
okini3939 | 0:d18dff347122 | 16 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
okini3939 | 0:d18dff347122 | 17 | */ |
okini3939 | 0:d18dff347122 | 18 | |
okini3939 | 0:d18dff347122 | 19 | #include "HTTPD.h" |
okini3939 | 0:d18dff347122 | 20 | #include "sha1.h" |
okini3939 | 0:d18dff347122 | 21 | |
okini3939 | 0:d18dff347122 | 22 | void HTTPD::recvWS (int id, char c) { |
okini3939 | 0:d18dff347122 | 23 | |
okini3939 | 0:d18dff347122 | 24 | switch (_state[id].mode) { |
okini3939 | 0:d18dff347122 | 25 | case MODE_WEBSOCKET: |
okini3939 | 0:d18dff347122 | 26 | if (_state[id].n == 0) { |
okini3939 | 0:d18dff347122 | 27 | // flag |
okini3939 | 0:d18dff347122 | 28 | _state[id].websocket_opcode = c & 0x0f; |
okini3939 | 0:d18dff347122 | 29 | _state[id].websocket_flg = c << 8; |
okini3939 | 0:d18dff347122 | 30 | _state[id].n ++; |
okini3939 | 0:d18dff347122 | 31 | } else |
okini3939 | 0:d18dff347122 | 32 | if (_state[id].n == 1) { |
okini3939 | 0:d18dff347122 | 33 | // length 7bit |
okini3939 | 0:d18dff347122 | 34 | _state[id].websocket_flg |= c; |
okini3939 | 0:d18dff347122 | 35 | _state[id].length = c & 0x7f; |
okini3939 | 0:d18dff347122 | 36 | _state[id].n ++; |
okini3939 | 0:d18dff347122 | 37 | if (_state[id].length < 126) { |
okini3939 | 0:d18dff347122 | 38 | _state[id].n = 0; |
okini3939 | 0:d18dff347122 | 39 | if (_state[id].length) { |
okini3939 | 0:d18dff347122 | 40 | if (_state[id].websocket_flg & 0x0080) { |
okini3939 | 0:d18dff347122 | 41 | _state[id].mode = MODE_WEBSOCKET_MASK; |
okini3939 | 0:d18dff347122 | 42 | } else { |
okini3939 | 0:d18dff347122 | 43 | _state[id].mode = MODE_WEBSOCKET_BODY; |
okini3939 | 0:d18dff347122 | 44 | } |
okini3939 | 0:d18dff347122 | 45 | } else { |
okini3939 | 0:d18dff347122 | 46 | _state[id].mode = MODE_WEBSOCKET_ENTER; |
okini3939 | 0:d18dff347122 | 47 | } |
okini3939 | 0:d18dff347122 | 48 | DBG("ws length %d\r\n", _state[id].length); |
okini3939 | 0:d18dff347122 | 49 | } |
okini3939 | 0:d18dff347122 | 50 | } else { |
okini3939 | 0:d18dff347122 | 51 | // length 16bit,64bit |
okini3939 | 0:d18dff347122 | 52 | if (_state[id].n == 2) { |
okini3939 | 0:d18dff347122 | 53 | _state[id].length = c; |
okini3939 | 0:d18dff347122 | 54 | _state[id].n ++; |
okini3939 | 0:d18dff347122 | 55 | } else |
okini3939 | 0:d18dff347122 | 56 | if (_state[id].n < 9 && (_state[id].websocket_flg & 0x7f) == 127) { |
okini3939 | 0:d18dff347122 | 57 | // 64bit |
okini3939 | 0:d18dff347122 | 58 | _state[id].length = (_state[id].length << 8) | c; |
okini3939 | 0:d18dff347122 | 59 | _state[id].n ++; |
okini3939 | 0:d18dff347122 | 60 | } else { |
okini3939 | 0:d18dff347122 | 61 | // end |
okini3939 | 0:d18dff347122 | 62 | _state[id].length = (_state[id].length << 8) | c; |
okini3939 | 0:d18dff347122 | 63 | _state[id].n = 0; |
okini3939 | 0:d18dff347122 | 64 | if (_state[id].websocket_flg & 0x0080) { |
okini3939 | 0:d18dff347122 | 65 | _state[id].mode = MODE_WEBSOCKET_MASK; |
okini3939 | 0:d18dff347122 | 66 | } else { |
okini3939 | 0:d18dff347122 | 67 | _state[id].mode = MODE_WEBSOCKET_BODY; |
okini3939 | 0:d18dff347122 | 68 | } |
okini3939 | 0:d18dff347122 | 69 | DBG("ws length2 %d\r\n", _state[id].length); |
okini3939 | 0:d18dff347122 | 70 | } |
okini3939 | 0:d18dff347122 | 71 | } |
okini3939 | 0:d18dff347122 | 72 | break; |
okini3939 | 0:d18dff347122 | 73 | case MODE_WEBSOCKET_MASK: |
okini3939 | 0:d18dff347122 | 74 | // masking key |
okini3939 | 0:d18dff347122 | 75 | _state[id].websocket_mask[_state[id].n] = c; |
okini3939 | 0:d18dff347122 | 76 | _state[id].n ++; |
okini3939 | 0:d18dff347122 | 77 | if (_state[id].n >= 4) { |
okini3939 | 0:d18dff347122 | 78 | _state[id].n = 0; |
okini3939 | 0:d18dff347122 | 79 | _state[id].mode = MODE_WEBSOCKET_BODY; |
okini3939 | 0:d18dff347122 | 80 | DBG("ws mask\r\n"); |
okini3939 | 0:d18dff347122 | 81 | } |
okini3939 | 0:d18dff347122 | 82 | break; |
okini3939 | 0:d18dff347122 | 83 | case MODE_WEBSOCKET_BODY: |
okini3939 | 0:d18dff347122 | 84 | // payload |
okini3939 | 0:d18dff347122 | 85 | if (_state[id].websocket_flg & 0x0080) { |
okini3939 | 0:d18dff347122 | 86 | // un-mask |
okini3939 | 0:d18dff347122 | 87 | _state[id].buf->queue(c ^ _state[id].websocket_mask[_state[id].n & 0x03]); |
okini3939 | 0:d18dff347122 | 88 | } else { |
okini3939 | 0:d18dff347122 | 89 | _state[id].buf->queue(c); |
okini3939 | 0:d18dff347122 | 90 | } |
okini3939 | 0:d18dff347122 | 91 | _state[id].n ++; |
okini3939 | 0:d18dff347122 | 92 | if (_state[id].n >= _state[id].length) { |
okini3939 | 0:d18dff347122 | 93 | _state[id].mode = MODE_WEBSOCKET_ENTER; |
okini3939 | 0:d18dff347122 | 94 | } |
okini3939 | 0:d18dff347122 | 95 | break; |
okini3939 | 0:d18dff347122 | 96 | } |
okini3939 | 0:d18dff347122 | 97 | } |
okini3939 | 0:d18dff347122 | 98 | |
okini3939 | 0:d18dff347122 | 99 | int HTTPD::parseWebsocket (int id) { |
okini3939 | 0:d18dff347122 | 100 | int i; |
okini3939 | 0:d18dff347122 | 101 | |
okini3939 | 0:d18dff347122 | 102 | DBG("ws opcode %d\r\n", _state[id].websocket_opcode); |
okini3939 | 0:d18dff347122 | 103 | switch (_state[id].websocket_opcode) { |
okini3939 | 0:d18dff347122 | 104 | case 0x00: // continuation |
okini3939 | 0:d18dff347122 | 105 | break; |
okini3939 | 0:d18dff347122 | 106 | case 0x01: // text |
okini3939 | 0:d18dff347122 | 107 | case 0x02: // binary |
okini3939 | 0:d18dff347122 | 108 | i = getHandler(_state[id].uri); |
okini3939 | 0:d18dff347122 | 109 | if (i >= 0) { |
okini3939 | 0:d18dff347122 | 110 | if (_handler[i].funcCgi) { |
okini3939 | 0:d18dff347122 | 111 | // cgi |
okini3939 | 0:d18dff347122 | 112 | _handler[i].funcCgi(id); |
okini3939 | 0:d18dff347122 | 113 | } |
okini3939 | 0:d18dff347122 | 114 | } |
okini3939 | 0:d18dff347122 | 115 | break; |
okini3939 | 0:d18dff347122 | 116 | case 0x08: // close |
okini3939 | 0:d18dff347122 | 117 | _state[id].client->close(); |
okini3939 | 0:d18dff347122 | 118 | break; |
okini3939 | 0:d18dff347122 | 119 | case 0x09: // ping |
okini3939 | 0:d18dff347122 | 120 | { |
okini3939 | 0:d18dff347122 | 121 | char pong[_state[id].n + 2]; |
okini3939 | 0:d18dff347122 | 122 | pong[0] = 0x8a; |
okini3939 | 0:d18dff347122 | 123 | pong[1] = 0x04; |
okini3939 | 0:d18dff347122 | 124 | for (i = 0; i < _state[id].length; i ++) { |
okini3939 | 0:d18dff347122 | 125 | if (_state[id].buf->dequeue(&pong[i + 2]) == false) break; |
okini3939 | 0:d18dff347122 | 126 | } |
okini3939 | 0:d18dff347122 | 127 | _state[id].client->send_all(pong, _state[id].length + 2); |
okini3939 | 0:d18dff347122 | 128 | } |
okini3939 | 0:d18dff347122 | 129 | break; |
okini3939 | 0:d18dff347122 | 130 | case 0x0a: // pong |
okini3939 | 0:d18dff347122 | 131 | break; |
okini3939 | 0:d18dff347122 | 132 | default: |
okini3939 | 0:d18dff347122 | 133 | break; |
okini3939 | 0:d18dff347122 | 134 | } |
okini3939 | 0:d18dff347122 | 135 | _state[id].n = 0; |
okini3939 | 0:d18dff347122 | 136 | _state[id].length = 0; |
okini3939 | 0:d18dff347122 | 137 | return 0; |
okini3939 | 0:d18dff347122 | 138 | } |
okini3939 | 0:d18dff347122 | 139 | |
okini3939 | 0:d18dff347122 | 140 | int HTTPD::acceptWebsocket (int id) { |
okini3939 | 0:d18dff347122 | 141 | char buf[HTTPD_CMD_SIZE], buf2[HTTPD_CMD_SIZE]; |
okini3939 | 0:d18dff347122 | 142 | |
okini3939 | 0:d18dff347122 | 143 | DBG("websocket accept: %d\r\n", id); |
okini3939 | 0:d18dff347122 | 144 | |
okini3939 | 0:d18dff347122 | 145 | strcpy(buf, "HTTP/1.1 101 Switching Protocols\r\n"); |
okini3939 | 0:d18dff347122 | 146 | _state[id].client->send_all(buf, strlen(buf)); |
okini3939 | 0:d18dff347122 | 147 | strcpy(buf, "Upgrade: websocket\r\n"); |
okini3939 | 0:d18dff347122 | 148 | _state[id].client->send_all(buf, strlen(buf)); |
okini3939 | 0:d18dff347122 | 149 | strcpy(buf, "Connection: Upgrade\r\n"); |
okini3939 | 0:d18dff347122 | 150 | _state[id].client->send_all(buf, strlen(buf)); |
okini3939 | 0:d18dff347122 | 151 | |
okini3939 | 0:d18dff347122 | 152 | strcpy(buf, "Sec-WebSocket-Accept: "); |
okini3939 | 0:d18dff347122 | 153 | _state[id].client->send_all(buf, strlen(buf)); |
okini3939 | 0:d18dff347122 | 154 | strcpy(buf, _state[id].websocket_key); |
okini3939 | 0:d18dff347122 | 155 | strcat(buf, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); |
okini3939 | 0:d18dff347122 | 156 | sha1(buf, strlen(buf), buf2); |
okini3939 | 0:d18dff347122 | 157 | base64encode(buf2, 20, buf, sizeof(buf)); |
okini3939 | 0:d18dff347122 | 158 | _state[id].client->send_all(buf, strlen(buf)); |
okini3939 | 0:d18dff347122 | 159 | strcpy(buf, "\r\n\r\n"); |
okini3939 | 0:d18dff347122 | 160 | _state[id].client->send_all(buf, strlen(buf)); |
okini3939 | 0:d18dff347122 | 161 | _state[id].client->set_blocking(true, HTTPD_TIMEOUT * 100); |
okini3939 | 0:d18dff347122 | 162 | return 0; |
okini3939 | 0:d18dff347122 | 163 | } |
okini3939 | 0:d18dff347122 | 164 | |
okini3939 | 0:d18dff347122 | 165 | int HTTPD::sendWebsocket (int id, const char *buf, int len, const char *mask) { |
okini3939 | 0:d18dff347122 | 166 | HTTPD *httpd = HTTPD::getInstance(); |
okini3939 | 0:d18dff347122 | 167 | int i = 0, r; |
okini3939 | 0:d18dff347122 | 168 | char tmp[10]; |
okini3939 | 0:d18dff347122 | 169 | |
okini3939 | 0:d18dff347122 | 170 | tmp[i++] = 0x81; // single, text frame |
okini3939 | 0:d18dff347122 | 171 | if (len < 126) { |
okini3939 | 0:d18dff347122 | 172 | tmp[i++] = (mask == NULL ? 0 : 0x80) | len; |
okini3939 | 0:d18dff347122 | 173 | } else { |
okini3939 | 0:d18dff347122 | 174 | tmp[i++] = (mask == NULL ? 0 : 0x80) | 126; |
okini3939 | 0:d18dff347122 | 175 | tmp[i++] = (len >> 8) & 0xff; |
okini3939 | 0:d18dff347122 | 176 | tmp[i++] = len & 0xff; |
okini3939 | 0:d18dff347122 | 177 | } |
okini3939 | 0:d18dff347122 | 178 | if (mask) { |
okini3939 | 0:d18dff347122 | 179 | memcpy(&tmp[i], mask, 4); |
okini3939 | 0:d18dff347122 | 180 | i += 4; |
okini3939 | 0:d18dff347122 | 181 | } |
okini3939 | 0:d18dff347122 | 182 | r = httpd->_state[id].client->send_all(tmp, i); |
okini3939 | 0:d18dff347122 | 183 | |
okini3939 | 0:d18dff347122 | 184 | if (r >= 0) { |
okini3939 | 0:d18dff347122 | 185 | if (mask) { |
okini3939 | 0:d18dff347122 | 186 | char tmp2[len]; |
okini3939 | 0:d18dff347122 | 187 | for (i = 0; i < len; i ++) { |
okini3939 | 0:d18dff347122 | 188 | tmp2[i] = buf[i] ^ mask[i & 0x03]; |
okini3939 | 0:d18dff347122 | 189 | } |
okini3939 | 0:d18dff347122 | 190 | r = httpd->_state[id].client->send_all(tmp2, len); |
okini3939 | 0:d18dff347122 | 191 | } else { |
okini3939 | 0:d18dff347122 | 192 | r = httpd->_state[id].client->send_all((char*)buf, len); |
okini3939 | 0:d18dff347122 | 193 | } |
okini3939 | 0:d18dff347122 | 194 | } |
okini3939 | 0:d18dff347122 | 195 | return r; |
okini3939 | 0:d18dff347122 | 196 | } |
okini3939 | 0:d18dff347122 | 197 | |
okini3939 | 0:d18dff347122 | 198 |