Webserver+3d print
cyclone_tcp/web_socket/web_socket.c@0:8918a71cdbe9, 2017-02-04 (annotated)
- Committer:
- Sergunb
- Date:
- Sat Feb 04 18:15:49 2017 +0000
- Revision:
- 0:8918a71cdbe9
nothing else
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
Sergunb | 0:8918a71cdbe9 | 1 | /** |
Sergunb | 0:8918a71cdbe9 | 2 | * @file web_socket.c |
Sergunb | 0:8918a71cdbe9 | 3 | * @brief WebSocket API (client and server) |
Sergunb | 0:8918a71cdbe9 | 4 | * |
Sergunb | 0:8918a71cdbe9 | 5 | * @section License |
Sergunb | 0:8918a71cdbe9 | 6 | * |
Sergunb | 0:8918a71cdbe9 | 7 | * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved. |
Sergunb | 0:8918a71cdbe9 | 8 | * |
Sergunb | 0:8918a71cdbe9 | 9 | * This file is part of CycloneTCP Open. |
Sergunb | 0:8918a71cdbe9 | 10 | * |
Sergunb | 0:8918a71cdbe9 | 11 | * This program is free software; you can redistribute it and/or |
Sergunb | 0:8918a71cdbe9 | 12 | * modify it under the terms of the GNU General Public License |
Sergunb | 0:8918a71cdbe9 | 13 | * as published by the Free Software Foundation; either version 2 |
Sergunb | 0:8918a71cdbe9 | 14 | * of the License, or (at your option) any later version. |
Sergunb | 0:8918a71cdbe9 | 15 | * |
Sergunb | 0:8918a71cdbe9 | 16 | * This program is distributed in the hope that it will be useful, |
Sergunb | 0:8918a71cdbe9 | 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
Sergunb | 0:8918a71cdbe9 | 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
Sergunb | 0:8918a71cdbe9 | 19 | * GNU General Public License for more details. |
Sergunb | 0:8918a71cdbe9 | 20 | * |
Sergunb | 0:8918a71cdbe9 | 21 | * You should have received a copy of the GNU General Public License |
Sergunb | 0:8918a71cdbe9 | 22 | * along with this program; if not, write to the Free Software Foundation, |
Sergunb | 0:8918a71cdbe9 | 23 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
Sergunb | 0:8918a71cdbe9 | 24 | * |
Sergunb | 0:8918a71cdbe9 | 25 | * @author Oryx Embedded SARL (www.oryx-embedded.com) |
Sergunb | 0:8918a71cdbe9 | 26 | * @version 1.7.6 |
Sergunb | 0:8918a71cdbe9 | 27 | **/ |
Sergunb | 0:8918a71cdbe9 | 28 | |
Sergunb | 0:8918a71cdbe9 | 29 | //Switch to the appropriate trace level |
Sergunb | 0:8918a71cdbe9 | 30 | #define TRACE_LEVEL WEB_SOCKET_TRACE_LEVEL |
Sergunb | 0:8918a71cdbe9 | 31 | |
Sergunb | 0:8918a71cdbe9 | 32 | //Dependencies |
Sergunb | 0:8918a71cdbe9 | 33 | #include <stdlib.h> |
Sergunb | 0:8918a71cdbe9 | 34 | #include "core/net.h" |
Sergunb | 0:8918a71cdbe9 | 35 | #include "web_socket/web_socket.h" |
Sergunb | 0:8918a71cdbe9 | 36 | #include "web_socket/web_socket_auth.h" |
Sergunb | 0:8918a71cdbe9 | 37 | #include "web_socket/web_socket_frame.h" |
Sergunb | 0:8918a71cdbe9 | 38 | #include "web_socket/web_socket_transport.h" |
Sergunb | 0:8918a71cdbe9 | 39 | #include "web_socket/web_socket_misc.h" |
Sergunb | 0:8918a71cdbe9 | 40 | #include "str.h" |
Sergunb | 0:8918a71cdbe9 | 41 | #include "base64.h" |
Sergunb | 0:8918a71cdbe9 | 42 | #include "debug.h" |
Sergunb | 0:8918a71cdbe9 | 43 | |
Sergunb | 0:8918a71cdbe9 | 44 | //Check TCP/IP stack configuration |
Sergunb | 0:8918a71cdbe9 | 45 | #if (WEB_SOCKET_SUPPORT == ENABLED) |
Sergunb | 0:8918a71cdbe9 | 46 | |
Sergunb | 0:8918a71cdbe9 | 47 | //WebSocket table |
Sergunb | 0:8918a71cdbe9 | 48 | WebSocket webSocketTable[WEB_SOCKET_MAX_COUNT]; |
Sergunb | 0:8918a71cdbe9 | 49 | //Random data generation callback function |
Sergunb | 0:8918a71cdbe9 | 50 | WebSocketRandCallback webSockRandCallback; |
Sergunb | 0:8918a71cdbe9 | 51 | |
Sergunb | 0:8918a71cdbe9 | 52 | |
Sergunb | 0:8918a71cdbe9 | 53 | /** |
Sergunb | 0:8918a71cdbe9 | 54 | * @brief WebSocket related initialization |
Sergunb | 0:8918a71cdbe9 | 55 | * @return Error code |
Sergunb | 0:8918a71cdbe9 | 56 | **/ |
Sergunb | 0:8918a71cdbe9 | 57 | |
Sergunb | 0:8918a71cdbe9 | 58 | error_t webSocketInit(void) |
Sergunb | 0:8918a71cdbe9 | 59 | { |
Sergunb | 0:8918a71cdbe9 | 60 | //Initialize WebSockets |
Sergunb | 0:8918a71cdbe9 | 61 | memset(webSocketTable, 0, sizeof(webSocketTable)); |
Sergunb | 0:8918a71cdbe9 | 62 | |
Sergunb | 0:8918a71cdbe9 | 63 | //Successful initialization |
Sergunb | 0:8918a71cdbe9 | 64 | return NO_ERROR; |
Sergunb | 0:8918a71cdbe9 | 65 | } |
Sergunb | 0:8918a71cdbe9 | 66 | |
Sergunb | 0:8918a71cdbe9 | 67 | |
Sergunb | 0:8918a71cdbe9 | 68 | /** |
Sergunb | 0:8918a71cdbe9 | 69 | * @brief Register RNG callback function |
Sergunb | 0:8918a71cdbe9 | 70 | * @param[in] callback RNG callback function |
Sergunb | 0:8918a71cdbe9 | 71 | * @return Error code |
Sergunb | 0:8918a71cdbe9 | 72 | **/ |
Sergunb | 0:8918a71cdbe9 | 73 | |
Sergunb | 0:8918a71cdbe9 | 74 | error_t webSocketRegisterRandCallback(WebSocketRandCallback callback) |
Sergunb | 0:8918a71cdbe9 | 75 | { |
Sergunb | 0:8918a71cdbe9 | 76 | //Check parameter |
Sergunb | 0:8918a71cdbe9 | 77 | if(callback == NULL) |
Sergunb | 0:8918a71cdbe9 | 78 | return ERROR_INVALID_PARAMETER; |
Sergunb | 0:8918a71cdbe9 | 79 | |
Sergunb | 0:8918a71cdbe9 | 80 | //Save callback function |
Sergunb | 0:8918a71cdbe9 | 81 | webSockRandCallback = callback; |
Sergunb | 0:8918a71cdbe9 | 82 | |
Sergunb | 0:8918a71cdbe9 | 83 | //Successful processing |
Sergunb | 0:8918a71cdbe9 | 84 | return NO_ERROR; |
Sergunb | 0:8918a71cdbe9 | 85 | } |
Sergunb | 0:8918a71cdbe9 | 86 | |
Sergunb | 0:8918a71cdbe9 | 87 | |
Sergunb | 0:8918a71cdbe9 | 88 | /** |
Sergunb | 0:8918a71cdbe9 | 89 | * @brief Create a WebSocket |
Sergunb | 0:8918a71cdbe9 | 90 | * @return Handle referencing the new WebSocket |
Sergunb | 0:8918a71cdbe9 | 91 | **/ |
Sergunb | 0:8918a71cdbe9 | 92 | |
Sergunb | 0:8918a71cdbe9 | 93 | WebSocket *webSocketOpen(void) |
Sergunb | 0:8918a71cdbe9 | 94 | { |
Sergunb | 0:8918a71cdbe9 | 95 | uint_t i; |
Sergunb | 0:8918a71cdbe9 | 96 | WebSocket *webSocket; |
Sergunb | 0:8918a71cdbe9 | 97 | |
Sergunb | 0:8918a71cdbe9 | 98 | //Initialize WebSocket handle |
Sergunb | 0:8918a71cdbe9 | 99 | webSocket = NULL; |
Sergunb | 0:8918a71cdbe9 | 100 | |
Sergunb | 0:8918a71cdbe9 | 101 | //Get exclusive access |
Sergunb | 0:8918a71cdbe9 | 102 | osAcquireMutex(&netMutex); |
Sergunb | 0:8918a71cdbe9 | 103 | |
Sergunb | 0:8918a71cdbe9 | 104 | //Loop through WebSocket descriptors |
Sergunb | 0:8918a71cdbe9 | 105 | for(i = 0; i < WEB_SOCKET_MAX_COUNT; i++) |
Sergunb | 0:8918a71cdbe9 | 106 | { |
Sergunb | 0:8918a71cdbe9 | 107 | //Unused WebSocket found? |
Sergunb | 0:8918a71cdbe9 | 108 | if(webSocketTable[i].state == WS_STATE_UNUSED) |
Sergunb | 0:8918a71cdbe9 | 109 | { |
Sergunb | 0:8918a71cdbe9 | 110 | //Save socket handle |
Sergunb | 0:8918a71cdbe9 | 111 | webSocket = &webSocketTable[i]; |
Sergunb | 0:8918a71cdbe9 | 112 | |
Sergunb | 0:8918a71cdbe9 | 113 | //Clear associated structure |
Sergunb | 0:8918a71cdbe9 | 114 | memset(webSocket, 0, sizeof(WebSocket)); |
Sergunb | 0:8918a71cdbe9 | 115 | //Set the default timeout to be used |
Sergunb | 0:8918a71cdbe9 | 116 | webSocket->timeout = INFINITE_DELAY; |
Sergunb | 0:8918a71cdbe9 | 117 | //Enter the CLOSED state |
Sergunb | 0:8918a71cdbe9 | 118 | webSocket->state = WS_STATE_CLOSED; |
Sergunb | 0:8918a71cdbe9 | 119 | |
Sergunb | 0:8918a71cdbe9 | 120 | //We are done |
Sergunb | 0:8918a71cdbe9 | 121 | break; |
Sergunb | 0:8918a71cdbe9 | 122 | } |
Sergunb | 0:8918a71cdbe9 | 123 | } |
Sergunb | 0:8918a71cdbe9 | 124 | |
Sergunb | 0:8918a71cdbe9 | 125 | //Release exclusive access |
Sergunb | 0:8918a71cdbe9 | 126 | osReleaseMutex(&netMutex); |
Sergunb | 0:8918a71cdbe9 | 127 | |
Sergunb | 0:8918a71cdbe9 | 128 | //Return a handle to the freshly created WebSocket |
Sergunb | 0:8918a71cdbe9 | 129 | return webSocket; |
Sergunb | 0:8918a71cdbe9 | 130 | } |
Sergunb | 0:8918a71cdbe9 | 131 | |
Sergunb | 0:8918a71cdbe9 | 132 | |
Sergunb | 0:8918a71cdbe9 | 133 | /** |
Sergunb | 0:8918a71cdbe9 | 134 | * @brief Upgrade a socket to a WebSocket |
Sergunb | 0:8918a71cdbe9 | 135 | * @param[in] socket Handle referencing the socket |
Sergunb | 0:8918a71cdbe9 | 136 | * @return Handle referencing the new WebSocket |
Sergunb | 0:8918a71cdbe9 | 137 | **/ |
Sergunb | 0:8918a71cdbe9 | 138 | |
Sergunb | 0:8918a71cdbe9 | 139 | WebSocket *webSocketUpgradeSocket(Socket *socket) |
Sergunb | 0:8918a71cdbe9 | 140 | { |
Sergunb | 0:8918a71cdbe9 | 141 | WebSocket *webSocket; |
Sergunb | 0:8918a71cdbe9 | 142 | |
Sergunb | 0:8918a71cdbe9 | 143 | //Valid socket handle? |
Sergunb | 0:8918a71cdbe9 | 144 | if(socket != NULL) |
Sergunb | 0:8918a71cdbe9 | 145 | { |
Sergunb | 0:8918a71cdbe9 | 146 | //Create a new WebSocket |
Sergunb | 0:8918a71cdbe9 | 147 | webSocket = webSocketOpen(); |
Sergunb | 0:8918a71cdbe9 | 148 | |
Sergunb | 0:8918a71cdbe9 | 149 | //WebSocket successfully created? |
Sergunb | 0:8918a71cdbe9 | 150 | if(webSocket != NULL) |
Sergunb | 0:8918a71cdbe9 | 151 | { |
Sergunb | 0:8918a71cdbe9 | 152 | //Attach the socket handle |
Sergunb | 0:8918a71cdbe9 | 153 | webSocket->socket = socket; |
Sergunb | 0:8918a71cdbe9 | 154 | //Initialize state |
Sergunb | 0:8918a71cdbe9 | 155 | webSocket->state = WS_STATE_INIT; |
Sergunb | 0:8918a71cdbe9 | 156 | } |
Sergunb | 0:8918a71cdbe9 | 157 | } |
Sergunb | 0:8918a71cdbe9 | 158 | else |
Sergunb | 0:8918a71cdbe9 | 159 | { |
Sergunb | 0:8918a71cdbe9 | 160 | //The specified socket is not valid... |
Sergunb | 0:8918a71cdbe9 | 161 | webSocket = NULL; |
Sergunb | 0:8918a71cdbe9 | 162 | } |
Sergunb | 0:8918a71cdbe9 | 163 | |
Sergunb | 0:8918a71cdbe9 | 164 | //Return a handle to the freshly created WebSocket |
Sergunb | 0:8918a71cdbe9 | 165 | return webSocket; |
Sergunb | 0:8918a71cdbe9 | 166 | } |
Sergunb | 0:8918a71cdbe9 | 167 | |
Sergunb | 0:8918a71cdbe9 | 168 | |
Sergunb | 0:8918a71cdbe9 | 169 | #if (WEB_SOCKET_TLS_SUPPORT == ENABLED) |
Sergunb | 0:8918a71cdbe9 | 170 | |
Sergunb | 0:8918a71cdbe9 | 171 | /** |
Sergunb | 0:8918a71cdbe9 | 172 | * @brief Upgrade a secure socket to a secure WebSocket |
Sergunb | 0:8918a71cdbe9 | 173 | * @param[in] socket Handle referencing the socket |
Sergunb | 0:8918a71cdbe9 | 174 | * @param[in] tlsContext Pointer to the SSL/TLS context |
Sergunb | 0:8918a71cdbe9 | 175 | * @return Handle referencing the new WebSocket |
Sergunb | 0:8918a71cdbe9 | 176 | **/ |
Sergunb | 0:8918a71cdbe9 | 177 | |
Sergunb | 0:8918a71cdbe9 | 178 | WebSocket *webSocketUpgradeSecureSocket(Socket *socket, TlsContext *tlsContext) |
Sergunb | 0:8918a71cdbe9 | 179 | { |
Sergunb | 0:8918a71cdbe9 | 180 | WebSocket *webSocket; |
Sergunb | 0:8918a71cdbe9 | 181 | |
Sergunb | 0:8918a71cdbe9 | 182 | //Valid SSL/TLS context? |
Sergunb | 0:8918a71cdbe9 | 183 | if(tlsContext != NULL) |
Sergunb | 0:8918a71cdbe9 | 184 | { |
Sergunb | 0:8918a71cdbe9 | 185 | //Create a new WebSocket |
Sergunb | 0:8918a71cdbe9 | 186 | webSocket = webSocketOpen(); |
Sergunb | 0:8918a71cdbe9 | 187 | |
Sergunb | 0:8918a71cdbe9 | 188 | //WebSocket successfully created? |
Sergunb | 0:8918a71cdbe9 | 189 | if(webSocket != NULL) |
Sergunb | 0:8918a71cdbe9 | 190 | { |
Sergunb | 0:8918a71cdbe9 | 191 | //Attach the socket handle |
Sergunb | 0:8918a71cdbe9 | 192 | webSocket->socket = socket; |
Sergunb | 0:8918a71cdbe9 | 193 | //Attach the SSL/TLS context |
Sergunb | 0:8918a71cdbe9 | 194 | webSocket->tlsContext = tlsContext; |
Sergunb | 0:8918a71cdbe9 | 195 | //Initialize state |
Sergunb | 0:8918a71cdbe9 | 196 | webSocket->state = WS_STATE_INIT; |
Sergunb | 0:8918a71cdbe9 | 197 | } |
Sergunb | 0:8918a71cdbe9 | 198 | } |
Sergunb | 0:8918a71cdbe9 | 199 | else |
Sergunb | 0:8918a71cdbe9 | 200 | { |
Sergunb | 0:8918a71cdbe9 | 201 | //The specified socket is not valid... |
Sergunb | 0:8918a71cdbe9 | 202 | webSocket = NULL; |
Sergunb | 0:8918a71cdbe9 | 203 | } |
Sergunb | 0:8918a71cdbe9 | 204 | |
Sergunb | 0:8918a71cdbe9 | 205 | //Return a handle to the freshly created WebSocket |
Sergunb | 0:8918a71cdbe9 | 206 | return webSocket; |
Sergunb | 0:8918a71cdbe9 | 207 | } |
Sergunb | 0:8918a71cdbe9 | 208 | |
Sergunb | 0:8918a71cdbe9 | 209 | |
Sergunb | 0:8918a71cdbe9 | 210 | /** |
Sergunb | 0:8918a71cdbe9 | 211 | * @brief Register TLS initialization callback function |
Sergunb | 0:8918a71cdbe9 | 212 | * @param[in] webSocket Handle that identifies a WebSocket |
Sergunb | 0:8918a71cdbe9 | 213 | * @param[in] callback TLS initialization callback function |
Sergunb | 0:8918a71cdbe9 | 214 | * @return Error code |
Sergunb | 0:8918a71cdbe9 | 215 | **/ |
Sergunb | 0:8918a71cdbe9 | 216 | |
Sergunb | 0:8918a71cdbe9 | 217 | error_t webSocketRegisterTlsInitCallback(WebSocket *webSocket, |
Sergunb | 0:8918a71cdbe9 | 218 | WebSocketTlsInitCallback callback) |
Sergunb | 0:8918a71cdbe9 | 219 | { |
Sergunb | 0:8918a71cdbe9 | 220 | //Check parameters |
Sergunb | 0:8918a71cdbe9 | 221 | if(webSocket == NULL || callback == NULL) |
Sergunb | 0:8918a71cdbe9 | 222 | return ERROR_INVALID_PARAMETER; |
Sergunb | 0:8918a71cdbe9 | 223 | |
Sergunb | 0:8918a71cdbe9 | 224 | //Save callback function |
Sergunb | 0:8918a71cdbe9 | 225 | webSocket->tlsInitCallback = callback; |
Sergunb | 0:8918a71cdbe9 | 226 | |
Sergunb | 0:8918a71cdbe9 | 227 | //Successful processing |
Sergunb | 0:8918a71cdbe9 | 228 | return NO_ERROR; |
Sergunb | 0:8918a71cdbe9 | 229 | } |
Sergunb | 0:8918a71cdbe9 | 230 | |
Sergunb | 0:8918a71cdbe9 | 231 | #endif |
Sergunb | 0:8918a71cdbe9 | 232 | |
Sergunb | 0:8918a71cdbe9 | 233 | |
Sergunb | 0:8918a71cdbe9 | 234 | /** |
Sergunb | 0:8918a71cdbe9 | 235 | * @brief Set timeout value for blocking operations |
Sergunb | 0:8918a71cdbe9 | 236 | * @param[in] webSocket Handle to a WebSocket |
Sergunb | 0:8918a71cdbe9 | 237 | * @param[in] timeout Maximum time to wait |
Sergunb | 0:8918a71cdbe9 | 238 | * @return Error code |
Sergunb | 0:8918a71cdbe9 | 239 | **/ |
Sergunb | 0:8918a71cdbe9 | 240 | |
Sergunb | 0:8918a71cdbe9 | 241 | error_t webSocketSetTimeout(WebSocket *webSocket, systime_t timeout) |
Sergunb | 0:8918a71cdbe9 | 242 | { |
Sergunb | 0:8918a71cdbe9 | 243 | //Make sure the WebSocket handle is valid |
Sergunb | 0:8918a71cdbe9 | 244 | if(webSocket == NULL) |
Sergunb | 0:8918a71cdbe9 | 245 | return ERROR_INVALID_PARAMETER; |
Sergunb | 0:8918a71cdbe9 | 246 | |
Sergunb | 0:8918a71cdbe9 | 247 | //Save timeout value |
Sergunb | 0:8918a71cdbe9 | 248 | webSocket->timeout = timeout; |
Sergunb | 0:8918a71cdbe9 | 249 | |
Sergunb | 0:8918a71cdbe9 | 250 | //Valid socket? |
Sergunb | 0:8918a71cdbe9 | 251 | if(webSocket->socket != NULL) |
Sergunb | 0:8918a71cdbe9 | 252 | socketSetTimeout(webSocket->socket, timeout); |
Sergunb | 0:8918a71cdbe9 | 253 | |
Sergunb | 0:8918a71cdbe9 | 254 | //Successful processing |
Sergunb | 0:8918a71cdbe9 | 255 | return NO_ERROR; |
Sergunb | 0:8918a71cdbe9 | 256 | } |
Sergunb | 0:8918a71cdbe9 | 257 | |
Sergunb | 0:8918a71cdbe9 | 258 | |
Sergunb | 0:8918a71cdbe9 | 259 | /** |
Sergunb | 0:8918a71cdbe9 | 260 | * @brief Set the hostname of the resource being requested |
Sergunb | 0:8918a71cdbe9 | 261 | * @param[in] webSocket Handle to a WebSocket |
Sergunb | 0:8918a71cdbe9 | 262 | * @param[in] host NULL-terminated string containing the hostname |
Sergunb | 0:8918a71cdbe9 | 263 | * @return Error code |
Sergunb | 0:8918a71cdbe9 | 264 | **/ |
Sergunb | 0:8918a71cdbe9 | 265 | |
Sergunb | 0:8918a71cdbe9 | 266 | error_t webSocketSetHost(WebSocket *webSocket, const char_t *host) |
Sergunb | 0:8918a71cdbe9 | 267 | { |
Sergunb | 0:8918a71cdbe9 | 268 | //Check parameters |
Sergunb | 0:8918a71cdbe9 | 269 | if(webSocket == NULL || host == NULL) |
Sergunb | 0:8918a71cdbe9 | 270 | return ERROR_INVALID_PARAMETER; |
Sergunb | 0:8918a71cdbe9 | 271 | |
Sergunb | 0:8918a71cdbe9 | 272 | //Save the hostname |
Sergunb | 0:8918a71cdbe9 | 273 | strSafeCopy(webSocket->host, host, WEB_SOCKET_HOST_MAX_LEN); |
Sergunb | 0:8918a71cdbe9 | 274 | |
Sergunb | 0:8918a71cdbe9 | 275 | //Successful processing |
Sergunb | 0:8918a71cdbe9 | 276 | return NO_ERROR; |
Sergunb | 0:8918a71cdbe9 | 277 | } |
Sergunb | 0:8918a71cdbe9 | 278 | |
Sergunb | 0:8918a71cdbe9 | 279 | |
Sergunb | 0:8918a71cdbe9 | 280 | /** |
Sergunb | 0:8918a71cdbe9 | 281 | * @brief Set the origin header field |
Sergunb | 0:8918a71cdbe9 | 282 | * @param[in] webSocket Handle to a WebSocket |
Sergunb | 0:8918a71cdbe9 | 283 | * @param[in] origin NULL-terminated string containing the origin |
Sergunb | 0:8918a71cdbe9 | 284 | * @return Error code |
Sergunb | 0:8918a71cdbe9 | 285 | **/ |
Sergunb | 0:8918a71cdbe9 | 286 | |
Sergunb | 0:8918a71cdbe9 | 287 | error_t webSocketSetOrigin(WebSocket *webSocket, const char_t *origin) |
Sergunb | 0:8918a71cdbe9 | 288 | { |
Sergunb | 0:8918a71cdbe9 | 289 | //Check parameters |
Sergunb | 0:8918a71cdbe9 | 290 | if(webSocket == NULL || origin == NULL) |
Sergunb | 0:8918a71cdbe9 | 291 | return ERROR_INVALID_PARAMETER; |
Sergunb | 0:8918a71cdbe9 | 292 | |
Sergunb | 0:8918a71cdbe9 | 293 | //Save origin |
Sergunb | 0:8918a71cdbe9 | 294 | strSafeCopy(webSocket->origin, origin, WEB_SOCKET_ORIGIN_MAX_LEN); |
Sergunb | 0:8918a71cdbe9 | 295 | |
Sergunb | 0:8918a71cdbe9 | 296 | //Successful processing |
Sergunb | 0:8918a71cdbe9 | 297 | return NO_ERROR; |
Sergunb | 0:8918a71cdbe9 | 298 | } |
Sergunb | 0:8918a71cdbe9 | 299 | |
Sergunb | 0:8918a71cdbe9 | 300 | |
Sergunb | 0:8918a71cdbe9 | 301 | /** |
Sergunb | 0:8918a71cdbe9 | 302 | * @brief Set the sub-protocol header field |
Sergunb | 0:8918a71cdbe9 | 303 | * @param[in] webSocket Handle to a WebSocket |
Sergunb | 0:8918a71cdbe9 | 304 | * @param[in] subProtocol NULL-terminated string containing the sub-protocol |
Sergunb | 0:8918a71cdbe9 | 305 | * @return Error code |
Sergunb | 0:8918a71cdbe9 | 306 | **/ |
Sergunb | 0:8918a71cdbe9 | 307 | |
Sergunb | 0:8918a71cdbe9 | 308 | error_t webSocketSetSubProtocol(WebSocket *webSocket, const char_t *subProtocol) |
Sergunb | 0:8918a71cdbe9 | 309 | { |
Sergunb | 0:8918a71cdbe9 | 310 | //Check parameters |
Sergunb | 0:8918a71cdbe9 | 311 | if(webSocket == NULL || subProtocol == NULL) |
Sergunb | 0:8918a71cdbe9 | 312 | return ERROR_INVALID_PARAMETER; |
Sergunb | 0:8918a71cdbe9 | 313 | |
Sergunb | 0:8918a71cdbe9 | 314 | //Save sub-protocol |
Sergunb | 0:8918a71cdbe9 | 315 | strSafeCopy(webSocket->subProtocol, subProtocol, WEB_SOCKET_SUB_PROTOCOL_MAX_LEN); |
Sergunb | 0:8918a71cdbe9 | 316 | |
Sergunb | 0:8918a71cdbe9 | 317 | //Successful processing |
Sergunb | 0:8918a71cdbe9 | 318 | return NO_ERROR; |
Sergunb | 0:8918a71cdbe9 | 319 | } |
Sergunb | 0:8918a71cdbe9 | 320 | |
Sergunb | 0:8918a71cdbe9 | 321 | |
Sergunb | 0:8918a71cdbe9 | 322 | /** |
Sergunb | 0:8918a71cdbe9 | 323 | * @brief Set authentication information |
Sergunb | 0:8918a71cdbe9 | 324 | * @param[in] webSocket Handle to a WebSocket |
Sergunb | 0:8918a71cdbe9 | 325 | * @param[in] username NULL-terminated string containing the user name to be used |
Sergunb | 0:8918a71cdbe9 | 326 | * @param[in] password NULL-terminated string containing the password to be used |
Sergunb | 0:8918a71cdbe9 | 327 | * @param[in] allowedAuthModes Logic OR of allowed HTTP authentication modes |
Sergunb | 0:8918a71cdbe9 | 328 | * @return Error code |
Sergunb | 0:8918a71cdbe9 | 329 | **/ |
Sergunb | 0:8918a71cdbe9 | 330 | |
Sergunb | 0:8918a71cdbe9 | 331 | error_t webSocketSetAuthInfo(WebSocket *webSocket, const char_t *username, |
Sergunb | 0:8918a71cdbe9 | 332 | const char_t *password, uint_t allowedAuthModes) |
Sergunb | 0:8918a71cdbe9 | 333 | { |
Sergunb | 0:8918a71cdbe9 | 334 | #if (WEB_SOCKET_BASIC_AUTH_SUPPORT == ENABLED || WEB_SOCKET_DIGEST_AUTH_SUPPORT == ENABLED) |
Sergunb | 0:8918a71cdbe9 | 335 | WebSocketAuthContext *authContext; |
Sergunb | 0:8918a71cdbe9 | 336 | |
Sergunb | 0:8918a71cdbe9 | 337 | //Check parameters |
Sergunb | 0:8918a71cdbe9 | 338 | if(webSocket == NULL || username == NULL || password == NULL) |
Sergunb | 0:8918a71cdbe9 | 339 | return ERROR_INVALID_PARAMETER; |
Sergunb | 0:8918a71cdbe9 | 340 | |
Sergunb | 0:8918a71cdbe9 | 341 | //Point to the authentication context |
Sergunb | 0:8918a71cdbe9 | 342 | authContext = &webSocket->authContext; |
Sergunb | 0:8918a71cdbe9 | 343 | |
Sergunb | 0:8918a71cdbe9 | 344 | //Save user name |
Sergunb | 0:8918a71cdbe9 | 345 | strSafeCopy(authContext->username, username, WEB_SOCKET_USERNAME_MAX_LEN); |
Sergunb | 0:8918a71cdbe9 | 346 | //Save password |
Sergunb | 0:8918a71cdbe9 | 347 | strSafeCopy(authContext->password, password, WEB_SOCKET_PASSWORD_MAX_LEN); |
Sergunb | 0:8918a71cdbe9 | 348 | //Save the list of allowed HTTP authentication modes |
Sergunb | 0:8918a71cdbe9 | 349 | authContext->allowedAuthModes = allowedAuthModes; |
Sergunb | 0:8918a71cdbe9 | 350 | #endif |
Sergunb | 0:8918a71cdbe9 | 351 | |
Sergunb | 0:8918a71cdbe9 | 352 | //Successful processing |
Sergunb | 0:8918a71cdbe9 | 353 | return NO_ERROR; |
Sergunb | 0:8918a71cdbe9 | 354 | } |
Sergunb | 0:8918a71cdbe9 | 355 | |
Sergunb | 0:8918a71cdbe9 | 356 | |
Sergunb | 0:8918a71cdbe9 | 357 | /** |
Sergunb | 0:8918a71cdbe9 | 358 | * @brief Bind the WebSocket to a particular network interface |
Sergunb | 0:8918a71cdbe9 | 359 | * @param[in] webSocket Handle to a WebSocket |
Sergunb | 0:8918a71cdbe9 | 360 | * @param[in] interface Network interface to be used |
Sergunb | 0:8918a71cdbe9 | 361 | * @return Error code |
Sergunb | 0:8918a71cdbe9 | 362 | **/ |
Sergunb | 0:8918a71cdbe9 | 363 | |
Sergunb | 0:8918a71cdbe9 | 364 | error_t webSocketBindToInterface(WebSocket *webSocket, NetInterface *interface) |
Sergunb | 0:8918a71cdbe9 | 365 | { |
Sergunb | 0:8918a71cdbe9 | 366 | //Make sure the WebSocket handle is valid |
Sergunb | 0:8918a71cdbe9 | 367 | if(webSocket == NULL) |
Sergunb | 0:8918a71cdbe9 | 368 | return ERROR_INVALID_PARAMETER; |
Sergunb | 0:8918a71cdbe9 | 369 | |
Sergunb | 0:8918a71cdbe9 | 370 | //Explicitly associate the WebSocket with the specified interface |
Sergunb | 0:8918a71cdbe9 | 371 | webSocket->interface = interface; |
Sergunb | 0:8918a71cdbe9 | 372 | |
Sergunb | 0:8918a71cdbe9 | 373 | //Successful processing |
Sergunb | 0:8918a71cdbe9 | 374 | return NO_ERROR; |
Sergunb | 0:8918a71cdbe9 | 375 | } |
Sergunb | 0:8918a71cdbe9 | 376 | |
Sergunb | 0:8918a71cdbe9 | 377 | |
Sergunb | 0:8918a71cdbe9 | 378 | /** |
Sergunb | 0:8918a71cdbe9 | 379 | * @brief Establish a WebSocket connection |
Sergunb | 0:8918a71cdbe9 | 380 | * @param[in] webSocket Handle to a WebSocket |
Sergunb | 0:8918a71cdbe9 | 381 | * @param[in] serverIpAddr IP address of the WebSocket server to connect to |
Sergunb | 0:8918a71cdbe9 | 382 | * @param[in] serverPort TCP port number that will be used to establish the |
Sergunb | 0:8918a71cdbe9 | 383 | * connection |
Sergunb | 0:8918a71cdbe9 | 384 | * @param[in] uri NULL-terminated string that contains the resource name |
Sergunb | 0:8918a71cdbe9 | 385 | * @return Error code |
Sergunb | 0:8918a71cdbe9 | 386 | **/ |
Sergunb | 0:8918a71cdbe9 | 387 | |
Sergunb | 0:8918a71cdbe9 | 388 | error_t webSocketConnect(WebSocket *webSocket, const IpAddr *serverIpAddr, |
Sergunb | 0:8918a71cdbe9 | 389 | uint16_t serverPort, const char_t *uri) |
Sergunb | 0:8918a71cdbe9 | 390 | { |
Sergunb | 0:8918a71cdbe9 | 391 | error_t error; |
Sergunb | 0:8918a71cdbe9 | 392 | size_t n; |
Sergunb | 0:8918a71cdbe9 | 393 | WebSocketFrameContext *txContext; |
Sergunb | 0:8918a71cdbe9 | 394 | |
Sergunb | 0:8918a71cdbe9 | 395 | //Make sure the WebSocket handle is valid |
Sergunb | 0:8918a71cdbe9 | 396 | if(webSocket == NULL) |
Sergunb | 0:8918a71cdbe9 | 397 | return ERROR_INVALID_PARAMETER; |
Sergunb | 0:8918a71cdbe9 | 398 | |
Sergunb | 0:8918a71cdbe9 | 399 | //Point to the TX context |
Sergunb | 0:8918a71cdbe9 | 400 | txContext = &webSocket->txContext; |
Sergunb | 0:8918a71cdbe9 | 401 | |
Sergunb | 0:8918a71cdbe9 | 402 | //Initialize status code |
Sergunb | 0:8918a71cdbe9 | 403 | error = NO_ERROR; |
Sergunb | 0:8918a71cdbe9 | 404 | |
Sergunb | 0:8918a71cdbe9 | 405 | //Establish connection |
Sergunb | 0:8918a71cdbe9 | 406 | while(webSocket->state != WS_STATE_OPEN) |
Sergunb | 0:8918a71cdbe9 | 407 | { |
Sergunb | 0:8918a71cdbe9 | 408 | //Check current state |
Sergunb | 0:8918a71cdbe9 | 409 | if(webSocket->state == WS_STATE_CLOSED) |
Sergunb | 0:8918a71cdbe9 | 410 | { |
Sergunb | 0:8918a71cdbe9 | 411 | //Check parameters |
Sergunb | 0:8918a71cdbe9 | 412 | if(serverIpAddr == NULL || uri == NULL) |
Sergunb | 0:8918a71cdbe9 | 413 | { |
Sergunb | 0:8918a71cdbe9 | 414 | //Report an error |
Sergunb | 0:8918a71cdbe9 | 415 | error = ERROR_INVALID_PARAMETER; |
Sergunb | 0:8918a71cdbe9 | 416 | } |
Sergunb | 0:8918a71cdbe9 | 417 | else |
Sergunb | 0:8918a71cdbe9 | 418 | { |
Sergunb | 0:8918a71cdbe9 | 419 | //A WebSocket client is a WebSocket endpoint that initiates a |
Sergunb | 0:8918a71cdbe9 | 420 | //connection to a peer |
Sergunb | 0:8918a71cdbe9 | 421 | webSocket->endpoint = WS_ENDPOINT_CLIENT; |
Sergunb | 0:8918a71cdbe9 | 422 | |
Sergunb | 0:8918a71cdbe9 | 423 | //Save the URI |
Sergunb | 0:8918a71cdbe9 | 424 | strSafeCopy(webSocket->uri, uri, WEB_SOCKET_URI_MAX_LEN); |
Sergunb | 0:8918a71cdbe9 | 425 | //Reset retry counter |
Sergunb | 0:8918a71cdbe9 | 426 | webSocket->retryCount = 0; |
Sergunb | 0:8918a71cdbe9 | 427 | |
Sergunb | 0:8918a71cdbe9 | 428 | #if (WEB_SOCKET_BASIC_AUTH_SUPPORT == ENABLED || WEB_SOCKET_DIGEST_AUTH_SUPPORT == ENABLED) |
Sergunb | 0:8918a71cdbe9 | 429 | //HTTP authentication is not used for the first connection attempt |
Sergunb | 0:8918a71cdbe9 | 430 | webSocket->authContext.requiredAuthMode = WS_AUTH_MODE_NONE; |
Sergunb | 0:8918a71cdbe9 | 431 | webSocket->authContext.selectedAuthMode = WS_AUTH_MODE_NONE; |
Sergunb | 0:8918a71cdbe9 | 432 | #endif |
Sergunb | 0:8918a71cdbe9 | 433 | //Initialize the WebSocket connection |
Sergunb | 0:8918a71cdbe9 | 434 | webSocketChangeState(webSocket, WS_STATE_INIT); |
Sergunb | 0:8918a71cdbe9 | 435 | } |
Sergunb | 0:8918a71cdbe9 | 436 | } |
Sergunb | 0:8918a71cdbe9 | 437 | else if(webSocket->state == WS_STATE_INIT) |
Sergunb | 0:8918a71cdbe9 | 438 | { |
Sergunb | 0:8918a71cdbe9 | 439 | //Increment retry counter |
Sergunb | 0:8918a71cdbe9 | 440 | webSocket->retryCount++; |
Sergunb | 0:8918a71cdbe9 | 441 | |
Sergunb | 0:8918a71cdbe9 | 442 | //Limit the number of connection attempts |
Sergunb | 0:8918a71cdbe9 | 443 | if(webSocket->retryCount > WEB_SOCKET_MAX_CONN_RETRIES) |
Sergunb | 0:8918a71cdbe9 | 444 | { |
Sergunb | 0:8918a71cdbe9 | 445 | //Report an error |
Sergunb | 0:8918a71cdbe9 | 446 | error = ERROR_OPEN_FAILED; |
Sergunb | 0:8918a71cdbe9 | 447 | } |
Sergunb | 0:8918a71cdbe9 | 448 | else |
Sergunb | 0:8918a71cdbe9 | 449 | { |
Sergunb | 0:8918a71cdbe9 | 450 | //Open network connection |
Sergunb | 0:8918a71cdbe9 | 451 | error = webSocketOpenConnection(webSocket); |
Sergunb | 0:8918a71cdbe9 | 452 | } |
Sergunb | 0:8918a71cdbe9 | 453 | |
Sergunb | 0:8918a71cdbe9 | 454 | //Check status code |
Sergunb | 0:8918a71cdbe9 | 455 | if(!error) |
Sergunb | 0:8918a71cdbe9 | 456 | { |
Sergunb | 0:8918a71cdbe9 | 457 | //Establish connection |
Sergunb | 0:8918a71cdbe9 | 458 | webSocketChangeState(webSocket, WS_STATE_CONNECTING); |
Sergunb | 0:8918a71cdbe9 | 459 | } |
Sergunb | 0:8918a71cdbe9 | 460 | } |
Sergunb | 0:8918a71cdbe9 | 461 | else if(webSocket->state == WS_STATE_CONNECTING) |
Sergunb | 0:8918a71cdbe9 | 462 | { |
Sergunb | 0:8918a71cdbe9 | 463 | //Establish connection |
Sergunb | 0:8918a71cdbe9 | 464 | error = webSocketEstablishConnection(webSocket, |
Sergunb | 0:8918a71cdbe9 | 465 | serverIpAddr, serverPort); |
Sergunb | 0:8918a71cdbe9 | 466 | |
Sergunb | 0:8918a71cdbe9 | 467 | //Check status code |
Sergunb | 0:8918a71cdbe9 | 468 | if(!error) |
Sergunb | 0:8918a71cdbe9 | 469 | { |
Sergunb | 0:8918a71cdbe9 | 470 | //Generate client's key |
Sergunb | 0:8918a71cdbe9 | 471 | error = webSocketGenerateClientKey(webSocket); |
Sergunb | 0:8918a71cdbe9 | 472 | } |
Sergunb | 0:8918a71cdbe9 | 473 | |
Sergunb | 0:8918a71cdbe9 | 474 | //Check status code |
Sergunb | 0:8918a71cdbe9 | 475 | if(!error) |
Sergunb | 0:8918a71cdbe9 | 476 | { |
Sergunb | 0:8918a71cdbe9 | 477 | //Format client handshake |
Sergunb | 0:8918a71cdbe9 | 478 | error = webSocketFormatClientHandshake(webSocket, serverPort); |
Sergunb | 0:8918a71cdbe9 | 479 | } |
Sergunb | 0:8918a71cdbe9 | 480 | |
Sergunb | 0:8918a71cdbe9 | 481 | //Check status code |
Sergunb | 0:8918a71cdbe9 | 482 | if(!error) |
Sergunb | 0:8918a71cdbe9 | 483 | { |
Sergunb | 0:8918a71cdbe9 | 484 | //Send client handshake |
Sergunb | 0:8918a71cdbe9 | 485 | webSocketChangeState(webSocket, WS_STATE_CLIENT_HANDSHAKE); |
Sergunb | 0:8918a71cdbe9 | 486 | } |
Sergunb | 0:8918a71cdbe9 | 487 | } |
Sergunb | 0:8918a71cdbe9 | 488 | else if(webSocket->state == WS_STATE_CLIENT_HANDSHAKE) |
Sergunb | 0:8918a71cdbe9 | 489 | { |
Sergunb | 0:8918a71cdbe9 | 490 | //Any remaining data to be sent? |
Sergunb | 0:8918a71cdbe9 | 491 | if(txContext->bufferPos < txContext->bufferLen) |
Sergunb | 0:8918a71cdbe9 | 492 | { |
Sergunb | 0:8918a71cdbe9 | 493 | //Send more data |
Sergunb | 0:8918a71cdbe9 | 494 | error = webSocketSendData(webSocket, |
Sergunb | 0:8918a71cdbe9 | 495 | txContext->buffer + txContext->bufferPos, |
Sergunb | 0:8918a71cdbe9 | 496 | txContext->bufferLen - txContext->bufferPos, &n, 0); |
Sergunb | 0:8918a71cdbe9 | 497 | |
Sergunb | 0:8918a71cdbe9 | 498 | //Advance data pointer |
Sergunb | 0:8918a71cdbe9 | 499 | txContext->bufferPos += n; |
Sergunb | 0:8918a71cdbe9 | 500 | } |
Sergunb | 0:8918a71cdbe9 | 501 | else |
Sergunb | 0:8918a71cdbe9 | 502 | { |
Sergunb | 0:8918a71cdbe9 | 503 | //Wait for server handshake |
Sergunb | 0:8918a71cdbe9 | 504 | webSocketChangeState(webSocket, WS_STATE_SERVER_HANDSHAKE); |
Sergunb | 0:8918a71cdbe9 | 505 | } |
Sergunb | 0:8918a71cdbe9 | 506 | } |
Sergunb | 0:8918a71cdbe9 | 507 | else if(webSocket->state == WS_STATE_SERVER_HANDSHAKE) |
Sergunb | 0:8918a71cdbe9 | 508 | { |
Sergunb | 0:8918a71cdbe9 | 509 | //Parse the server handshake |
Sergunb | 0:8918a71cdbe9 | 510 | error = webSocketParseHandshake(webSocket); |
Sergunb | 0:8918a71cdbe9 | 511 | } |
Sergunb | 0:8918a71cdbe9 | 512 | else if(webSocket->state == WS_STATE_SERVER_RESP_BODY) |
Sergunb | 0:8918a71cdbe9 | 513 | { |
Sergunb | 0:8918a71cdbe9 | 514 | //Check Connection header field |
Sergunb | 0:8918a71cdbe9 | 515 | if(webSocket->handshakeContext.connectionClose) |
Sergunb | 0:8918a71cdbe9 | 516 | { |
Sergunb | 0:8918a71cdbe9 | 517 | //Close connection |
Sergunb | 0:8918a71cdbe9 | 518 | webSocketCloseConnection(webSocket); |
Sergunb | 0:8918a71cdbe9 | 519 | //Try to connect again |
Sergunb | 0:8918a71cdbe9 | 520 | webSocketChangeState(webSocket, WS_STATE_INIT); |
Sergunb | 0:8918a71cdbe9 | 521 | } |
Sergunb | 0:8918a71cdbe9 | 522 | else |
Sergunb | 0:8918a71cdbe9 | 523 | { |
Sergunb | 0:8918a71cdbe9 | 524 | //Any remaining data to read in the response body? |
Sergunb | 0:8918a71cdbe9 | 525 | if(webSocket->handshakeContext.contentLength > 0) |
Sergunb | 0:8918a71cdbe9 | 526 | { |
Sergunb | 0:8918a71cdbe9 | 527 | //Limit the number of bytes to read at a time |
Sergunb | 0:8918a71cdbe9 | 528 | n = MIN(webSocket->handshakeContext.contentLength, WEB_SOCKET_BUFFER_SIZE); |
Sergunb | 0:8918a71cdbe9 | 529 | //Discard any received data |
Sergunb | 0:8918a71cdbe9 | 530 | error = webSocketReceiveData(webSocket, txContext->buffer, n, &n, 0); |
Sergunb | 0:8918a71cdbe9 | 531 | //Decrement byte counter |
Sergunb | 0:8918a71cdbe9 | 532 | webSocket->handshakeContext.contentLength -= n; |
Sergunb | 0:8918a71cdbe9 | 533 | } |
Sergunb | 0:8918a71cdbe9 | 534 | else |
Sergunb | 0:8918a71cdbe9 | 535 | { |
Sergunb | 0:8918a71cdbe9 | 536 | //Format client handshake |
Sergunb | 0:8918a71cdbe9 | 537 | error = webSocketFormatClientHandshake(webSocket, serverPort); |
Sergunb | 0:8918a71cdbe9 | 538 | //Try to authenticate again |
Sergunb | 0:8918a71cdbe9 | 539 | webSocketChangeState(webSocket, WS_STATE_CLIENT_HANDSHAKE); |
Sergunb | 0:8918a71cdbe9 | 540 | } |
Sergunb | 0:8918a71cdbe9 | 541 | } |
Sergunb | 0:8918a71cdbe9 | 542 | } |
Sergunb | 0:8918a71cdbe9 | 543 | else |
Sergunb | 0:8918a71cdbe9 | 544 | { |
Sergunb | 0:8918a71cdbe9 | 545 | //Invalid state |
Sergunb | 0:8918a71cdbe9 | 546 | error = ERROR_WRONG_STATE; |
Sergunb | 0:8918a71cdbe9 | 547 | } |
Sergunb | 0:8918a71cdbe9 | 548 | |
Sergunb | 0:8918a71cdbe9 | 549 | //Check whether authentication is required |
Sergunb | 0:8918a71cdbe9 | 550 | if(error == ERROR_AUTH_REQUIRED) |
Sergunb | 0:8918a71cdbe9 | 551 | { |
Sergunb | 0:8918a71cdbe9 | 552 | #if (WEB_SOCKET_BASIC_AUTH_SUPPORT == ENABLED) |
Sergunb | 0:8918a71cdbe9 | 553 | //Basic authentication? |
Sergunb | 0:8918a71cdbe9 | 554 | if(webSocket->authContext.requiredAuthMode == WS_AUTH_MODE_BASIC) |
Sergunb | 0:8918a71cdbe9 | 555 | { |
Sergunb | 0:8918a71cdbe9 | 556 | //Check whether the basic authentication scheme is allowed |
Sergunb | 0:8918a71cdbe9 | 557 | if(webSocket->authContext.allowedAuthModes & WS_AUTH_MODE_BASIC) |
Sergunb | 0:8918a71cdbe9 | 558 | { |
Sergunb | 0:8918a71cdbe9 | 559 | //Do not try to connect again if the credentials are not valid... |
Sergunb | 0:8918a71cdbe9 | 560 | if(webSocket->authContext.selectedAuthMode == WS_AUTH_MODE_NONE) |
Sergunb | 0:8918a71cdbe9 | 561 | { |
Sergunb | 0:8918a71cdbe9 | 562 | //Catch exception |
Sergunb | 0:8918a71cdbe9 | 563 | error = NO_ERROR; |
Sergunb | 0:8918a71cdbe9 | 564 | //Force the WebSocket client to use basic authentication |
Sergunb | 0:8918a71cdbe9 | 565 | webSocket->authContext.selectedAuthMode = WS_AUTH_MODE_BASIC; |
Sergunb | 0:8918a71cdbe9 | 566 | //Read response body, if any |
Sergunb | 0:8918a71cdbe9 | 567 | webSocketChangeState(webSocket, WS_STATE_SERVER_RESP_BODY); |
Sergunb | 0:8918a71cdbe9 | 568 | } |
Sergunb | 0:8918a71cdbe9 | 569 | } |
Sergunb | 0:8918a71cdbe9 | 570 | } |
Sergunb | 0:8918a71cdbe9 | 571 | #endif |
Sergunb | 0:8918a71cdbe9 | 572 | #if (WEB_SOCKET_DIGEST_AUTH_SUPPORT == ENABLED) |
Sergunb | 0:8918a71cdbe9 | 573 | //Digest authentication? |
Sergunb | 0:8918a71cdbe9 | 574 | if(webSocket->authContext.requiredAuthMode == WS_AUTH_MODE_DIGEST) |
Sergunb | 0:8918a71cdbe9 | 575 | { |
Sergunb | 0:8918a71cdbe9 | 576 | //Check whether the digest authentication scheme is allowed |
Sergunb | 0:8918a71cdbe9 | 577 | if(webSocket->authContext.allowedAuthModes & WS_AUTH_MODE_DIGEST) |
Sergunb | 0:8918a71cdbe9 | 578 | { |
Sergunb | 0:8918a71cdbe9 | 579 | //Do not try to connect again if the credentials are not valid... |
Sergunb | 0:8918a71cdbe9 | 580 | if(webSocket->authContext.selectedAuthMode == WS_AUTH_MODE_NONE) |
Sergunb | 0:8918a71cdbe9 | 581 | { |
Sergunb | 0:8918a71cdbe9 | 582 | //Force the WebSocket client to use digest authentication |
Sergunb | 0:8918a71cdbe9 | 583 | webSocket->authContext.selectedAuthMode = WS_AUTH_MODE_DIGEST; |
Sergunb | 0:8918a71cdbe9 | 584 | |
Sergunb | 0:8918a71cdbe9 | 585 | //Make sure that the RNG callback function has been registered |
Sergunb | 0:8918a71cdbe9 | 586 | if(webSockRandCallback != NULL) |
Sergunb | 0:8918a71cdbe9 | 587 | { |
Sergunb | 0:8918a71cdbe9 | 588 | //Generate a random cnonce |
Sergunb | 0:8918a71cdbe9 | 589 | error = webSockRandCallback(txContext->buffer, |
Sergunb | 0:8918a71cdbe9 | 590 | WEB_SOCKET_CNONCE_SIZE); |
Sergunb | 0:8918a71cdbe9 | 591 | } |
Sergunb | 0:8918a71cdbe9 | 592 | else |
Sergunb | 0:8918a71cdbe9 | 593 | { |
Sergunb | 0:8918a71cdbe9 | 594 | //A cryptographically strong random number generator |
Sergunb | 0:8918a71cdbe9 | 595 | //must be used to generate the cnonce |
Sergunb | 0:8918a71cdbe9 | 596 | error = ERROR_PRNG_NOT_READY; |
Sergunb | 0:8918a71cdbe9 | 597 | } |
Sergunb | 0:8918a71cdbe9 | 598 | |
Sergunb | 0:8918a71cdbe9 | 599 | //Convert the byte array to hex string |
Sergunb | 0:8918a71cdbe9 | 600 | webSocketConvertArrayToHexString(txContext->buffer, |
Sergunb | 0:8918a71cdbe9 | 601 | WEB_SOCKET_CNONCE_SIZE, webSocket->authContext.cnonce); |
Sergunb | 0:8918a71cdbe9 | 602 | |
Sergunb | 0:8918a71cdbe9 | 603 | //Read response body, if any |
Sergunb | 0:8918a71cdbe9 | 604 | webSocketChangeState(webSocket, WS_STATE_SERVER_RESP_BODY); |
Sergunb | 0:8918a71cdbe9 | 605 | } |
Sergunb | 0:8918a71cdbe9 | 606 | } |
Sergunb | 0:8918a71cdbe9 | 607 | } |
Sergunb | 0:8918a71cdbe9 | 608 | #endif |
Sergunb | 0:8918a71cdbe9 | 609 | } |
Sergunb | 0:8918a71cdbe9 | 610 | |
Sergunb | 0:8918a71cdbe9 | 611 | //If an error occurred, then the client must fail the WebSocket |
Sergunb | 0:8918a71cdbe9 | 612 | //connection |
Sergunb | 0:8918a71cdbe9 | 613 | if(error) |
Sergunb | 0:8918a71cdbe9 | 614 | { |
Sergunb | 0:8918a71cdbe9 | 615 | #if (NET_RTOS_SUPPORT == DISABLED) |
Sergunb | 0:8918a71cdbe9 | 616 | //Timeout error? |
Sergunb | 0:8918a71cdbe9 | 617 | if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT) |
Sergunb | 0:8918a71cdbe9 | 618 | break; |
Sergunb | 0:8918a71cdbe9 | 619 | #endif |
Sergunb | 0:8918a71cdbe9 | 620 | //Close connection |
Sergunb | 0:8918a71cdbe9 | 621 | webSocketCloseConnection(webSocket); |
Sergunb | 0:8918a71cdbe9 | 622 | |
Sergunb | 0:8918a71cdbe9 | 623 | //Switch to the CLOSED state |
Sergunb | 0:8918a71cdbe9 | 624 | webSocketChangeState(webSocket, WS_STATE_CLOSED); |
Sergunb | 0:8918a71cdbe9 | 625 | //Exit immediately |
Sergunb | 0:8918a71cdbe9 | 626 | break; |
Sergunb | 0:8918a71cdbe9 | 627 | } |
Sergunb | 0:8918a71cdbe9 | 628 | } |
Sergunb | 0:8918a71cdbe9 | 629 | |
Sergunb | 0:8918a71cdbe9 | 630 | //Return status code |
Sergunb | 0:8918a71cdbe9 | 631 | return error; |
Sergunb | 0:8918a71cdbe9 | 632 | } |
Sergunb | 0:8918a71cdbe9 | 633 | |
Sergunb | 0:8918a71cdbe9 | 634 | |
Sergunb | 0:8918a71cdbe9 | 635 | /** |
Sergunb | 0:8918a71cdbe9 | 636 | * @brief Set client's key |
Sergunb | 0:8918a71cdbe9 | 637 | * @param[in] webSocket Handle to a WebSocket |
Sergunb | 0:8918a71cdbe9 | 638 | * @param[in] clientKey NULL-terminated string that holds the the client's key |
Sergunb | 0:8918a71cdbe9 | 639 | * @return Error code |
Sergunb | 0:8918a71cdbe9 | 640 | **/ |
Sergunb | 0:8918a71cdbe9 | 641 | |
Sergunb | 0:8918a71cdbe9 | 642 | error_t webSocketSetClientKey(WebSocket *webSocket, const char_t *clientKey) |
Sergunb | 0:8918a71cdbe9 | 643 | { |
Sergunb | 0:8918a71cdbe9 | 644 | error_t error; |
Sergunb | 0:8918a71cdbe9 | 645 | size_t n; |
Sergunb | 0:8918a71cdbe9 | 646 | |
Sergunb | 0:8918a71cdbe9 | 647 | //Check parameters |
Sergunb | 0:8918a71cdbe9 | 648 | if(webSocket == NULL || clientKey == NULL) |
Sergunb | 0:8918a71cdbe9 | 649 | return ERROR_INVALID_PARAMETER; |
Sergunb | 0:8918a71cdbe9 | 650 | |
Sergunb | 0:8918a71cdbe9 | 651 | //Get the length of the client's key |
Sergunb | 0:8918a71cdbe9 | 652 | n = strlen(clientKey); |
Sergunb | 0:8918a71cdbe9 | 653 | |
Sergunb | 0:8918a71cdbe9 | 654 | //Check the length of the key |
Sergunb | 0:8918a71cdbe9 | 655 | if(n > WEB_SOCKET_CLIENT_KEY_SIZE) |
Sergunb | 0:8918a71cdbe9 | 656 | return ERROR_INVALID_LENGTH; |
Sergunb | 0:8918a71cdbe9 | 657 | |
Sergunb | 0:8918a71cdbe9 | 658 | //Copy client's key |
Sergunb | 0:8918a71cdbe9 | 659 | strcpy(webSocket->handshakeContext.clientKey, clientKey); |
Sergunb | 0:8918a71cdbe9 | 660 | |
Sergunb | 0:8918a71cdbe9 | 661 | //a WebSocket server is a WebSocket endpoint that awaits |
Sergunb | 0:8918a71cdbe9 | 662 | //connections from peers |
Sergunb | 0:8918a71cdbe9 | 663 | webSocket->endpoint = WS_ENDPOINT_SERVER; |
Sergunb | 0:8918a71cdbe9 | 664 | |
Sergunb | 0:8918a71cdbe9 | 665 | //Initialize status code |
Sergunb | 0:8918a71cdbe9 | 666 | webSocket->statusCode = WS_STATUS_CODE_NO_STATUS_RCVD; |
Sergunb | 0:8918a71cdbe9 | 667 | |
Sergunb | 0:8918a71cdbe9 | 668 | //Initialize handshake parameters |
Sergunb | 0:8918a71cdbe9 | 669 | webSocket->handshakeContext.version = WS_HTTP_VERSION_1_1; |
Sergunb | 0:8918a71cdbe9 | 670 | webSocket->handshakeContext.connectionUpgrade = TRUE; |
Sergunb | 0:8918a71cdbe9 | 671 | webSocket->handshakeContext.upgradeWebSocket = TRUE; |
Sergunb | 0:8918a71cdbe9 | 672 | |
Sergunb | 0:8918a71cdbe9 | 673 | //Initialize FIN flag |
Sergunb | 0:8918a71cdbe9 | 674 | webSocket->rxContext.fin = TRUE; |
Sergunb | 0:8918a71cdbe9 | 675 | |
Sergunb | 0:8918a71cdbe9 | 676 | //Verify client's key |
Sergunb | 0:8918a71cdbe9 | 677 | error = webSocketVerifyClientKey(webSocket); |
Sergunb | 0:8918a71cdbe9 | 678 | //Any error to report? |
Sergunb | 0:8918a71cdbe9 | 679 | if(error) |
Sergunb | 0:8918a71cdbe9 | 680 | return error; |
Sergunb | 0:8918a71cdbe9 | 681 | |
Sergunb | 0:8918a71cdbe9 | 682 | //Generate server's key |
Sergunb | 0:8918a71cdbe9 | 683 | error = webSocketGenerateServerKey(webSocket); |
Sergunb | 0:8918a71cdbe9 | 684 | //Any error to report? |
Sergunb | 0:8918a71cdbe9 | 685 | if(error) |
Sergunb | 0:8918a71cdbe9 | 686 | return error; |
Sergunb | 0:8918a71cdbe9 | 687 | |
Sergunb | 0:8918a71cdbe9 | 688 | //Format server handshake |
Sergunb | 0:8918a71cdbe9 | 689 | error = webSocketFormatServerHandshake(webSocket); |
Sergunb | 0:8918a71cdbe9 | 690 | //Any error to report? |
Sergunb | 0:8918a71cdbe9 | 691 | if(error) |
Sergunb | 0:8918a71cdbe9 | 692 | return error; |
Sergunb | 0:8918a71cdbe9 | 693 | |
Sergunb | 0:8918a71cdbe9 | 694 | //Update FSM state |
Sergunb | 0:8918a71cdbe9 | 695 | webSocket->state = WS_STATE_SERVER_HANDSHAKE; |
Sergunb | 0:8918a71cdbe9 | 696 | |
Sergunb | 0:8918a71cdbe9 | 697 | //Successful processing |
Sergunb | 0:8918a71cdbe9 | 698 | return NO_ERROR; |
Sergunb | 0:8918a71cdbe9 | 699 | } |
Sergunb | 0:8918a71cdbe9 | 700 | |
Sergunb | 0:8918a71cdbe9 | 701 | |
Sergunb | 0:8918a71cdbe9 | 702 | /** |
Sergunb | 0:8918a71cdbe9 | 703 | * @brief Parse client's handshake |
Sergunb | 0:8918a71cdbe9 | 704 | * @param[in] webSocket Handle that identifies a WebSocket |
Sergunb | 0:8918a71cdbe9 | 705 | * @return Error code |
Sergunb | 0:8918a71cdbe9 | 706 | **/ |
Sergunb | 0:8918a71cdbe9 | 707 | |
Sergunb | 0:8918a71cdbe9 | 708 | error_t webSocketParseClientHandshake(WebSocket *webSocket) |
Sergunb | 0:8918a71cdbe9 | 709 | { |
Sergunb | 0:8918a71cdbe9 | 710 | error_t error; |
Sergunb | 0:8918a71cdbe9 | 711 | |
Sergunb | 0:8918a71cdbe9 | 712 | //Make sure the WebSocket handle is valid |
Sergunb | 0:8918a71cdbe9 | 713 | if(webSocket == NULL) |
Sergunb | 0:8918a71cdbe9 | 714 | return ERROR_INVALID_PARAMETER; |
Sergunb | 0:8918a71cdbe9 | 715 | |
Sergunb | 0:8918a71cdbe9 | 716 | //a WebSocket server is a WebSocket endpoint that awaits |
Sergunb | 0:8918a71cdbe9 | 717 | //connections from peers |
Sergunb | 0:8918a71cdbe9 | 718 | webSocket->endpoint = WS_ENDPOINT_SERVER; |
Sergunb | 0:8918a71cdbe9 | 719 | |
Sergunb | 0:8918a71cdbe9 | 720 | //Initialize status code |
Sergunb | 0:8918a71cdbe9 | 721 | error = NO_ERROR; |
Sergunb | 0:8918a71cdbe9 | 722 | |
Sergunb | 0:8918a71cdbe9 | 723 | //Establish connection |
Sergunb | 0:8918a71cdbe9 | 724 | while(webSocket->state != WS_STATE_SERVER_HANDSHAKE) |
Sergunb | 0:8918a71cdbe9 | 725 | { |
Sergunb | 0:8918a71cdbe9 | 726 | //Check current state |
Sergunb | 0:8918a71cdbe9 | 727 | if(webSocket->state == WS_STATE_INIT) |
Sergunb | 0:8918a71cdbe9 | 728 | { |
Sergunb | 0:8918a71cdbe9 | 729 | //Open network connection |
Sergunb | 0:8918a71cdbe9 | 730 | error = webSocketOpenConnection(webSocket); |
Sergunb | 0:8918a71cdbe9 | 731 | |
Sergunb | 0:8918a71cdbe9 | 732 | //Check status code |
Sergunb | 0:8918a71cdbe9 | 733 | if(!error) |
Sergunb | 0:8918a71cdbe9 | 734 | { |
Sergunb | 0:8918a71cdbe9 | 735 | //Establish connection |
Sergunb | 0:8918a71cdbe9 | 736 | webSocketChangeState(webSocket, WS_STATE_CONNECTING); |
Sergunb | 0:8918a71cdbe9 | 737 | } |
Sergunb | 0:8918a71cdbe9 | 738 | } |
Sergunb | 0:8918a71cdbe9 | 739 | else if(webSocket->state == WS_STATE_CONNECTING) |
Sergunb | 0:8918a71cdbe9 | 740 | { |
Sergunb | 0:8918a71cdbe9 | 741 | #if (WEB_SOCKET_TLS_SUPPORT == ENABLED) |
Sergunb | 0:8918a71cdbe9 | 742 | //Use SSL/TLS to secure the connection? |
Sergunb | 0:8918a71cdbe9 | 743 | if(webSocket->tlsInitCallback != NULL) |
Sergunb | 0:8918a71cdbe9 | 744 | { |
Sergunb | 0:8918a71cdbe9 | 745 | //Establish a SSL/TLS connection |
Sergunb | 0:8918a71cdbe9 | 746 | error = tlsConnect(webSocket->tlsContext); |
Sergunb | 0:8918a71cdbe9 | 747 | } |
Sergunb | 0:8918a71cdbe9 | 748 | #endif |
Sergunb | 0:8918a71cdbe9 | 749 | //Check status code |
Sergunb | 0:8918a71cdbe9 | 750 | if(!error) |
Sergunb | 0:8918a71cdbe9 | 751 | { |
Sergunb | 0:8918a71cdbe9 | 752 | //Parse client handshake |
Sergunb | 0:8918a71cdbe9 | 753 | webSocketChangeState(webSocket, WS_STATE_CLIENT_HANDSHAKE); |
Sergunb | 0:8918a71cdbe9 | 754 | } |
Sergunb | 0:8918a71cdbe9 | 755 | } |
Sergunb | 0:8918a71cdbe9 | 756 | else if(webSocket->state == WS_STATE_CLIENT_HANDSHAKE) |
Sergunb | 0:8918a71cdbe9 | 757 | { |
Sergunb | 0:8918a71cdbe9 | 758 | //Parse the client handshake |
Sergunb | 0:8918a71cdbe9 | 759 | error = webSocketParseHandshake(webSocket); |
Sergunb | 0:8918a71cdbe9 | 760 | |
Sergunb | 0:8918a71cdbe9 | 761 | //Check status code |
Sergunb | 0:8918a71cdbe9 | 762 | if(!error) |
Sergunb | 0:8918a71cdbe9 | 763 | { |
Sergunb | 0:8918a71cdbe9 | 764 | //Generate server's key |
Sergunb | 0:8918a71cdbe9 | 765 | error = webSocketGenerateServerKey(webSocket); |
Sergunb | 0:8918a71cdbe9 | 766 | } |
Sergunb | 0:8918a71cdbe9 | 767 | |
Sergunb | 0:8918a71cdbe9 | 768 | //Check status code |
Sergunb | 0:8918a71cdbe9 | 769 | if(!error) |
Sergunb | 0:8918a71cdbe9 | 770 | { |
Sergunb | 0:8918a71cdbe9 | 771 | //Format server handshake |
Sergunb | 0:8918a71cdbe9 | 772 | error = webSocketFormatServerHandshake(webSocket); |
Sergunb | 0:8918a71cdbe9 | 773 | } |
Sergunb | 0:8918a71cdbe9 | 774 | } |
Sergunb | 0:8918a71cdbe9 | 775 | else |
Sergunb | 0:8918a71cdbe9 | 776 | { |
Sergunb | 0:8918a71cdbe9 | 777 | //Invalid state |
Sergunb | 0:8918a71cdbe9 | 778 | error = ERROR_WRONG_STATE; |
Sergunb | 0:8918a71cdbe9 | 779 | } |
Sergunb | 0:8918a71cdbe9 | 780 | |
Sergunb | 0:8918a71cdbe9 | 781 | //Any error to report? |
Sergunb | 0:8918a71cdbe9 | 782 | if(error) |
Sergunb | 0:8918a71cdbe9 | 783 | break; |
Sergunb | 0:8918a71cdbe9 | 784 | } |
Sergunb | 0:8918a71cdbe9 | 785 | |
Sergunb | 0:8918a71cdbe9 | 786 | //Return status code |
Sergunb | 0:8918a71cdbe9 | 787 | return error; |
Sergunb | 0:8918a71cdbe9 | 788 | } |
Sergunb | 0:8918a71cdbe9 | 789 | |
Sergunb | 0:8918a71cdbe9 | 790 | |
Sergunb | 0:8918a71cdbe9 | 791 | /** |
Sergunb | 0:8918a71cdbe9 | 792 | * @brief Send server's handshake |
Sergunb | 0:8918a71cdbe9 | 793 | * @param[in] webSocket Handle that identifies a WebSocket |
Sergunb | 0:8918a71cdbe9 | 794 | * @return Error code |
Sergunb | 0:8918a71cdbe9 | 795 | **/ |
Sergunb | 0:8918a71cdbe9 | 796 | |
Sergunb | 0:8918a71cdbe9 | 797 | error_t webSocketSendServerHandshake(WebSocket *webSocket) |
Sergunb | 0:8918a71cdbe9 | 798 | { |
Sergunb | 0:8918a71cdbe9 | 799 | error_t error; |
Sergunb | 0:8918a71cdbe9 | 800 | size_t n; |
Sergunb | 0:8918a71cdbe9 | 801 | WebSocketFrameContext *txContext; |
Sergunb | 0:8918a71cdbe9 | 802 | |
Sergunb | 0:8918a71cdbe9 | 803 | //Make sure the WebSocket handle is valid |
Sergunb | 0:8918a71cdbe9 | 804 | if(webSocket == NULL) |
Sergunb | 0:8918a71cdbe9 | 805 | return ERROR_INVALID_PARAMETER; |
Sergunb | 0:8918a71cdbe9 | 806 | |
Sergunb | 0:8918a71cdbe9 | 807 | //Point to the TX context |
Sergunb | 0:8918a71cdbe9 | 808 | txContext = &webSocket->txContext; |
Sergunb | 0:8918a71cdbe9 | 809 | |
Sergunb | 0:8918a71cdbe9 | 810 | //Initialize status code |
Sergunb | 0:8918a71cdbe9 | 811 | error = NO_ERROR; |
Sergunb | 0:8918a71cdbe9 | 812 | |
Sergunb | 0:8918a71cdbe9 | 813 | //Establish connection |
Sergunb | 0:8918a71cdbe9 | 814 | while(webSocket->state != WS_STATE_OPEN) |
Sergunb | 0:8918a71cdbe9 | 815 | { |
Sergunb | 0:8918a71cdbe9 | 816 | //Check current state |
Sergunb | 0:8918a71cdbe9 | 817 | if(webSocket->state == WS_STATE_SERVER_HANDSHAKE) |
Sergunb | 0:8918a71cdbe9 | 818 | { |
Sergunb | 0:8918a71cdbe9 | 819 | //Any remaining data to be sent? |
Sergunb | 0:8918a71cdbe9 | 820 | if(txContext->bufferPos < txContext->bufferLen) |
Sergunb | 0:8918a71cdbe9 | 821 | { |
Sergunb | 0:8918a71cdbe9 | 822 | //Send more data |
Sergunb | 0:8918a71cdbe9 | 823 | error = webSocketSendData(webSocket, |
Sergunb | 0:8918a71cdbe9 | 824 | txContext->buffer + txContext->bufferPos, |
Sergunb | 0:8918a71cdbe9 | 825 | txContext->bufferLen - txContext->bufferPos, &n, 0); |
Sergunb | 0:8918a71cdbe9 | 826 | |
Sergunb | 0:8918a71cdbe9 | 827 | //Advance data pointer |
Sergunb | 0:8918a71cdbe9 | 828 | txContext->bufferPos += n; |
Sergunb | 0:8918a71cdbe9 | 829 | } |
Sergunb | 0:8918a71cdbe9 | 830 | else |
Sergunb | 0:8918a71cdbe9 | 831 | { |
Sergunb | 0:8918a71cdbe9 | 832 | //The WebSocket connection is established |
Sergunb | 0:8918a71cdbe9 | 833 | webSocketChangeState(webSocket, WS_STATE_OPEN); |
Sergunb | 0:8918a71cdbe9 | 834 | } |
Sergunb | 0:8918a71cdbe9 | 835 | } |
Sergunb | 0:8918a71cdbe9 | 836 | else |
Sergunb | 0:8918a71cdbe9 | 837 | { |
Sergunb | 0:8918a71cdbe9 | 838 | //Invalid state |
Sergunb | 0:8918a71cdbe9 | 839 | error = ERROR_WRONG_STATE; |
Sergunb | 0:8918a71cdbe9 | 840 | } |
Sergunb | 0:8918a71cdbe9 | 841 | |
Sergunb | 0:8918a71cdbe9 | 842 | //Any error to report? |
Sergunb | 0:8918a71cdbe9 | 843 | if(error) |
Sergunb | 0:8918a71cdbe9 | 844 | break; |
Sergunb | 0:8918a71cdbe9 | 845 | } |
Sergunb | 0:8918a71cdbe9 | 846 | |
Sergunb | 0:8918a71cdbe9 | 847 | //Return status code |
Sergunb | 0:8918a71cdbe9 | 848 | return error; |
Sergunb | 0:8918a71cdbe9 | 849 | } |
Sergunb | 0:8918a71cdbe9 | 850 | |
Sergunb | 0:8918a71cdbe9 | 851 | |
Sergunb | 0:8918a71cdbe9 | 852 | /** |
Sergunb | 0:8918a71cdbe9 | 853 | * @brief Send HTTP error response to the client |
Sergunb | 0:8918a71cdbe9 | 854 | * @param[in] webSocket Handle that identifies a WebSocket |
Sergunb | 0:8918a71cdbe9 | 855 | * @param[in] statusCode HTTP status code |
Sergunb | 0:8918a71cdbe9 | 856 | * @param[in] message Text message |
Sergunb | 0:8918a71cdbe9 | 857 | * @return Error code |
Sergunb | 0:8918a71cdbe9 | 858 | **/ |
Sergunb | 0:8918a71cdbe9 | 859 | |
Sergunb | 0:8918a71cdbe9 | 860 | error_t webSocketSendErrorResponse(WebSocket *webSocket, |
Sergunb | 0:8918a71cdbe9 | 861 | uint_t statusCode, const char_t *message) |
Sergunb | 0:8918a71cdbe9 | 862 | { |
Sergunb | 0:8918a71cdbe9 | 863 | error_t error; |
Sergunb | 0:8918a71cdbe9 | 864 | size_t n; |
Sergunb | 0:8918a71cdbe9 | 865 | WebSocketFrameContext *txContext; |
Sergunb | 0:8918a71cdbe9 | 866 | |
Sergunb | 0:8918a71cdbe9 | 867 | //Make sure the WebSocket handle is valid |
Sergunb | 0:8918a71cdbe9 | 868 | if(webSocket == NULL) |
Sergunb | 0:8918a71cdbe9 | 869 | return ERROR_INVALID_PARAMETER; |
Sergunb | 0:8918a71cdbe9 | 870 | |
Sergunb | 0:8918a71cdbe9 | 871 | //Point to the TX context |
Sergunb | 0:8918a71cdbe9 | 872 | txContext = &webSocket->txContext; |
Sergunb | 0:8918a71cdbe9 | 873 | |
Sergunb | 0:8918a71cdbe9 | 874 | //Initialize status code |
Sergunb | 0:8918a71cdbe9 | 875 | error = NO_ERROR; |
Sergunb | 0:8918a71cdbe9 | 876 | |
Sergunb | 0:8918a71cdbe9 | 877 | //Send HTTP error message |
Sergunb | 0:8918a71cdbe9 | 878 | while(1) |
Sergunb | 0:8918a71cdbe9 | 879 | { |
Sergunb | 0:8918a71cdbe9 | 880 | //Check current state |
Sergunb | 0:8918a71cdbe9 | 881 | if(txContext->state == WS_SUB_STATE_INIT) |
Sergunb | 0:8918a71cdbe9 | 882 | { |
Sergunb | 0:8918a71cdbe9 | 883 | //Format HTTP error response |
Sergunb | 0:8918a71cdbe9 | 884 | error = webSocketFormatErrorResponse(webSocket, statusCode, message); |
Sergunb | 0:8918a71cdbe9 | 885 | |
Sergunb | 0:8918a71cdbe9 | 886 | //Send the response |
Sergunb | 0:8918a71cdbe9 | 887 | txContext->state = WS_SUB_STATE_FRAME_PAYLOAD; |
Sergunb | 0:8918a71cdbe9 | 888 | } |
Sergunb | 0:8918a71cdbe9 | 889 | else if(txContext->state == WS_SUB_STATE_FRAME_PAYLOAD) |
Sergunb | 0:8918a71cdbe9 | 890 | { |
Sergunb | 0:8918a71cdbe9 | 891 | //Any remaining data to be sent? |
Sergunb | 0:8918a71cdbe9 | 892 | if(txContext->bufferPos < txContext->bufferLen) |
Sergunb | 0:8918a71cdbe9 | 893 | { |
Sergunb | 0:8918a71cdbe9 | 894 | //Send more data |
Sergunb | 0:8918a71cdbe9 | 895 | error = webSocketSendData(webSocket, |
Sergunb | 0:8918a71cdbe9 | 896 | txContext->buffer + txContext->bufferPos, |
Sergunb | 0:8918a71cdbe9 | 897 | txContext->bufferLen - txContext->bufferPos, &n, 0); |
Sergunb | 0:8918a71cdbe9 | 898 | |
Sergunb | 0:8918a71cdbe9 | 899 | //Advance data pointer |
Sergunb | 0:8918a71cdbe9 | 900 | txContext->bufferPos += n; |
Sergunb | 0:8918a71cdbe9 | 901 | } |
Sergunb | 0:8918a71cdbe9 | 902 | else |
Sergunb | 0:8918a71cdbe9 | 903 | { |
Sergunb | 0:8918a71cdbe9 | 904 | //We are done |
Sergunb | 0:8918a71cdbe9 | 905 | webSocketChangeState(webSocket, WS_STATE_SHUTDOWN); |
Sergunb | 0:8918a71cdbe9 | 906 | break; |
Sergunb | 0:8918a71cdbe9 | 907 | } |
Sergunb | 0:8918a71cdbe9 | 908 | } |
Sergunb | 0:8918a71cdbe9 | 909 | else |
Sergunb | 0:8918a71cdbe9 | 910 | { |
Sergunb | 0:8918a71cdbe9 | 911 | //Invalid state |
Sergunb | 0:8918a71cdbe9 | 912 | error = ERROR_WRONG_STATE; |
Sergunb | 0:8918a71cdbe9 | 913 | } |
Sergunb | 0:8918a71cdbe9 | 914 | |
Sergunb | 0:8918a71cdbe9 | 915 | //Any error to report? |
Sergunb | 0:8918a71cdbe9 | 916 | if(error) |
Sergunb | 0:8918a71cdbe9 | 917 | break; |
Sergunb | 0:8918a71cdbe9 | 918 | } |
Sergunb | 0:8918a71cdbe9 | 919 | |
Sergunb | 0:8918a71cdbe9 | 920 | //Return status code |
Sergunb | 0:8918a71cdbe9 | 921 | return error; |
Sergunb | 0:8918a71cdbe9 | 922 | } |
Sergunb | 0:8918a71cdbe9 | 923 | |
Sergunb | 0:8918a71cdbe9 | 924 | |
Sergunb | 0:8918a71cdbe9 | 925 | /** |
Sergunb | 0:8918a71cdbe9 | 926 | * @brief Transmit data over the WebSocket connection |
Sergunb | 0:8918a71cdbe9 | 927 | * @param[in] webSocket Handle that identifies a WebSocket |
Sergunb | 0:8918a71cdbe9 | 928 | * @param[in] data Pointer to a buffer containing the data to be transmitted |
Sergunb | 0:8918a71cdbe9 | 929 | * @param[in] length Number of data bytes to send |
Sergunb | 0:8918a71cdbe9 | 930 | * @param[in] type Frame type |
Sergunb | 0:8918a71cdbe9 | 931 | * @param[out] written Actual number of bytes written (optional parameter) |
Sergunb | 0:8918a71cdbe9 | 932 | * @return Error code |
Sergunb | 0:8918a71cdbe9 | 933 | **/ |
Sergunb | 0:8918a71cdbe9 | 934 | |
Sergunb | 0:8918a71cdbe9 | 935 | error_t webSocketSend(WebSocket *webSocket, const void *data, |
Sergunb | 0:8918a71cdbe9 | 936 | size_t length, WebSocketFrameType type, size_t *written) |
Sergunb | 0:8918a71cdbe9 | 937 | { |
Sergunb | 0:8918a71cdbe9 | 938 | //An unfragmented message consists of a single frame with the FIN bit |
Sergunb | 0:8918a71cdbe9 | 939 | //set and an opcode other than 0 |
Sergunb | 0:8918a71cdbe9 | 940 | return webSocketSendEx(webSocket, data, length, |
Sergunb | 0:8918a71cdbe9 | 941 | type, written, TRUE, TRUE); |
Sergunb | 0:8918a71cdbe9 | 942 | } |
Sergunb | 0:8918a71cdbe9 | 943 | |
Sergunb | 0:8918a71cdbe9 | 944 | |
Sergunb | 0:8918a71cdbe9 | 945 | /** |
Sergunb | 0:8918a71cdbe9 | 946 | * @brief Transmit data over the WebSocket connection |
Sergunb | 0:8918a71cdbe9 | 947 | * @param[in] webSocket Handle that identifies a WebSocket |
Sergunb | 0:8918a71cdbe9 | 948 | * @param[in] data Pointer to a buffer containing the data to be transmitted |
Sergunb | 0:8918a71cdbe9 | 949 | * @param[in] length Number of data bytes to send |
Sergunb | 0:8918a71cdbe9 | 950 | * @param[in] type Frame type |
Sergunb | 0:8918a71cdbe9 | 951 | * @param[out] written Actual number of bytes written (optional parameter) |
Sergunb | 0:8918a71cdbe9 | 952 | * @param[in] firstFrag First fragment of the message |
Sergunb | 0:8918a71cdbe9 | 953 | * @param[in] lastFrag Last fragment of the message |
Sergunb | 0:8918a71cdbe9 | 954 | **/ |
Sergunb | 0:8918a71cdbe9 | 955 | |
Sergunb | 0:8918a71cdbe9 | 956 | error_t webSocketSendEx(WebSocket *webSocket, const void *data, size_t length, |
Sergunb | 0:8918a71cdbe9 | 957 | WebSocketFrameType type, size_t *written, bool_t firstFrag, bool_t lastFrag) |
Sergunb | 0:8918a71cdbe9 | 958 | { |
Sergunb | 0:8918a71cdbe9 | 959 | error_t error; |
Sergunb | 0:8918a71cdbe9 | 960 | size_t i; |
Sergunb | 0:8918a71cdbe9 | 961 | size_t j; |
Sergunb | 0:8918a71cdbe9 | 962 | size_t k; |
Sergunb | 0:8918a71cdbe9 | 963 | size_t n; |
Sergunb | 0:8918a71cdbe9 | 964 | const uint8_t *p; |
Sergunb | 0:8918a71cdbe9 | 965 | WebSocketFrameContext *txContext; |
Sergunb | 0:8918a71cdbe9 | 966 | |
Sergunb | 0:8918a71cdbe9 | 967 | //Check parameters |
Sergunb | 0:8918a71cdbe9 | 968 | if(webSocket == NULL || data == NULL) |
Sergunb | 0:8918a71cdbe9 | 969 | return ERROR_INVALID_PARAMETER; |
Sergunb | 0:8918a71cdbe9 | 970 | |
Sergunb | 0:8918a71cdbe9 | 971 | //A data frame may be transmitted by either the client or the server at |
Sergunb | 0:8918a71cdbe9 | 972 | //any time after opening handshake completion and before that endpoint |
Sergunb | 0:8918a71cdbe9 | 973 | //has sent a Close frame |
Sergunb | 0:8918a71cdbe9 | 974 | if(webSocket->state != WS_STATE_OPEN) |
Sergunb | 0:8918a71cdbe9 | 975 | return ERROR_NOT_CONNECTED; |
Sergunb | 0:8918a71cdbe9 | 976 | |
Sergunb | 0:8918a71cdbe9 | 977 | //Point to the TX context |
Sergunb | 0:8918a71cdbe9 | 978 | txContext = &webSocket->txContext; |
Sergunb | 0:8918a71cdbe9 | 979 | |
Sergunb | 0:8918a71cdbe9 | 980 | //Initialize status code |
Sergunb | 0:8918a71cdbe9 | 981 | error = NO_ERROR; |
Sergunb | 0:8918a71cdbe9 | 982 | |
Sergunb | 0:8918a71cdbe9 | 983 | //Point to the application data to be written |
Sergunb | 0:8918a71cdbe9 | 984 | p = (const uint8_t *) data; |
Sergunb | 0:8918a71cdbe9 | 985 | //No data has been transmitted yet |
Sergunb | 0:8918a71cdbe9 | 986 | i = 0; |
Sergunb | 0:8918a71cdbe9 | 987 | |
Sergunb | 0:8918a71cdbe9 | 988 | //Send as much data as possible |
Sergunb | 0:8918a71cdbe9 | 989 | while(1) |
Sergunb | 0:8918a71cdbe9 | 990 | { |
Sergunb | 0:8918a71cdbe9 | 991 | //Check current sub-state |
Sergunb | 0:8918a71cdbe9 | 992 | if(txContext->state == WS_SUB_STATE_INIT) |
Sergunb | 0:8918a71cdbe9 | 993 | { |
Sergunb | 0:8918a71cdbe9 | 994 | //A fragmented message consists of a single frame with the FIN bit |
Sergunb | 0:8918a71cdbe9 | 995 | //clear and an opcode other than 0, followed by zero or more frames |
Sergunb | 0:8918a71cdbe9 | 996 | //with the FIN bit clear and the opcode set to 0, and terminated by |
Sergunb | 0:8918a71cdbe9 | 997 | //a single frame with the FIN bit set and an opcode of 0 |
Sergunb | 0:8918a71cdbe9 | 998 | if(!firstFrag) |
Sergunb | 0:8918a71cdbe9 | 999 | type = WS_FRAME_TYPE_CONTINUATION; |
Sergunb | 0:8918a71cdbe9 | 1000 | |
Sergunb | 0:8918a71cdbe9 | 1001 | //Format WebSocket frame header |
Sergunb | 0:8918a71cdbe9 | 1002 | error = webSocketFormatFrameHeader(webSocket, lastFrag, type, length - i); |
Sergunb | 0:8918a71cdbe9 | 1003 | |
Sergunb | 0:8918a71cdbe9 | 1004 | //Send the frame header |
Sergunb | 0:8918a71cdbe9 | 1005 | txContext->state = WS_SUB_STATE_FRAME_HEADER; |
Sergunb | 0:8918a71cdbe9 | 1006 | } |
Sergunb | 0:8918a71cdbe9 | 1007 | else if(txContext->state == WS_SUB_STATE_FRAME_HEADER) |
Sergunb | 0:8918a71cdbe9 | 1008 | { |
Sergunb | 0:8918a71cdbe9 | 1009 | //Any remaining data to be sent? |
Sergunb | 0:8918a71cdbe9 | 1010 | if(txContext->bufferPos < txContext->bufferLen) |
Sergunb | 0:8918a71cdbe9 | 1011 | { |
Sergunb | 0:8918a71cdbe9 | 1012 | //Send more data |
Sergunb | 0:8918a71cdbe9 | 1013 | error = webSocketSendData(webSocket, |
Sergunb | 0:8918a71cdbe9 | 1014 | txContext->buffer + txContext->bufferPos, |
Sergunb | 0:8918a71cdbe9 | 1015 | txContext->bufferLen - txContext->bufferPos, &n, 0); |
Sergunb | 0:8918a71cdbe9 | 1016 | |
Sergunb | 0:8918a71cdbe9 | 1017 | //Advance data pointer |
Sergunb | 0:8918a71cdbe9 | 1018 | txContext->bufferPos += n; |
Sergunb | 0:8918a71cdbe9 | 1019 | } |
Sergunb | 0:8918a71cdbe9 | 1020 | else |
Sergunb | 0:8918a71cdbe9 | 1021 | { |
Sergunb | 0:8918a71cdbe9 | 1022 | //Flush the transmit buffer |
Sergunb | 0:8918a71cdbe9 | 1023 | txContext->payloadPos = 0; |
Sergunb | 0:8918a71cdbe9 | 1024 | txContext->bufferPos = 0; |
Sergunb | 0:8918a71cdbe9 | 1025 | txContext->bufferLen = 0; |
Sergunb | 0:8918a71cdbe9 | 1026 | |
Sergunb | 0:8918a71cdbe9 | 1027 | //Send the payload of the WebSocket frame |
Sergunb | 0:8918a71cdbe9 | 1028 | txContext->state = WS_SUB_STATE_FRAME_PAYLOAD; |
Sergunb | 0:8918a71cdbe9 | 1029 | } |
Sergunb | 0:8918a71cdbe9 | 1030 | } |
Sergunb | 0:8918a71cdbe9 | 1031 | else if(txContext->state == WS_SUB_STATE_FRAME_PAYLOAD) |
Sergunb | 0:8918a71cdbe9 | 1032 | { |
Sergunb | 0:8918a71cdbe9 | 1033 | //Any remaining data to be sent? |
Sergunb | 0:8918a71cdbe9 | 1034 | if(txContext->bufferPos < txContext->bufferLen) |
Sergunb | 0:8918a71cdbe9 | 1035 | { |
Sergunb | 0:8918a71cdbe9 | 1036 | //Send more data |
Sergunb | 0:8918a71cdbe9 | 1037 | error = webSocketSendData(webSocket, |
Sergunb | 0:8918a71cdbe9 | 1038 | txContext->buffer + txContext->bufferPos, |
Sergunb | 0:8918a71cdbe9 | 1039 | txContext->bufferLen - txContext->bufferPos, &n, 0); |
Sergunb | 0:8918a71cdbe9 | 1040 | |
Sergunb | 0:8918a71cdbe9 | 1041 | //Advance data pointer |
Sergunb | 0:8918a71cdbe9 | 1042 | txContext->payloadPos += n; |
Sergunb | 0:8918a71cdbe9 | 1043 | txContext->bufferPos += n; |
Sergunb | 0:8918a71cdbe9 | 1044 | |
Sergunb | 0:8918a71cdbe9 | 1045 | //Total number of data that have been written |
Sergunb | 0:8918a71cdbe9 | 1046 | i += n; |
Sergunb | 0:8918a71cdbe9 | 1047 | } |
Sergunb | 0:8918a71cdbe9 | 1048 | else |
Sergunb | 0:8918a71cdbe9 | 1049 | { |
Sergunb | 0:8918a71cdbe9 | 1050 | //Send as much data as possible |
Sergunb | 0:8918a71cdbe9 | 1051 | if(txContext->payloadPos < txContext->payloadLen) |
Sergunb | 0:8918a71cdbe9 | 1052 | { |
Sergunb | 0:8918a71cdbe9 | 1053 | //Calculate the number of bytes that are pending |
Sergunb | 0:8918a71cdbe9 | 1054 | n = MIN(length - i, txContext->payloadLen - txContext->payloadPos); |
Sergunb | 0:8918a71cdbe9 | 1055 | //Limit the number of bytes to be copied at a time |
Sergunb | 0:8918a71cdbe9 | 1056 | n = MIN(n, WEB_SOCKET_BUFFER_SIZE); |
Sergunb | 0:8918a71cdbe9 | 1057 | |
Sergunb | 0:8918a71cdbe9 | 1058 | //Copy application data to the transmit buffer |
Sergunb | 0:8918a71cdbe9 | 1059 | memcpy(txContext->buffer, p, n); |
Sergunb | 0:8918a71cdbe9 | 1060 | |
Sergunb | 0:8918a71cdbe9 | 1061 | //All frames sent from the client to the server are masked |
Sergunb | 0:8918a71cdbe9 | 1062 | if(webSocket->endpoint == WS_ENDPOINT_CLIENT) |
Sergunb | 0:8918a71cdbe9 | 1063 | { |
Sergunb | 0:8918a71cdbe9 | 1064 | //Apply masking |
Sergunb | 0:8918a71cdbe9 | 1065 | for(j = 0; j < n; j++) |
Sergunb | 0:8918a71cdbe9 | 1066 | { |
Sergunb | 0:8918a71cdbe9 | 1067 | //Index of the masking key to be applied |
Sergunb | 0:8918a71cdbe9 | 1068 | k = (txContext->payloadPos + j) % 4; |
Sergunb | 0:8918a71cdbe9 | 1069 | //Convert unmasked data into masked data |
Sergunb | 0:8918a71cdbe9 | 1070 | txContext->buffer[j] = p[i + j] ^ txContext->maskingKey[k]; |
Sergunb | 0:8918a71cdbe9 | 1071 | } |
Sergunb | 0:8918a71cdbe9 | 1072 | } |
Sergunb | 0:8918a71cdbe9 | 1073 | |
Sergunb | 0:8918a71cdbe9 | 1074 | //Rewind to the beginning of the buffer |
Sergunb | 0:8918a71cdbe9 | 1075 | txContext->bufferPos = 0; |
Sergunb | 0:8918a71cdbe9 | 1076 | //Update the number of data buffered but not yet sent |
Sergunb | 0:8918a71cdbe9 | 1077 | txContext->bufferLen = n; |
Sergunb | 0:8918a71cdbe9 | 1078 | } |
Sergunb | 0:8918a71cdbe9 | 1079 | else |
Sergunb | 0:8918a71cdbe9 | 1080 | { |
Sergunb | 0:8918a71cdbe9 | 1081 | //Prepare to send a new WebSocket frame |
Sergunb | 0:8918a71cdbe9 | 1082 | txContext->state = WS_SUB_STATE_INIT; |
Sergunb | 0:8918a71cdbe9 | 1083 | |
Sergunb | 0:8918a71cdbe9 | 1084 | //Write operation complete? |
Sergunb | 0:8918a71cdbe9 | 1085 | if(i >= length) |
Sergunb | 0:8918a71cdbe9 | 1086 | break; |
Sergunb | 0:8918a71cdbe9 | 1087 | } |
Sergunb | 0:8918a71cdbe9 | 1088 | } |
Sergunb | 0:8918a71cdbe9 | 1089 | } |
Sergunb | 0:8918a71cdbe9 | 1090 | else |
Sergunb | 0:8918a71cdbe9 | 1091 | { |
Sergunb | 0:8918a71cdbe9 | 1092 | //Invalid state |
Sergunb | 0:8918a71cdbe9 | 1093 | error = ERROR_WRONG_STATE; |
Sergunb | 0:8918a71cdbe9 | 1094 | } |
Sergunb | 0:8918a71cdbe9 | 1095 | |
Sergunb | 0:8918a71cdbe9 | 1096 | //Any error to report? |
Sergunb | 0:8918a71cdbe9 | 1097 | if(error) |
Sergunb | 0:8918a71cdbe9 | 1098 | break; |
Sergunb | 0:8918a71cdbe9 | 1099 | } |
Sergunb | 0:8918a71cdbe9 | 1100 | |
Sergunb | 0:8918a71cdbe9 | 1101 | //Total number of data that have been written |
Sergunb | 0:8918a71cdbe9 | 1102 | if(written != NULL) |
Sergunb | 0:8918a71cdbe9 | 1103 | *written = i; |
Sergunb | 0:8918a71cdbe9 | 1104 | |
Sergunb | 0:8918a71cdbe9 | 1105 | //Return status code |
Sergunb | 0:8918a71cdbe9 | 1106 | return error; |
Sergunb | 0:8918a71cdbe9 | 1107 | } |
Sergunb | 0:8918a71cdbe9 | 1108 | |
Sergunb | 0:8918a71cdbe9 | 1109 | |
Sergunb | 0:8918a71cdbe9 | 1110 | /** |
Sergunb | 0:8918a71cdbe9 | 1111 | * @brief Receive data from a WebSocket connection |
Sergunb | 0:8918a71cdbe9 | 1112 | * @param[in] webSocket Handle that identifies a WebSocket |
Sergunb | 0:8918a71cdbe9 | 1113 | * @param[out] data Buffer where to store the incoming data |
Sergunb | 0:8918a71cdbe9 | 1114 | * @param[in] size Maximum number of bytes that can be received |
Sergunb | 0:8918a71cdbe9 | 1115 | * @param[out] type Frame type |
Sergunb | 0:8918a71cdbe9 | 1116 | * @param[out] received Number of bytes that have been received |
Sergunb | 0:8918a71cdbe9 | 1117 | * @return Error code |
Sergunb | 0:8918a71cdbe9 | 1118 | **/ |
Sergunb | 0:8918a71cdbe9 | 1119 | |
Sergunb | 0:8918a71cdbe9 | 1120 | error_t webSocketReceive(WebSocket *webSocket, void *data, |
Sergunb | 0:8918a71cdbe9 | 1121 | size_t size, WebSocketFrameType *type, size_t *received) |
Sergunb | 0:8918a71cdbe9 | 1122 | { |
Sergunb | 0:8918a71cdbe9 | 1123 | bool_t firstFrag; |
Sergunb | 0:8918a71cdbe9 | 1124 | bool_t lastFrag; |
Sergunb | 0:8918a71cdbe9 | 1125 | |
Sergunb | 0:8918a71cdbe9 | 1126 | return webSocketReceiveEx(webSocket, data, size, |
Sergunb | 0:8918a71cdbe9 | 1127 | type, received, &firstFrag, &lastFrag); |
Sergunb | 0:8918a71cdbe9 | 1128 | } |
Sergunb | 0:8918a71cdbe9 | 1129 | |
Sergunb | 0:8918a71cdbe9 | 1130 | |
Sergunb | 0:8918a71cdbe9 | 1131 | /** |
Sergunb | 0:8918a71cdbe9 | 1132 | * @brief Receive data from a WebSocket connection |
Sergunb | 0:8918a71cdbe9 | 1133 | * @param[in] webSocket Handle that identifies a WebSocket |
Sergunb | 0:8918a71cdbe9 | 1134 | * @param[out] data Buffer where to store the incoming data |
Sergunb | 0:8918a71cdbe9 | 1135 | * @param[in] size Maximum number of bytes that can be received |
Sergunb | 0:8918a71cdbe9 | 1136 | * @param[out] type Frame type |
Sergunb | 0:8918a71cdbe9 | 1137 | * @param[out] received Number of bytes that have been received |
Sergunb | 0:8918a71cdbe9 | 1138 | * @param[out] firstFrag First fragment of the message |
Sergunb | 0:8918a71cdbe9 | 1139 | * @param[out] lastFrag Last fragment of the message |
Sergunb | 0:8918a71cdbe9 | 1140 | * @return Error code |
Sergunb | 0:8918a71cdbe9 | 1141 | **/ |
Sergunb | 0:8918a71cdbe9 | 1142 | |
Sergunb | 0:8918a71cdbe9 | 1143 | error_t webSocketReceiveEx(WebSocket *webSocket, void *data, size_t size, |
Sergunb | 0:8918a71cdbe9 | 1144 | WebSocketFrameType *type, size_t *received, bool_t *firstFrag, bool_t *lastFrag) |
Sergunb | 0:8918a71cdbe9 | 1145 | { |
Sergunb | 0:8918a71cdbe9 | 1146 | error_t error; |
Sergunb | 0:8918a71cdbe9 | 1147 | size_t i; |
Sergunb | 0:8918a71cdbe9 | 1148 | size_t j; |
Sergunb | 0:8918a71cdbe9 | 1149 | size_t k; |
Sergunb | 0:8918a71cdbe9 | 1150 | size_t n; |
Sergunb | 0:8918a71cdbe9 | 1151 | WebSocketFrame *frame; |
Sergunb | 0:8918a71cdbe9 | 1152 | WebSocketFrameContext *rxContext; |
Sergunb | 0:8918a71cdbe9 | 1153 | |
Sergunb | 0:8918a71cdbe9 | 1154 | //Make sure the WebSocket handle is valid |
Sergunb | 0:8918a71cdbe9 | 1155 | if(webSocket == NULL) |
Sergunb | 0:8918a71cdbe9 | 1156 | return ERROR_INVALID_PARAMETER; |
Sergunb | 0:8918a71cdbe9 | 1157 | |
Sergunb | 0:8918a71cdbe9 | 1158 | //Check the state of the WebSocket connection |
Sergunb | 0:8918a71cdbe9 | 1159 | if(webSocket->state != WS_STATE_OPEN && |
Sergunb | 0:8918a71cdbe9 | 1160 | webSocket->state != WS_STATE_CLOSING_RX) |
Sergunb | 0:8918a71cdbe9 | 1161 | return ERROR_NOT_CONNECTED; |
Sergunb | 0:8918a71cdbe9 | 1162 | |
Sergunb | 0:8918a71cdbe9 | 1163 | //Point to the RX context |
Sergunb | 0:8918a71cdbe9 | 1164 | rxContext = &webSocket->rxContext; |
Sergunb | 0:8918a71cdbe9 | 1165 | //Point to the WebSocket frame header |
Sergunb | 0:8918a71cdbe9 | 1166 | frame = (WebSocketFrame *) rxContext->buffer; |
Sergunb | 0:8918a71cdbe9 | 1167 | |
Sergunb | 0:8918a71cdbe9 | 1168 | //Initialize status code |
Sergunb | 0:8918a71cdbe9 | 1169 | error = NO_ERROR; |
Sergunb | 0:8918a71cdbe9 | 1170 | |
Sergunb | 0:8918a71cdbe9 | 1171 | //Initialize flags |
Sergunb | 0:8918a71cdbe9 | 1172 | if(type != NULL) |
Sergunb | 0:8918a71cdbe9 | 1173 | *type = WS_FRAME_TYPE_CONTINUATION; |
Sergunb | 0:8918a71cdbe9 | 1174 | if(firstFrag != NULL) |
Sergunb | 0:8918a71cdbe9 | 1175 | *firstFrag = FALSE; |
Sergunb | 0:8918a71cdbe9 | 1176 | if(lastFrag != NULL) |
Sergunb | 0:8918a71cdbe9 | 1177 | *lastFrag = FALSE; |
Sergunb | 0:8918a71cdbe9 | 1178 | |
Sergunb | 0:8918a71cdbe9 | 1179 | //No data has been read yet |
Sergunb | 0:8918a71cdbe9 | 1180 | i = 0; |
Sergunb | 0:8918a71cdbe9 | 1181 | |
Sergunb | 0:8918a71cdbe9 | 1182 | //Read as much data as possible |
Sergunb | 0:8918a71cdbe9 | 1183 | while(i < size) |
Sergunb | 0:8918a71cdbe9 | 1184 | { |
Sergunb | 0:8918a71cdbe9 | 1185 | //Check current sub-state |
Sergunb | 0:8918a71cdbe9 | 1186 | if(rxContext->state == WS_SUB_STATE_INIT) |
Sergunb | 0:8918a71cdbe9 | 1187 | { |
Sergunb | 0:8918a71cdbe9 | 1188 | //Flush the receive buffer |
Sergunb | 0:8918a71cdbe9 | 1189 | rxContext->bufferPos = 0; |
Sergunb | 0:8918a71cdbe9 | 1190 | rxContext->bufferLen = sizeof(WebSocketFrame); |
Sergunb | 0:8918a71cdbe9 | 1191 | |
Sergunb | 0:8918a71cdbe9 | 1192 | //Decode the frame header |
Sergunb | 0:8918a71cdbe9 | 1193 | rxContext->state = WS_SUB_STATE_FRAME_HEADER; |
Sergunb | 0:8918a71cdbe9 | 1194 | } |
Sergunb | 0:8918a71cdbe9 | 1195 | else if(rxContext->state == WS_SUB_STATE_FRAME_HEADER) |
Sergunb | 0:8918a71cdbe9 | 1196 | { |
Sergunb | 0:8918a71cdbe9 | 1197 | //Incomplete frame header? |
Sergunb | 0:8918a71cdbe9 | 1198 | if(rxContext->bufferPos < rxContext->bufferLen) |
Sergunb | 0:8918a71cdbe9 | 1199 | { |
Sergunb | 0:8918a71cdbe9 | 1200 | //Read more data |
Sergunb | 0:8918a71cdbe9 | 1201 | error = webSocketReceiveData(webSocket, |
Sergunb | 0:8918a71cdbe9 | 1202 | rxContext->buffer + rxContext->bufferPos, |
Sergunb | 0:8918a71cdbe9 | 1203 | rxContext->bufferLen - rxContext->bufferPos, &n, 0); |
Sergunb | 0:8918a71cdbe9 | 1204 | |
Sergunb | 0:8918a71cdbe9 | 1205 | //Advance data pointer |
Sergunb | 0:8918a71cdbe9 | 1206 | rxContext->bufferPos += n; |
Sergunb | 0:8918a71cdbe9 | 1207 | } |
Sergunb | 0:8918a71cdbe9 | 1208 | else |
Sergunb | 0:8918a71cdbe9 | 1209 | { |
Sergunb | 0:8918a71cdbe9 | 1210 | //Check the Payload Length field |
Sergunb | 0:8918a71cdbe9 | 1211 | if(frame->payloadLen == 126) |
Sergunb | 0:8918a71cdbe9 | 1212 | rxContext->bufferLen += sizeof(uint16_t); |
Sergunb | 0:8918a71cdbe9 | 1213 | else if(frame->payloadLen == 127) |
Sergunb | 0:8918a71cdbe9 | 1214 | rxContext->bufferLen += sizeof(uint64_t); |
Sergunb | 0:8918a71cdbe9 | 1215 | |
Sergunb | 0:8918a71cdbe9 | 1216 | //Check whether the masking key is present |
Sergunb | 0:8918a71cdbe9 | 1217 | if(frame->mask) |
Sergunb | 0:8918a71cdbe9 | 1218 | rxContext->bufferLen += sizeof(uint32_t); |
Sergunb | 0:8918a71cdbe9 | 1219 | |
Sergunb | 0:8918a71cdbe9 | 1220 | //The Opcode field defines the interpretation of the payload data |
Sergunb | 0:8918a71cdbe9 | 1221 | if(frame->opcode == WS_FRAME_TYPE_CLOSE) |
Sergunb | 0:8918a71cdbe9 | 1222 | { |
Sergunb | 0:8918a71cdbe9 | 1223 | //All control frames must have a payload length of 125 bytes or less |
Sergunb | 0:8918a71cdbe9 | 1224 | if(frame->payloadLen <= 125) |
Sergunb | 0:8918a71cdbe9 | 1225 | { |
Sergunb | 0:8918a71cdbe9 | 1226 | //Retrieve the length of the WebSocket frame |
Sergunb | 0:8918a71cdbe9 | 1227 | rxContext->bufferLen += frame->payloadLen; |
Sergunb | 0:8918a71cdbe9 | 1228 | } |
Sergunb | 0:8918a71cdbe9 | 1229 | else |
Sergunb | 0:8918a71cdbe9 | 1230 | { |
Sergunb | 0:8918a71cdbe9 | 1231 | //Report a protocol error |
Sergunb | 0:8918a71cdbe9 | 1232 | webSocket->statusCode = WS_STATUS_CODE_PROTOCOL_ERROR; |
Sergunb | 0:8918a71cdbe9 | 1233 | //Terminate the WebSocket connection |
Sergunb | 0:8918a71cdbe9 | 1234 | error = ERROR_INVALID_FRAME; |
Sergunb | 0:8918a71cdbe9 | 1235 | } |
Sergunb | 0:8918a71cdbe9 | 1236 | } |
Sergunb | 0:8918a71cdbe9 | 1237 | |
Sergunb | 0:8918a71cdbe9 | 1238 | //Decode the extended payload length and the masking key, if any |
Sergunb | 0:8918a71cdbe9 | 1239 | rxContext->state = WS_SUB_STATE_FRAME_EXT_HEADER; |
Sergunb | 0:8918a71cdbe9 | 1240 | } |
Sergunb | 0:8918a71cdbe9 | 1241 | } |
Sergunb | 0:8918a71cdbe9 | 1242 | else if(rxContext->state == WS_SUB_STATE_FRAME_EXT_HEADER) |
Sergunb | 0:8918a71cdbe9 | 1243 | { |
Sergunb | 0:8918a71cdbe9 | 1244 | //Incomplete frame header? |
Sergunb | 0:8918a71cdbe9 | 1245 | if(rxContext->bufferPos < rxContext->bufferLen) |
Sergunb | 0:8918a71cdbe9 | 1246 | { |
Sergunb | 0:8918a71cdbe9 | 1247 | //Read more data |
Sergunb | 0:8918a71cdbe9 | 1248 | error = webSocketReceiveData(webSocket, |
Sergunb | 0:8918a71cdbe9 | 1249 | rxContext->buffer + rxContext->bufferPos, |
Sergunb | 0:8918a71cdbe9 | 1250 | rxContext->bufferLen - rxContext->bufferPos, &n, 0); |
Sergunb | 0:8918a71cdbe9 | 1251 | |
Sergunb | 0:8918a71cdbe9 | 1252 | //Advance data pointer |
Sergunb | 0:8918a71cdbe9 | 1253 | rxContext->bufferPos += n; |
Sergunb | 0:8918a71cdbe9 | 1254 | } |
Sergunb | 0:8918a71cdbe9 | 1255 | else |
Sergunb | 0:8918a71cdbe9 | 1256 | { |
Sergunb | 0:8918a71cdbe9 | 1257 | //Parse the header of the WebSocket frame |
Sergunb | 0:8918a71cdbe9 | 1258 | error = webSocketParseFrameHeader(webSocket, frame, type); |
Sergunb | 0:8918a71cdbe9 | 1259 | |
Sergunb | 0:8918a71cdbe9 | 1260 | //Check status code |
Sergunb | 0:8918a71cdbe9 | 1261 | if(error == ERROR_UNEXPECTED_MESSAGE) |
Sergunb | 0:8918a71cdbe9 | 1262 | { |
Sergunb | 0:8918a71cdbe9 | 1263 | error = NO_ERROR; |
Sergunb | 0:8918a71cdbe9 | 1264 | break; |
Sergunb | 0:8918a71cdbe9 | 1265 | } |
Sergunb | 0:8918a71cdbe9 | 1266 | else if(error == NO_ERROR) |
Sergunb | 0:8918a71cdbe9 | 1267 | { |
Sergunb | 0:8918a71cdbe9 | 1268 | if(firstFrag != NULL) |
Sergunb | 0:8918a71cdbe9 | 1269 | *firstFrag = TRUE; |
Sergunb | 0:8918a71cdbe9 | 1270 | } |
Sergunb | 0:8918a71cdbe9 | 1271 | |
Sergunb | 0:8918a71cdbe9 | 1272 | //Flush the receive buffer |
Sergunb | 0:8918a71cdbe9 | 1273 | rxContext->payloadPos = 0; |
Sergunb | 0:8918a71cdbe9 | 1274 | rxContext->bufferPos = 0; |
Sergunb | 0:8918a71cdbe9 | 1275 | rxContext->bufferLen = 0; |
Sergunb | 0:8918a71cdbe9 | 1276 | |
Sergunb | 0:8918a71cdbe9 | 1277 | //Decode the payload of the WebSocket frame |
Sergunb | 0:8918a71cdbe9 | 1278 | rxContext->state = WS_SUB_STATE_FRAME_PAYLOAD; |
Sergunb | 0:8918a71cdbe9 | 1279 | } |
Sergunb | 0:8918a71cdbe9 | 1280 | } |
Sergunb | 0:8918a71cdbe9 | 1281 | else if(rxContext->state == WS_SUB_STATE_FRAME_PAYLOAD) |
Sergunb | 0:8918a71cdbe9 | 1282 | { |
Sergunb | 0:8918a71cdbe9 | 1283 | if(rxContext->payloadPos < rxContext->payloadLen) |
Sergunb | 0:8918a71cdbe9 | 1284 | { |
Sergunb | 0:8918a71cdbe9 | 1285 | //Limit the number of bytes to read at a time |
Sergunb | 0:8918a71cdbe9 | 1286 | n = MIN(size - i, rxContext->payloadLen - rxContext->payloadPos); |
Sergunb | 0:8918a71cdbe9 | 1287 | //Limit the number of bytes to be copied at a time |
Sergunb | 0:8918a71cdbe9 | 1288 | n = MIN(n, WEB_SOCKET_BUFFER_SIZE); |
Sergunb | 0:8918a71cdbe9 | 1289 | |
Sergunb | 0:8918a71cdbe9 | 1290 | //Read more data |
Sergunb | 0:8918a71cdbe9 | 1291 | error = webSocketReceiveData(webSocket, rxContext->buffer, n, &n, 0); |
Sergunb | 0:8918a71cdbe9 | 1292 | |
Sergunb | 0:8918a71cdbe9 | 1293 | //All frames sent from the client to the server are masked |
Sergunb | 0:8918a71cdbe9 | 1294 | if(rxContext->mask) |
Sergunb | 0:8918a71cdbe9 | 1295 | { |
Sergunb | 0:8918a71cdbe9 | 1296 | //Unmask the data |
Sergunb | 0:8918a71cdbe9 | 1297 | for(j = 0; j < n; j++) |
Sergunb | 0:8918a71cdbe9 | 1298 | { |
Sergunb | 0:8918a71cdbe9 | 1299 | //Index of the masking key to be applied |
Sergunb | 0:8918a71cdbe9 | 1300 | k = (rxContext->payloadPos + j) % 4; |
Sergunb | 0:8918a71cdbe9 | 1301 | //Convert masked data into unmasked data |
Sergunb | 0:8918a71cdbe9 | 1302 | rxContext->buffer[j] ^= rxContext->maskingKey[k]; |
Sergunb | 0:8918a71cdbe9 | 1303 | } |
Sergunb | 0:8918a71cdbe9 | 1304 | } |
Sergunb | 0:8918a71cdbe9 | 1305 | |
Sergunb | 0:8918a71cdbe9 | 1306 | //Text frame? |
Sergunb | 0:8918a71cdbe9 | 1307 | if(rxContext->dataFrameType == WS_FRAME_TYPE_TEXT && |
Sergunb | 0:8918a71cdbe9 | 1308 | rxContext->controlFrameType == WS_FRAME_TYPE_CONTINUATION) |
Sergunb | 0:8918a71cdbe9 | 1309 | { |
Sergunb | 0:8918a71cdbe9 | 1310 | //Compute the number of remaining data bytes in the UTF-8 stream |
Sergunb | 0:8918a71cdbe9 | 1311 | if(rxContext->fin) |
Sergunb | 0:8918a71cdbe9 | 1312 | k = rxContext->payloadLen - rxContext->payloadPos; |
Sergunb | 0:8918a71cdbe9 | 1313 | else |
Sergunb | 0:8918a71cdbe9 | 1314 | k = 0; |
Sergunb | 0:8918a71cdbe9 | 1315 | |
Sergunb | 0:8918a71cdbe9 | 1316 | //Invalid UTF-8 sequence? |
Sergunb | 0:8918a71cdbe9 | 1317 | if(!webSocketCheckUtf8Stream(&webSocket->utf8Context, |
Sergunb | 0:8918a71cdbe9 | 1318 | rxContext->buffer, n, k)) |
Sergunb | 0:8918a71cdbe9 | 1319 | { |
Sergunb | 0:8918a71cdbe9 | 1320 | //The received data is not consistent with the type of the message |
Sergunb | 0:8918a71cdbe9 | 1321 | webSocket->statusCode = WS_STATUS_CODE_INVALID_PAYLOAD_DATA; |
Sergunb | 0:8918a71cdbe9 | 1322 | //The endpoint must fail the WebSocket connection |
Sergunb | 0:8918a71cdbe9 | 1323 | error = ERROR_INVALID_FRAME; |
Sergunb | 0:8918a71cdbe9 | 1324 | } |
Sergunb | 0:8918a71cdbe9 | 1325 | } |
Sergunb | 0:8918a71cdbe9 | 1326 | |
Sergunb | 0:8918a71cdbe9 | 1327 | //Sanity check |
Sergunb | 0:8918a71cdbe9 | 1328 | if(data != NULL) |
Sergunb | 0:8918a71cdbe9 | 1329 | { |
Sergunb | 0:8918a71cdbe9 | 1330 | //Copy application data |
Sergunb | 0:8918a71cdbe9 | 1331 | memcpy((uint8_t *) data + i, rxContext->buffer, n); |
Sergunb | 0:8918a71cdbe9 | 1332 | } |
Sergunb | 0:8918a71cdbe9 | 1333 | |
Sergunb | 0:8918a71cdbe9 | 1334 | //Advance data pointer |
Sergunb | 0:8918a71cdbe9 | 1335 | rxContext->payloadPos += n; |
Sergunb | 0:8918a71cdbe9 | 1336 | |
Sergunb | 0:8918a71cdbe9 | 1337 | //Total number of data that have been read |
Sergunb | 0:8918a71cdbe9 | 1338 | i += n; |
Sergunb | 0:8918a71cdbe9 | 1339 | } |
Sergunb | 0:8918a71cdbe9 | 1340 | |
Sergunb | 0:8918a71cdbe9 | 1341 | if(rxContext->payloadPos == rxContext->payloadLen) |
Sergunb | 0:8918a71cdbe9 | 1342 | { |
Sergunb | 0:8918a71cdbe9 | 1343 | //Decode the next WebSocket frame |
Sergunb | 0:8918a71cdbe9 | 1344 | rxContext->state = WS_SUB_STATE_INIT; |
Sergunb | 0:8918a71cdbe9 | 1345 | |
Sergunb | 0:8918a71cdbe9 | 1346 | //Last fragment of the message? |
Sergunb | 0:8918a71cdbe9 | 1347 | if(rxContext->fin || rxContext->controlFrameType != WS_FRAME_TYPE_CONTINUATION) |
Sergunb | 0:8918a71cdbe9 | 1348 | { |
Sergunb | 0:8918a71cdbe9 | 1349 | if(lastFrag != NULL) |
Sergunb | 0:8918a71cdbe9 | 1350 | *lastFrag = TRUE; |
Sergunb | 0:8918a71cdbe9 | 1351 | |
Sergunb | 0:8918a71cdbe9 | 1352 | //Exit immediately |
Sergunb | 0:8918a71cdbe9 | 1353 | break; |
Sergunb | 0:8918a71cdbe9 | 1354 | } |
Sergunb | 0:8918a71cdbe9 | 1355 | } |
Sergunb | 0:8918a71cdbe9 | 1356 | } |
Sergunb | 0:8918a71cdbe9 | 1357 | else |
Sergunb | 0:8918a71cdbe9 | 1358 | { |
Sergunb | 0:8918a71cdbe9 | 1359 | //Invalid state |
Sergunb | 0:8918a71cdbe9 | 1360 | error = ERROR_WRONG_STATE; |
Sergunb | 0:8918a71cdbe9 | 1361 | } |
Sergunb | 0:8918a71cdbe9 | 1362 | |
Sergunb | 0:8918a71cdbe9 | 1363 | //Any error to report? |
Sergunb | 0:8918a71cdbe9 | 1364 | if(error) |
Sergunb | 0:8918a71cdbe9 | 1365 | break; |
Sergunb | 0:8918a71cdbe9 | 1366 | } |
Sergunb | 0:8918a71cdbe9 | 1367 | |
Sergunb | 0:8918a71cdbe9 | 1368 | //Check status code |
Sergunb | 0:8918a71cdbe9 | 1369 | if(!error) |
Sergunb | 0:8918a71cdbe9 | 1370 | { |
Sergunb | 0:8918a71cdbe9 | 1371 | //Return the frame type |
Sergunb | 0:8918a71cdbe9 | 1372 | if(type != NULL) |
Sergunb | 0:8918a71cdbe9 | 1373 | { |
Sergunb | 0:8918a71cdbe9 | 1374 | //Control or data frame? |
Sergunb | 0:8918a71cdbe9 | 1375 | if(rxContext->controlFrameType != WS_FRAME_TYPE_CONTINUATION) |
Sergunb | 0:8918a71cdbe9 | 1376 | *type = rxContext->controlFrameType; |
Sergunb | 0:8918a71cdbe9 | 1377 | else |
Sergunb | 0:8918a71cdbe9 | 1378 | *type = rxContext->dataFrameType; |
Sergunb | 0:8918a71cdbe9 | 1379 | } |
Sergunb | 0:8918a71cdbe9 | 1380 | } |
Sergunb | 0:8918a71cdbe9 | 1381 | |
Sergunb | 0:8918a71cdbe9 | 1382 | //Return the total number of data that have been read |
Sergunb | 0:8918a71cdbe9 | 1383 | if(received != NULL) |
Sergunb | 0:8918a71cdbe9 | 1384 | *received = i; |
Sergunb | 0:8918a71cdbe9 | 1385 | |
Sergunb | 0:8918a71cdbe9 | 1386 | //Return status code |
Sergunb | 0:8918a71cdbe9 | 1387 | return error; |
Sergunb | 0:8918a71cdbe9 | 1388 | } |
Sergunb | 0:8918a71cdbe9 | 1389 | |
Sergunb | 0:8918a71cdbe9 | 1390 | |
Sergunb | 0:8918a71cdbe9 | 1391 | /** |
Sergunb | 0:8918a71cdbe9 | 1392 | * @brief Check whether some data is available in the receive buffer |
Sergunb | 0:8918a71cdbe9 | 1393 | * @param[in] webSocket Handle to a WebSocket |
Sergunb | 0:8918a71cdbe9 | 1394 | * @return The function returns TRUE if some data is pending and can be read |
Sergunb | 0:8918a71cdbe9 | 1395 | * immediately without blocking. Otherwise, FALSE is returned |
Sergunb | 0:8918a71cdbe9 | 1396 | **/ |
Sergunb | 0:8918a71cdbe9 | 1397 | |
Sergunb | 0:8918a71cdbe9 | 1398 | bool_t webSocketIsRxReady(WebSocket *webSocket) |
Sergunb | 0:8918a71cdbe9 | 1399 | { |
Sergunb | 0:8918a71cdbe9 | 1400 | bool_t available = FALSE; |
Sergunb | 0:8918a71cdbe9 | 1401 | |
Sergunb | 0:8918a71cdbe9 | 1402 | #if (WEB_SOCKET_TLS_SUPPORT == ENABLED) |
Sergunb | 0:8918a71cdbe9 | 1403 | //Check whether a secure connection is being used |
Sergunb | 0:8918a71cdbe9 | 1404 | if(webSocket->tlsContext != NULL) |
Sergunb | 0:8918a71cdbe9 | 1405 | { |
Sergunb | 0:8918a71cdbe9 | 1406 | //Check whether some data is pending in the receive buffer |
Sergunb | 0:8918a71cdbe9 | 1407 | if(webSocket->tlsContext->rxBufferLen > 0) |
Sergunb | 0:8918a71cdbe9 | 1408 | available = TRUE; |
Sergunb | 0:8918a71cdbe9 | 1409 | } |
Sergunb | 0:8918a71cdbe9 | 1410 | #endif |
Sergunb | 0:8918a71cdbe9 | 1411 | |
Sergunb | 0:8918a71cdbe9 | 1412 | //The function returns TRUE if some data can be read immediately |
Sergunb | 0:8918a71cdbe9 | 1413 | //without blocking |
Sergunb | 0:8918a71cdbe9 | 1414 | return available; |
Sergunb | 0:8918a71cdbe9 | 1415 | } |
Sergunb | 0:8918a71cdbe9 | 1416 | |
Sergunb | 0:8918a71cdbe9 | 1417 | |
Sergunb | 0:8918a71cdbe9 | 1418 | /** |
Sergunb | 0:8918a71cdbe9 | 1419 | * @brief Gracefully close a WebSocket connection |
Sergunb | 0:8918a71cdbe9 | 1420 | * @param[in] webSocket Handle to a WebSocket |
Sergunb | 0:8918a71cdbe9 | 1421 | **/ |
Sergunb | 0:8918a71cdbe9 | 1422 | |
Sergunb | 0:8918a71cdbe9 | 1423 | error_t webSocketShutdown(WebSocket *webSocket) |
Sergunb | 0:8918a71cdbe9 | 1424 | { |
Sergunb | 0:8918a71cdbe9 | 1425 | error_t error; |
Sergunb | 0:8918a71cdbe9 | 1426 | size_t n; |
Sergunb | 0:8918a71cdbe9 | 1427 | WebSocketFrameContext *txContext; |
Sergunb | 0:8918a71cdbe9 | 1428 | |
Sergunb | 0:8918a71cdbe9 | 1429 | //Make sure the WebSocket handle is valid |
Sergunb | 0:8918a71cdbe9 | 1430 | if(webSocket == NULL) |
Sergunb | 0:8918a71cdbe9 | 1431 | return ERROR_INVALID_PARAMETER; |
Sergunb | 0:8918a71cdbe9 | 1432 | |
Sergunb | 0:8918a71cdbe9 | 1433 | //Point to the TX context |
Sergunb | 0:8918a71cdbe9 | 1434 | txContext = &webSocket->txContext; |
Sergunb | 0:8918a71cdbe9 | 1435 | |
Sergunb | 0:8918a71cdbe9 | 1436 | //Initialize status code |
Sergunb | 0:8918a71cdbe9 | 1437 | error = NO_ERROR; |
Sergunb | 0:8918a71cdbe9 | 1438 | |
Sergunb | 0:8918a71cdbe9 | 1439 | //Closing handshake |
Sergunb | 0:8918a71cdbe9 | 1440 | while(webSocket->state != WS_STATE_CLOSED) |
Sergunb | 0:8918a71cdbe9 | 1441 | { |
Sergunb | 0:8918a71cdbe9 | 1442 | //Check current state |
Sergunb | 0:8918a71cdbe9 | 1443 | if(webSocket->state == WS_STATE_OPEN) |
Sergunb | 0:8918a71cdbe9 | 1444 | { |
Sergunb | 0:8918a71cdbe9 | 1445 | //Check whether the latest frame has been completely transmitted |
Sergunb | 0:8918a71cdbe9 | 1446 | if(txContext->payloadPos == txContext->payloadLen) |
Sergunb | 0:8918a71cdbe9 | 1447 | { |
Sergunb | 0:8918a71cdbe9 | 1448 | //Format Close frame |
Sergunb | 0:8918a71cdbe9 | 1449 | error = webSocketFormatCloseFrame(webSocket); |
Sergunb | 0:8918a71cdbe9 | 1450 | //Send Close frame |
Sergunb | 0:8918a71cdbe9 | 1451 | webSocket->state = WS_STATE_CLOSING_TX; |
Sergunb | 0:8918a71cdbe9 | 1452 | } |
Sergunb | 0:8918a71cdbe9 | 1453 | else |
Sergunb | 0:8918a71cdbe9 | 1454 | { |
Sergunb | 0:8918a71cdbe9 | 1455 | //The WebSocket connection cannot be closed until the |
Sergunb | 0:8918a71cdbe9 | 1456 | //transmission of the frame is complete... |
Sergunb | 0:8918a71cdbe9 | 1457 | error = ERROR_FAILURE; |
Sergunb | 0:8918a71cdbe9 | 1458 | } |
Sergunb | 0:8918a71cdbe9 | 1459 | } |
Sergunb | 0:8918a71cdbe9 | 1460 | else if(webSocket->state == WS_STATE_CLOSING_TX) |
Sergunb | 0:8918a71cdbe9 | 1461 | { |
Sergunb | 0:8918a71cdbe9 | 1462 | //Any remaining data to be sent? |
Sergunb | 0:8918a71cdbe9 | 1463 | if(txContext->bufferPos < txContext->bufferLen) |
Sergunb | 0:8918a71cdbe9 | 1464 | { |
Sergunb | 0:8918a71cdbe9 | 1465 | //Send more data |
Sergunb | 0:8918a71cdbe9 | 1466 | error = webSocketSendData(webSocket, |
Sergunb | 0:8918a71cdbe9 | 1467 | txContext->buffer + txContext->bufferPos, |
Sergunb | 0:8918a71cdbe9 | 1468 | txContext->bufferLen - txContext->bufferPos, &n, 0); |
Sergunb | 0:8918a71cdbe9 | 1469 | |
Sergunb | 0:8918a71cdbe9 | 1470 | //Advance data pointer |
Sergunb | 0:8918a71cdbe9 | 1471 | txContext->bufferPos += n; |
Sergunb | 0:8918a71cdbe9 | 1472 | } |
Sergunb | 0:8918a71cdbe9 | 1473 | else |
Sergunb | 0:8918a71cdbe9 | 1474 | { |
Sergunb | 0:8918a71cdbe9 | 1475 | //Check whether a Close frame has been received from the peer |
Sergunb | 0:8918a71cdbe9 | 1476 | if(webSocket->handshakeContext.closingFrameReceived) |
Sergunb | 0:8918a71cdbe9 | 1477 | webSocket->state = WS_STATE_SHUTDOWN; |
Sergunb | 0:8918a71cdbe9 | 1478 | else |
Sergunb | 0:8918a71cdbe9 | 1479 | webSocket->state = WS_STATE_CLOSING_RX; |
Sergunb | 0:8918a71cdbe9 | 1480 | } |
Sergunb | 0:8918a71cdbe9 | 1481 | } |
Sergunb | 0:8918a71cdbe9 | 1482 | else if(webSocket->state == WS_STATE_CLOSING_RX) |
Sergunb | 0:8918a71cdbe9 | 1483 | { |
Sergunb | 0:8918a71cdbe9 | 1484 | //After receiving a control frame indicating the connection should |
Sergunb | 0:8918a71cdbe9 | 1485 | //be closed, a peer discards any further data received |
Sergunb | 0:8918a71cdbe9 | 1486 | error = webSocketReceive(webSocket, NULL, WEB_SOCKET_BUFFER_SIZE, NULL, 0); |
Sergunb | 0:8918a71cdbe9 | 1487 | |
Sergunb | 0:8918a71cdbe9 | 1488 | //Check status code |
Sergunb | 0:8918a71cdbe9 | 1489 | if(error == NO_ERROR) |
Sergunb | 0:8918a71cdbe9 | 1490 | { |
Sergunb | 0:8918a71cdbe9 | 1491 | //Close frame received? |
Sergunb | 0:8918a71cdbe9 | 1492 | if(webSocket->handshakeContext.closingFrameReceived) |
Sergunb | 0:8918a71cdbe9 | 1493 | { |
Sergunb | 0:8918a71cdbe9 | 1494 | //Properly shutdown the network connection |
Sergunb | 0:8918a71cdbe9 | 1495 | webSocket->state = WS_STATE_SHUTDOWN; |
Sergunb | 0:8918a71cdbe9 | 1496 | } |
Sergunb | 0:8918a71cdbe9 | 1497 | } |
Sergunb | 0:8918a71cdbe9 | 1498 | else if(error == ERROR_INVALID_FRAME || error == ERROR_END_OF_STREAM) |
Sergunb | 0:8918a71cdbe9 | 1499 | { |
Sergunb | 0:8918a71cdbe9 | 1500 | //Catch exception |
Sergunb | 0:8918a71cdbe9 | 1501 | error = NO_ERROR; |
Sergunb | 0:8918a71cdbe9 | 1502 | //Properly shutdown the network connection |
Sergunb | 0:8918a71cdbe9 | 1503 | webSocket->state = WS_STATE_SHUTDOWN; |
Sergunb | 0:8918a71cdbe9 | 1504 | } |
Sergunb | 0:8918a71cdbe9 | 1505 | } |
Sergunb | 0:8918a71cdbe9 | 1506 | else if(webSocket->state == WS_STATE_SHUTDOWN) |
Sergunb | 0:8918a71cdbe9 | 1507 | { |
Sergunb | 0:8918a71cdbe9 | 1508 | //Properly dispose the network connection |
Sergunb | 0:8918a71cdbe9 | 1509 | error = webSocketShutdownConnection(webSocket); |
Sergunb | 0:8918a71cdbe9 | 1510 | |
Sergunb | 0:8918a71cdbe9 | 1511 | //Check status code |
Sergunb | 0:8918a71cdbe9 | 1512 | if(!error) |
Sergunb | 0:8918a71cdbe9 | 1513 | { |
Sergunb | 0:8918a71cdbe9 | 1514 | //The connection has been properly closed |
Sergunb | 0:8918a71cdbe9 | 1515 | webSocket->state = WS_STATE_CLOSED; |
Sergunb | 0:8918a71cdbe9 | 1516 | } |
Sergunb | 0:8918a71cdbe9 | 1517 | } |
Sergunb | 0:8918a71cdbe9 | 1518 | else |
Sergunb | 0:8918a71cdbe9 | 1519 | { |
Sergunb | 0:8918a71cdbe9 | 1520 | //Invalid state |
Sergunb | 0:8918a71cdbe9 | 1521 | error = ERROR_WRONG_STATE; |
Sergunb | 0:8918a71cdbe9 | 1522 | } |
Sergunb | 0:8918a71cdbe9 | 1523 | |
Sergunb | 0:8918a71cdbe9 | 1524 | //Any error to report? |
Sergunb | 0:8918a71cdbe9 | 1525 | if(error) |
Sergunb | 0:8918a71cdbe9 | 1526 | break; |
Sergunb | 0:8918a71cdbe9 | 1527 | } |
Sergunb | 0:8918a71cdbe9 | 1528 | |
Sergunb | 0:8918a71cdbe9 | 1529 | //Return status code |
Sergunb | 0:8918a71cdbe9 | 1530 | return error; |
Sergunb | 0:8918a71cdbe9 | 1531 | } |
Sergunb | 0:8918a71cdbe9 | 1532 | |
Sergunb | 0:8918a71cdbe9 | 1533 | |
Sergunb | 0:8918a71cdbe9 | 1534 | /** |
Sergunb | 0:8918a71cdbe9 | 1535 | * @brief Close a WebSocket connection |
Sergunb | 0:8918a71cdbe9 | 1536 | * @param[in] webSocket Handle identifying the WebSocket to close |
Sergunb | 0:8918a71cdbe9 | 1537 | **/ |
Sergunb | 0:8918a71cdbe9 | 1538 | |
Sergunb | 0:8918a71cdbe9 | 1539 | void webSocketClose(WebSocket *webSocket) |
Sergunb | 0:8918a71cdbe9 | 1540 | { |
Sergunb | 0:8918a71cdbe9 | 1541 | //Make sure the WebSocket handle is valid |
Sergunb | 0:8918a71cdbe9 | 1542 | if(webSocket != NULL) |
Sergunb | 0:8918a71cdbe9 | 1543 | { |
Sergunb | 0:8918a71cdbe9 | 1544 | //Close connection |
Sergunb | 0:8918a71cdbe9 | 1545 | webSocketCloseConnection(webSocket); |
Sergunb | 0:8918a71cdbe9 | 1546 | //Release the WebSocket |
Sergunb | 0:8918a71cdbe9 | 1547 | webSocketChangeState(webSocket, WS_STATE_UNUSED); |
Sergunb | 0:8918a71cdbe9 | 1548 | } |
Sergunb | 0:8918a71cdbe9 | 1549 | } |
Sergunb | 0:8918a71cdbe9 | 1550 | |
Sergunb | 0:8918a71cdbe9 | 1551 | #endif |
Sergunb | 0:8918a71cdbe9 | 1552 |