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