web_socket_misc.c
Go to the documentation of this file.
1 /**
2  * @file web_socket_misc.c
3  * @brief Helper functions for WebSockets
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2024 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneTCP Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.4.4
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL WEB_SOCKET_TRACE_LEVEL
33 
34 //Dependencies
35 #include <stdlib.h>
36 #include "core/net.h"
37 #include "web_socket/web_socket.h"
42 #include "encoding/base64.h"
43 #include "hash/sha1.h"
44 #include "str.h"
45 #include "debug.h"
46 
47 //Check TCP/IP stack configuration
48 #if (WEB_SOCKET_SUPPORT == ENABLED)
49 
50 //WebSocket GUID
51 const char_t webSocketGuid[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
52 
53 
54 /**
55  * @brief HTTP status codes
56  **/
57 
58 static const WebSocketStatusCodeDesc statusCodeList[] =
59 {
60  //Success
61  {200, "OK"},
62  {201, "Created"},
63  {202, "Accepted"},
64  {204, "No Content"},
65  //Redirection
66  {301, "Moved Permanently"},
67  {302, "Found"},
68  {304, "Not Modified"},
69  //Client error
70  {400, "Bad Request"},
71  {401, "Unauthorized"},
72  {403, "Forbidden"},
73  {404, "Not Found"},
74  //Server error
75  {500, "Internal Server Error"},
76  {501, "Not Implemented"},
77  {502, "Bad Gateway"},
78  {503, "Service Unavailable"}
79 };
80 
81 
82 /**
83  * @brief Update WebSocket state
84  * @param[in] webSocket Handle to a WebSocket
85  * @param[in] newState New state to switch to
86  **/
87 
88 void webSocketChangeState(WebSocket *webSocket, WebSocketState newState)
89 {
90  //Switch to the new state
91  webSocket->state = newState;
92  //Save current time;
93  webSocket->timestamp = osGetSystemTime();
94 
95  //Reset sub-state
96  webSocket->txContext.state = WS_SUB_STATE_INIT;
97  webSocket->rxContext.state = WS_SUB_STATE_INIT;
98 }
99 
100 
101 /**
102  * @brief Parse client or server handshake
103  * @param[in] webSocket Handle to a WebSocket
104  * @return Error code
105  **/
106 
108 {
109  error_t error;
110  size_t n;
111  WebSocketFrameContext *rxContext;
112 
113  //Point to the RX context
114  rxContext = &webSocket->rxContext;
115 
116  //Initialize status code
117  error = NO_ERROR;
118 
119  //Wait for the handshake to complete
120  while(1)
121  {
122  //Client or server operation?
123  if(webSocket->endpoint == WS_ENDPOINT_CLIENT)
124  {
125  if(webSocket->state == WS_STATE_OPEN)
126  break;
127  }
128  else
129  {
130  if(webSocket->state == WS_STATE_SERVER_HANDSHAKE)
131  break;
132  }
133 
134  //Check current sub-state
135  if(rxContext->state == WS_SUB_STATE_INIT)
136  {
137  //Initialize status code
138  webSocket->statusCode = WS_STATUS_CODE_NO_STATUS_RCVD;
139 
140  //Initialize FIN flag
141  rxContext->fin = TRUE;
142 
143  //Flush the receive buffer
144  rxContext->bufferPos = 0;
145  rxContext->bufferLen = 0;
146 
147  //Initialize variables
148  webSocket->handshakeContext.statusCode = 0;
149  webSocket->handshakeContext.upgradeWebSocket = FALSE;
150  webSocket->handshakeContext.connectionUpgrade = FALSE;
151  webSocket->handshakeContext.connectionClose = FALSE;
152  webSocket->handshakeContext.contentLength = 0;
153  webSocket->handshakeContext.closingFrameSent = FALSE;
154  webSocket->handshakeContext.closingFrameReceived = FALSE;
155 
156 #if (WEB_SOCKET_BASIC_AUTH_SUPPORT == ENABLED || WEB_SOCKET_DIGEST_AUTH_SUPPORT == ENABLED)
157  webSocket->authContext.requiredAuthMode = WS_AUTH_MODE_NONE;
158 #endif
159 
160 #if (WEB_SOCKET_DIGEST_AUTH_SUPPORT == ENABLED)
161  osStrcpy(webSocket->authContext.nonce, "");
162  osStrcpy(webSocket->authContext.opaque, "");
163  webSocket->authContext.stale = FALSE;
164 #endif
165 
166  //Client or server operation?
167  if(webSocket->endpoint == WS_ENDPOINT_CLIENT)
168  {
169  //Clear server key
170  osStrcpy(webSocket->handshakeContext.serverKey, "");
171 
172  //Debug message
173  TRACE_DEBUG("WebSocket: server handshake\r\n");
174  }
175  else
176  {
177  //Clear client key
178  osStrcpy(webSocket->handshakeContext.clientKey, "");
179 
180  //Debug message
181  TRACE_DEBUG("WebSocket: client handshake\r\n");
182  }
183 
184  //Decode the leading line
186  }
187  else if(rxContext->state == WS_SUB_STATE_HANDSHAKE_LEADING_LINE)
188  {
189  //Check whether more data is required
190  if(rxContext->bufferLen < 2)
191  {
192  //Limit the number of characters to read at a time
193  n = WEB_SOCKET_BUFFER_SIZE - 1 - rxContext->bufferLen;
194 
195  //Read data until a CLRF character is encountered
196  error = webSocketReceiveData(webSocket, rxContext->buffer +
197  rxContext->bufferLen, n, &n, SOCKET_FLAG_BREAK_CRLF);
198 
199  //Update the length of the buffer
200  rxContext->bufferLen += n;
201  }
202  else if(rxContext->bufferLen >= (WEB_SOCKET_BUFFER_SIZE - 1))
203  {
204  //Report an error
205  error = ERROR_INVALID_REQUEST;
206  }
207  else if(osStrncmp((char_t *) rxContext->buffer + rxContext->bufferLen - 2,
208  "\r\n", 2) != 0)
209  {
210  //Limit the number of characters to read at a time
211  n = WEB_SOCKET_BUFFER_SIZE - 1 - rxContext->bufferLen;
212 
213  //Read data until a CLRF character is encountered
214  error = webSocketReceiveData(webSocket, rxContext->buffer +
215  rxContext->bufferLen, n, &n, SOCKET_FLAG_BREAK_CRLF);
216 
217  //Update the length of the buffer
218  rxContext->bufferLen += n;
219  }
220  else
221  {
222  //Properly terminate the string with a NULL character
223  rxContext->buffer[rxContext->bufferLen] = '\0';
224 
225  //Client or server operation?
226  if(webSocket->endpoint == WS_ENDPOINT_CLIENT)
227  {
228  //The leading line from the server follows the Status-Line format
229  error = webSocketParseStatusLine(webSocket, (char_t *) rxContext->buffer);
230  }
231  else
232  {
233  //The leading line from the client follows the Request-Line format
234  error = webSocketParseRequestLine(webSocket, (char_t *) rxContext->buffer);
235  }
236 
237  //Flush the receive buffer
238  rxContext->bufferPos = 0;
239  rxContext->bufferLen = 0;
240 
241  //Parse the header fields of the handshake
243  }
244  }
245  else if(rxContext->state == WS_SUB_STATE_HANDSHAKE_HEADER_FIELD)
246  {
247  //Check whether more data is required
248  if(rxContext->bufferLen < 2)
249  {
250  //Limit the number of characters to read at a time
251  n = WEB_SOCKET_BUFFER_SIZE - 1 - rxContext->bufferLen;
252 
253  //Read data until a CLRF character is encountered
254  error = webSocketReceiveData(webSocket, rxContext->buffer +
255  rxContext->bufferLen, n, &n, SOCKET_FLAG_BREAK_CRLF);
256 
257  //Update the length of the buffer
258  rxContext->bufferLen += n;
259  }
260  else if(rxContext->bufferLen >= (WEB_SOCKET_BUFFER_SIZE - 1))
261  {
262  //Report an error
263  error = ERROR_INVALID_REQUEST;
264  }
265  else if(osStrncmp((char_t *) rxContext->buffer + rxContext->bufferLen - 2,
266  "\r\n", 2) != 0)
267  {
268  //Limit the number of characters to read at a time
269  n = WEB_SOCKET_BUFFER_SIZE - 1 - rxContext->bufferLen;
270 
271  //Read data until a CLRF character is encountered
272  error = webSocketReceiveData(webSocket, rxContext->buffer +
273  rxContext->bufferLen, n, &n, SOCKET_FLAG_BREAK_CRLF);
274 
275  //Update the length of the buffer
276  rxContext->bufferLen += n;
277  }
278  else
279  {
280  //Properly terminate the string with a NULL character
281  rxContext->buffer[rxContext->bufferLen] = '\0';
282 
283  //An empty line indicates the end of the header fields
284  if(osStrcmp((char_t *) rxContext->buffer, "\r\n") == 0)
285  {
286  //Client or server operation?
287  if(webSocket->endpoint == WS_ENDPOINT_CLIENT)
288  {
289  //Verify server's handshake
290  error = webSocketVerifyServerHandshake(webSocket);
291  }
292  else
293  {
294  //Verify client's handshake
295  error = webSocketVerifyClientHandshake(webSocket);
296  }
297  }
298  else
299  {
300  //Read the next character to detect if the CRLF is immediately
301  //followed by a LWSP character
302  rxContext->state = WS_SUB_STATE_HANDSHAKE_LWSP;
303  }
304  }
305  }
306  else if(rxContext->state == WS_SUB_STATE_HANDSHAKE_LWSP)
307  {
308  char_t nextChar;
309 
310  //Read the next character
311  error = webSocketReceiveData(webSocket, &nextChar, sizeof(char_t), &n, 0);
312 
313  //Successful read operation?
314  if(!error && n == sizeof(char_t))
315  {
316  //LWSP character found?
317  if(nextChar == ' ' || nextChar == '\t')
318  {
319  //Unfolding is accomplished by regarding CRLF immediately
320  //followed by a LWSP as equivalent to the LWSP character
321  if(rxContext->bufferLen >= 2)
322  {
323  //Remove trailing CRLF sequence
324  rxContext->bufferLen -= 2;
325  }
326 
327  //The header field spans multiple line
329  }
330  else
331  {
332  //Parse header field
333  error = webSocketParseHeaderField(webSocket, (char_t *) rxContext->buffer);
334 
335  //Restore the very first character of the header field
336  rxContext->buffer[0] = nextChar;
337  //Adjust the length of the receive buffer
338  rxContext->bufferLen = sizeof(char_t);
339 
340  //Decode the next header field
342  }
343  }
344  }
345  else
346  {
347  //Invalid state
348  error = ERROR_WRONG_STATE;
349  }
350 
351  //Any error to report?
352  if(error)
353  break;
354  }
355 
356  //Return status code
357  return error;
358 }
359 
360 
361 /**
362  * @brief Parse the Request-Line of the client's handshake
363  * @param[in] webSocket Handle to a WebSocket
364  * @param[in] line NULL-terminated string that contains the Request-Line
365  * @return Error code
366  **/
367 
369 {
370  error_t error;
371  char_t *token;
372  char_t *p;
373  char_t *s;
374 
375  //Debug message
376  TRACE_DEBUG("%s", line);
377 
378  //The Request-Line begins with a method token
379  token = osStrtok_r(line, " \r\n", &p);
380  //Unable to retrieve the method?
381  if(token == NULL)
382  return ERROR_INVALID_REQUEST;
383 
384  //The method of the request must be GET
385  if(osStrcasecmp(token, "GET") != 0)
386  return ERROR_INVALID_REQUEST;
387 
388  //The Request-URI is following the method token
389  token = osStrtok_r(NULL, " \r\n", &p);
390  //Unable to retrieve the Request-URI?
391  if(token == NULL)
392  return ERROR_INVALID_REQUEST;
393 
394  //Check whether a query string is present
395  s = osStrchr(token, '?');
396 
397  //Query string found?
398  if(s != NULL)
399  {
400  //Split the string
401  *s = '\0';
402 
403  //Save the Request-URI
405  webSocket->uri, WEB_SOCKET_URI_MAX_LEN);
406  //Any error to report?
407  if(error)
408  return ERROR_INVALID_REQUEST;
409 
410  //Check the length of the query string
412  return ERROR_INVALID_REQUEST;
413 
414  //Save the query string
415  osStrcpy(webSocket->queryString, s + 1);
416  }
417  else
418  {
419  //Save the Request-URI
421  webSocket->uri, WEB_SOCKET_URI_MAX_LEN);
422  //Any error to report?
423  if(error)
424  return ERROR_INVALID_REQUEST;
425 
426  //No query string
427  webSocket->queryString[0] = '\0';
428  }
429 
430  //The protocol version is following the Request-URI
431  token = osStrtok_r(NULL, " \r\n", &p);
432 
433  //HTTP version 0.9?
434  if(token == NULL)
435  {
436  //Save version number
437  webSocket->handshakeContext.version = WS_HTTP_VERSION_0_9;
438  //Persistent connections are not supported
439  webSocket->handshakeContext.connectionClose = TRUE;
440  }
441  //HTTP version 1.0?
442  else if(osStrcasecmp(token, "HTTP/1.0") == 0)
443  {
444  //Save version number
445  webSocket->handshakeContext.version = WS_HTTP_VERSION_1_0;
446  //By default connections are not persistent
447  webSocket->handshakeContext.connectionClose = TRUE;
448  }
449  //HTTP version 1.1?
450  else if(osStrcasecmp(token, "HTTP/1.1") == 0)
451  {
452  //Save version number
453  webSocket->handshakeContext.version = WS_HTTP_VERSION_1_1;
454  //HTTP 1.1 makes persistent connections the default
455  webSocket->handshakeContext.connectionClose = FALSE;
456  }
457  //HTTP version not supported?
458  else
459  {
460  //Report an error
461  return ERROR_INVALID_REQUEST;
462  }
463 
464  //Successful processing
465  return NO_ERROR;
466 }
467 
468 
469 /**
470  * @brief Parse the Status-Line of the server's handshake
471  * @param[in] webSocket Handle to a WebSocket
472  * @param[in] line NULL-terminated string that contains the Status-Line
473  * @return Error code
474  **/
475 
477 {
478  char_t *p;
479  char_t *token;
480 
481  //Debug message
482  TRACE_DEBUG("%s", line);
483 
484  //Retrieve the HTTP-Version field
485  token = osStrtok_r(line, " ", &p);
486  //Any parsing error?
487  if(token == NULL)
488  return ERROR_INVALID_SYNTAX;
489 
490  //Retrieve the Status-Code field
491  token = osStrtok_r(NULL, " ", &p);
492  //Any parsing error?
493  if(token == NULL)
494  return ERROR_INVALID_SYNTAX;
495 
496  //Convert the status code
497  webSocket->handshakeContext.statusCode = osStrtoul(token, &p, 10);
498  //Any parsing error?
499  if(*p != '\0')
500  return ERROR_INVALID_SYNTAX;
501 
502  //Successful processing
503  return NO_ERROR;
504 }
505 
506 
507 /**
508  * @brief Parse a header field
509  * @param[in] webSocket Handle to a WebSocket
510  * @param[in] line NULL-terminated string that contains the header field
511  * @return Error code
512  **/
513 
515 {
516  char_t *separator;
517  char_t *name;
518  char_t *value;
519  WebSocketHandshakeContext *handshakeContext;
520 
521  //Point to the handshake context
522  handshakeContext = &webSocket->handshakeContext;
523 
524  //Debug message
525  TRACE_DEBUG("%s", line);
526 
527  //Check whether a separator is present
528  separator = osStrchr(line, ':');
529 
530  //Separator found?
531  if(separator != NULL)
532  {
533  //Split the line
534  *separator = '\0';
535 
536  //Get field name and value
537  name = strTrimWhitespace(line);
538  value = strTrimWhitespace(separator + 1);
539 
540  //Upgrade header field found?
541  if(osStrcasecmp(name, "Upgrade") == 0)
542  {
543  if(osStrcasecmp(value, "websocket") == 0)
544  handshakeContext->upgradeWebSocket = TRUE;
545 
546  }
547  //Connection header field found?
548  else if(osStrcasecmp(name, "Connection") == 0)
549  {
550  //Parse Connection header field
552  }
553  //Sec-WebSocket-Key header field found?
554  else if(osStrcasecmp(name, "Sec-WebSocket-Key") == 0)
555  {
556  //Server operation?
557  if(webSocket->endpoint == WS_ENDPOINT_SERVER)
558  {
559  //Save the contents of the Sec-WebSocket-Key header field
560  strSafeCopy(handshakeContext->clientKey, value,
562  }
563  }
564  //Sec-WebSocket-Accept header field found?
565  else if(osStrcasecmp(name, "Sec-WebSocket-Accept") == 0)
566  {
567  //Client operation?
568  if(webSocket->endpoint == WS_ENDPOINT_CLIENT)
569  {
570  //Save the contents of the Sec-WebSocket-Accept header field
571  strSafeCopy(handshakeContext->serverKey, value,
573  }
574  }
575 #if (WEB_SOCKET_BASIC_AUTH_SUPPORT == ENABLED || WEB_SOCKET_DIGEST_AUTH_SUPPORT == ENABLED)
576  //WWW-Authenticate header field found?
577  else if(osStrcasecmp(name, "WWW-Authenticate") == 0)
578  {
579  //Parse WWW-Authenticate header field
581  }
582 #endif
583  //Content-Length header field found?
584  else if(osStrcasecmp(name, "Content-Length") == 0)
585  {
586  handshakeContext->contentLength = osStrtoul(value, NULL, 10);
587  }
588  }
589 
590  //Successful processing
591  return NO_ERROR;
592 }
593 
594 
595 /**
596  * @brief Parse Connection header field
597  * @param[in] webSocket Handle to a WebSocket
598  * @param[in] value NULL-terminated string that contains the value of header field
599  **/
600 
602 {
603  char_t *p;
604  char_t *token;
605 
606  //Get the first value of the list
607  token = osStrtok_r(value, ",", &p);
608 
609  //Parse the comma-separated list
610  while(token != NULL)
611  {
612  //Trim whitespace characters
614 
615  //Check current value
616  if(osStrcasecmp(value, "keep-alive") == 0)
617  {
618  //The connection is persistent
619  webSocket->handshakeContext.connectionClose = FALSE;
620  }
621  else if(osStrcasecmp(value, "close") == 0)
622  {
623  //The connection will be closed after completion of the response
624  webSocket->handshakeContext.connectionClose = TRUE;
625  }
626  else if(osStrcasecmp(value, "upgrade") == 0)
627  {
628  //Upgrade the connection
629  webSocket->handshakeContext.connectionUpgrade = TRUE;
630  }
631 
632  //Get next value
633  token = osStrtok_r(NULL, ",", &p);
634  }
635 }
636 
637 
638 /**
639  * @brief Format client's handshake
640  * @param[in] webSocket Handle to a WebSocket
641  * @param[in] serverPort TCP port number used to establish the connection
642  * @return Error code
643  **/
644 
645 error_t webSocketFormatClientHandshake(WebSocket *webSocket, uint16_t serverPort)
646 {
647  char_t *p;
648  WebSocketFrameContext *txContext;
649 
650  //Point to the TX context
651  txContext = &webSocket->txContext;
652  //Point to the buffer where to format the client's handshake
653  p = (char_t *) txContext->buffer;
654 
655  //The Request-Line begins with a method token, followed by the
656  //Request-URI and the protocol version, and ending with CRLF
657  p += osSprintf(p, "GET %s HTTP/1.1\r\n", webSocket->uri);
658 
659  //Add Host header field
660  if(webSocket->host[0] != '\0')
661  {
662  //The Host header field specifies the Internet host and port number of
663  //the resource being requested
664  p += osSprintf(p, "Host: %s:%d\r\n", webSocket->host, serverPort);
665  }
666  else
667  {
668  //If the requested URI does not include a host name for the service being
669  //requested, then the Host header field must be given with an empty value
670  p += osSprintf(p, "Host:\r\n");
671  }
672 
673 #if (WEB_SOCKET_BASIC_AUTH_SUPPORT == ENABLED || WEB_SOCKET_DIGEST_AUTH_SUPPORT == ENABLED)
674  //Check whether authentication is required
675  if(webSocket->authContext.selectedAuthMode != WS_AUTH_MODE_NONE)
676  {
677  //Add Authorization header field
678  p += webSocketAddAuthorizationField(webSocket, p);
679  }
680 #endif
681 
682  //Add Origin header field
683  if(webSocket->origin[0] != '\0')
684  {
685  p += osSprintf(p, "Origin: %s\r\n", webSocket->origin);
686  }
687  else
688  {
689  p += osSprintf(p, "Origin: null\r\n");
690  }
691 
692  //Add Upgrade header field
693  p += osSprintf(p, "Upgrade: websocket\r\n");
694  //Add Connection header field
695  p += osSprintf(p, "Connection: Upgrade\r\n");
696 
697  //Add Sec-WebSocket-Protocol header field
698  if(webSocket->subProtocol[0] != '\0')
699  p += osSprintf(p, "Sec-WebSocket-Protocol: %s\r\n", webSocket->subProtocol);
700 
701  //Add Sec-WebSocket-Key header field
702  p += osSprintf(p, "Sec-WebSocket-Key: %s\r\n",
703  webSocket->handshakeContext.clientKey);
704 
705  //Add Sec-WebSocket-Version header field
706  p += osSprintf(p, "Sec-WebSocket-Version: 13\r\n");
707  //An empty line indicates the end of the header fields
708  p += osSprintf(p, "\r\n");
709 
710  //Debug message
711  TRACE_DEBUG("\r\n");
712  TRACE_DEBUG("WebSocket: client handshake\r\n");
713  TRACE_DEBUG("%s", txContext->buffer);
714 
715  //Rewind to the beginning of the buffer
716  txContext->bufferPos = 0;
717  //Update the number of data buffered but not yet sent
718  txContext->bufferLen = osStrlen((char_t *) txContext->buffer);
719 
720  //Successful processing
721  return NO_ERROR;
722 }
723 
724 
725 /**
726  * @brief Format server's handshake
727  * @param[in] webSocket Handle to a WebSocket
728  * @return Error code
729  **/
730 
732 {
733  char_t *p;
734  WebSocketFrameContext *txContext;
735 
736  //Point to the TX context
737  txContext = &webSocket->txContext;
738  //Point to the buffer where to format the client's handshake
739  p = (char_t *) txContext->buffer;
740 
741  //The first line is an HTTP Status-Line, with the status code 101
742  p += osSprintf(p, "HTTP/1.1 101 Switching Protocols\r\n");
743 
744  //Add Upgrade header field
745  p += osSprintf(p, "Upgrade: websocket\r\n");
746  //Add Connection header field
747  p += osSprintf(p, "Connection: Upgrade\r\n");
748 
749  //Add Sec-WebSocket-Protocol header field
750  if(webSocket->subProtocol[0] != '\0')
751  p += osSprintf(p, "Sec-WebSocket-Protocol: %s\r\n", webSocket->subProtocol);
752 
753  //Add Sec-WebSocket-Accept header field
754  p += osSprintf(p, "Sec-WebSocket-Accept: %s\r\n",
755  webSocket->handshakeContext.serverKey);
756 
757  //An empty line indicates the end of the header fields
758  p += osSprintf(p, "\r\n");
759 
760  //Debug message
761  TRACE_DEBUG("\r\n");
762  TRACE_DEBUG("WebSocket: server handshake\r\n");
763  TRACE_DEBUG("%s", txContext->buffer);
764 
765  //Rewind to the beginning of the buffer
766  txContext->bufferPos = 0;
767  //Update the number of data buffered but not yet sent
768  txContext->bufferLen = osStrlen((char_t *) txContext->buffer);
769 
770  //Successful processing
771  return NO_ERROR;
772 }
773 
774 
775 /**
776  * @brief Format HTTP error response
777  * @param[in] webSocket Handle to a WebSocket
778  * @param[in] statusCode HTTP status code
779  * @param[in] message User message
780  * @return Error code
781  **/
782 
784  uint_t statusCode, const char_t *message)
785 {
786  uint_t i;
787  size_t length;
788  char_t *p;
789  WebSocketFrameContext *txContext;
790 
791  //HTML response template
792  static const char_t template[] =
793  "<!doctype html>\r\n"
794  "<html>\r\n"
795  "<head><title>Error %03d</title></head>\r\n"
796  "<body>\r\n"
797  "<h2>Error %03d</h2>\r\n"
798  "<p>%s</p>\r\n"
799  "</body>\r\n"
800  "</html>\r\n";
801 
802  //Point to the TX context
803  txContext = &webSocket->txContext;
804  //Point to the buffer where to format the client's handshake
805  p = (char_t *) txContext->buffer;
806 
807  //The first line of a response message is the Status-Line, consisting
808  //of the protocol version followed by a numeric status code and its
809  //associated textual phrase
810  p += osSprintf(p, "HTTP/%u.%u %u ", MSB(webSocket->handshakeContext.version),
811  LSB(webSocket->handshakeContext.version), statusCode);
812 
813  //Retrieve the Reason-Phrase that corresponds to the Status-Code
814  for(i = 0; i < arraysize(statusCodeList); i++)
815  {
816  //Check the status code
817  if(statusCodeList[i].value == statusCode)
818  {
819  //Append the textual phrase to the Status-Line
820  p += osSprintf(p, "%s", statusCodeList[i].message);
821  //Break the loop and continue processing
822  break;
823  }
824  }
825 
826  //Properly terminate the Status-Line
827  p += osSprintf(p, "\r\n");
828 
829  //Content type
830  p += osSprintf(p, "Content-Type: %s\r\n", "text/html");
831 
832  //Compute the length of the response
833  length = osStrlen(template) + osStrlen(message) - 4;
834  //Set Content-Length field
835  p += osSprintf(p, "Content-Length: %" PRIuSIZE "\r\n", length);
836 
837  //Terminate the header with an empty line
838  p += osSprintf(p, "\r\n");
839 
840  //Format HTML response
841  p += osSprintf(p, template, statusCode, statusCode, message);
842 
843  //Rewind to the beginning of the buffer
844  txContext->bufferPos = 0;
845  //Update the number of data buffered but not yet sent
846  txContext->bufferLen = osStrlen((char_t *) txContext->buffer);
847 
848  //Successful processing
849  return NO_ERROR;
850 }
851 
852 
853 /**
854  * @brief Verify client's handshake
855  * @param[in] webSocket Handle to a WebSocket
856  * @return Error code
857  **/
858 
860 {
861  error_t error;
862  WebSocketHandshakeContext *handshakeContext;
863 
864  //Debug message
865  TRACE_DEBUG("WebSocket: verifying client handshake\r\n");
866 
867  //Point to the handshake context
868  handshakeContext = &webSocket->handshakeContext;
869 
870  //The HTTP version must be at least 1.1
871  if(handshakeContext->version < WS_HTTP_VERSION_1_1)
872  return ERROR_INVALID_REQUEST;
873 
874  //The request must contain an Upgrade header field whose value
875  //must include the "websocket" keyword
876  if(!handshakeContext->upgradeWebSocket)
877  return ERROR_INVALID_REQUEST;
878 
879  //The request must contain a Connection header field whose value
880  //must include the "Upgrade" token
881  if(!handshakeContext->connectionUpgrade)
882  return ERROR_INVALID_REQUEST;
883 
884  //The request must include a header field with the name Sec-WebSocket-Key
885  if(handshakeContext->clientKey[0] == 0)
886  return ERROR_INVALID_REQUEST;
887 
888  //Check the Sec-WebSocket-Key header field
889  error = webSocketVerifyClientKey(webSocket);
890  //Verification failed?
891  if(error)
892  return error;
893 
894  //Generate the server part of the handshake
896 
897  //Successful processing
898  return NO_ERROR;
899 }
900 
901 
902 /**
903  * @brief Verify server's handshake
904  * @param[in] webSocket Handle to a WebSocket
905  * @return Error code
906  **/
907 
909 {
910  error_t error;
911  WebSocketHandshakeContext *handshakeContext;
912 
913  //Debug message
914  TRACE_DEBUG("WebSocket: verifying server handshake\r\n");
915 
916  //Point to the handshake context
917  handshakeContext = &webSocket->handshakeContext;
918 
919  //If the status code received from the server is not 101, the client
920  //handles the response per HTTP procedures
921  if(handshakeContext->statusCode == 401)
922  {
923  //Authorization required
924  return ERROR_AUTH_REQUIRED;
925  }
926  else if(handshakeContext->statusCode != 101)
927  {
928  //Unknown status code
929  return ERROR_INVALID_STATUS;
930  }
931 
932  //If the response lacks an Upgrade header field or the Upgrade header field
933  //contains a value that is not an ASCII case-insensitive match for the
934  //value "websocket", the client must fail the WebSocket connection
935  if(!handshakeContext->upgradeWebSocket)
936  return ERROR_INVALID_SYNTAX;
937 
938  //If the response lacks a Connection header field or the Connection header
939  //field doesn't contain a token that is an ASCII case-insensitive match for
940  //the value "Upgrade", the client must fail the WebSocket connection
941  if(!handshakeContext->connectionUpgrade)
942  return ERROR_INVALID_SYNTAX;
943 
944  //If the response lacks a Sec-WebSocket-Accept header field, the client
945  //must fail the WebSocket connection
946  if(osStrlen(handshakeContext->serverKey) == 0)
947  return ERROR_INVALID_SYNTAX;
948 
949  //Check the Sec-WebSocket-Accept header field
950  error = webSocketVerifyServerKey(webSocket);
951  //Verification failed?
952  if(error)
953  return error;
954 
955  //If the server's response is validated as provided for above, it is
956  //said that the WebSocket connection is established and that the
957  //WebSocket connection is in the OPEN state
959 
960  //Successful processing
961  return NO_ERROR;
962 }
963 
964 
965 /**
966  * @brief Generate client's key
967  * @param[in] webSocket Handle to a WebSocket
968  * @return Error code
969  **/
970 
972 {
973  error_t error;
974  size_t n;
975  uint8_t nonce[16];
976  WebSocketHandshakeContext *handshakeContext;
977 
978  //Debug message
979  TRACE_DEBUG("WebSocket: Generating client's key...\r\n");
980 
981  //Point to the handshake context
982  handshakeContext = &webSocket->handshakeContext;
983 
984  //Make sure that the RNG callback function has been registered
985  if(webSockRandCallback == NULL)
986  {
987  //A cryptographically strong random number generator
988  //must be used to generate the nonce
989  return ERROR_PRNG_NOT_READY;
990  }
991 
992  //A nonce must be selected randomly for each connection
993  error = webSockRandCallback(nonce, sizeof(nonce));
994  //Any error to report?
995  if(error)
996  return error;
997 
998  //Encode the client's key
999  base64Encode(nonce, sizeof(nonce), handshakeContext->clientKey, &n);
1000 
1001  //Debug message
1002  TRACE_DEBUG(" Client key: %s\r\n", handshakeContext->clientKey);
1003 
1004  //Successful processing
1005  return NO_ERROR;
1006 }
1007 
1008 
1009 /**
1010  * @brief Generate server's key
1011  * @param[in] webSocket Handle to a WebSocket
1012  * @return Error code
1013  **/
1014 
1016 {
1017  size_t n;
1018  WebSocketHandshakeContext *handshakeContext;
1019  Sha1Context sha1Context;
1020 
1021  //Debug message
1022  TRACE_DEBUG("WebSocket: Generating server's key...\r\n");
1023 
1024  //Point to the handshake context
1025  handshakeContext = &webSocket->handshakeContext;
1026 
1027  //Retrieve the length of the client key
1028  n = osStrlen(handshakeContext->clientKey);
1029 
1030  //Concatenate the Sec-WebSocket-Key with the GUID string and digest
1031  //the resulting string using SHA-1
1032  sha1Init(&sha1Context);
1033  sha1Update(&sha1Context, handshakeContext->clientKey, n);
1035  sha1Final(&sha1Context, NULL);
1036 
1037  //Encode the result using Base64
1038  base64Encode(sha1Context.digest, SHA1_DIGEST_SIZE,
1039  handshakeContext->serverKey, &n);
1040 
1041  //Debug message
1042  TRACE_DEBUG(" Server key: %s\r\n", handshakeContext->serverKey);
1043 
1044  //Successful processing
1045  return NO_ERROR;
1046 }
1047 
1048 
1049 /**
1050  * @brief Verify client's key
1051  * @param[in] webSocket Handle to a WebSocket
1052  * @return Error code
1053  **/
1054 
1056 {
1057  error_t error;
1058  size_t n;
1059  char_t *buffer;
1060  WebSocketHandshakeContext *handshakeContext;
1061 
1062  //Debug message
1063  TRACE_DEBUG("WebSocket: Verifying client's key...\r\n");
1064 
1065  //Temporary buffer
1066  buffer = (char_t *) webSocket->txContext.buffer;
1067 
1068  //Point to the handshake context
1069  handshakeContext = &webSocket->handshakeContext;
1070 
1071  //Retrieve the length of the client's key
1072  n = osStrlen(handshakeContext->clientKey);
1073 
1074  //The value of the Sec-WebSocket-Key header field must be a 16-byte
1075  //value that has been Base64-encoded
1076  error = base64Decode(handshakeContext->clientKey, n, buffer, &n);
1077  //Decoding failed?
1078  if(error)
1079  return ERROR_INVALID_KEY;
1080 
1081  //Check the length of the resulting value
1082  if(n != 16)
1083  return ERROR_INVALID_KEY;
1084 
1085  //Successful verification
1086  return NO_ERROR;
1087 }
1088 
1089 
1090 /**
1091  * @brief Verify server's key
1092  * @param[in] webSocket Handle to a WebSocket
1093  * @return Error code
1094  **/
1095 
1097 {
1098  size_t n;
1099  char_t *buffer;
1100  WebSocketHandshakeContext *handshakeContext;
1101  Sha1Context sha1Context;
1102 
1103  //Debug message
1104  TRACE_DEBUG("WebSocket: Verifying server's key...\r\n");
1105 
1106  //Temporary buffer
1107  buffer = (char_t *) webSocket->txContext.buffer;
1108 
1109  //Point to the handshake context
1110  handshakeContext = &webSocket->handshakeContext;
1111 
1112  //Retrieve the length of the client's key
1113  n = osStrlen(handshakeContext->clientKey);
1114 
1115  //Concatenate the Sec-WebSocket-Key with the GUID string and digest
1116  //the resulting string using SHA-1
1117  sha1Init(&sha1Context);
1118  sha1Update(&sha1Context, handshakeContext->clientKey, n);
1120  sha1Final(&sha1Context, NULL);
1121 
1122  //Encode the result using Base64
1123  base64Encode(sha1Context.digest, SHA1_DIGEST_SIZE, buffer, &n);
1124 
1125  //Debug message
1126  TRACE_DEBUG(" Client key: %s\r\n", handshakeContext->clientKey);
1127  TRACE_DEBUG(" Server key: %s\r\n", handshakeContext->serverKey);
1128  TRACE_DEBUG(" Calculated key: %s\r\n", webSocket->txContext.buffer);
1129 
1130  //Check whether the server's key is valid
1131  if(osStrcmp(handshakeContext->serverKey, buffer) != 0)
1132  return ERROR_INVALID_KEY;
1133 
1134  //Successful verification
1135  return NO_ERROR;
1136 }
1137 
1138 
1139 /**
1140  * @brief Check whether a status code is valid
1141  * @param[in] statusCode Status code
1142  * @return The function returns TRUE is the specified status code is
1143  * valid. Otherwise, FALSE is returned
1144  **/
1145 
1146 bool_t webSocketCheckStatusCode(uint16_t statusCode)
1147 {
1148  bool_t valid;
1149 
1150  //Check status code
1151  if(statusCode == WS_STATUS_CODE_NORMAL_CLOSURE ||
1152  statusCode == WS_STATUS_CODE_GOING_AWAY ||
1153  statusCode == WS_STATUS_CODE_PROTOCOL_ERROR ||
1154  statusCode == WS_STATUS_CODE_UNSUPPORTED_DATA ||
1155  statusCode == WS_STATUS_CODE_INVALID_PAYLOAD_DATA ||
1156  statusCode == WS_STATUS_CODE_POLICY_VIOLATION ||
1157  statusCode == WS_STATUS_CODE_MESSAGE_TOO_BIG ||
1158  statusCode == WS_STATUS_CODE_MANDATORY_EXT ||
1159  statusCode == WS_STATUS_CODE_INTERNAL_ERROR)
1160  {
1161  valid = TRUE;
1162  }
1163  else if(statusCode >= 3000)
1164  {
1165  valid = TRUE;
1166  }
1167  else
1168  {
1169  valid = FALSE;
1170  }
1171 
1172  //The function returns TRUE is the specified status code is valid
1173  return valid;
1174 }
1175 
1176 
1177 /**
1178  * @brief Decode a percent-encoded string
1179  * @param[in] input NULL-terminated string to be decoded
1180  * @param[out] output NULL-terminated string resulting from the decoding process
1181  * @param[in] outputSize Size of the output buffer in bytes
1182  * @return Error code
1183  **/
1184 
1186  char_t *output, size_t outputSize)
1187 {
1188  size_t i;
1189  char_t buffer[3];
1190 
1191  //Check parameters
1192  if(input == NULL || output == NULL)
1193  return ERROR_INVALID_PARAMETER;
1194 
1195  //Decode the percent-encoded string
1196  for(i = 0; *input != '\0' && i < outputSize; i++)
1197  {
1198  //Check current character
1199  if(*input == '+')
1200  {
1201  //Replace '+' characters with spaces
1202  output[i] = ' ';
1203  //Advance data pointer
1204  input++;
1205  }
1206  else if(input[0] == '%' && input[1] != '\0' && input[2] != '\0')
1207  {
1208  //Process percent-encoded characters
1209  buffer[0] = input[1];
1210  buffer[1] = input[2];
1211  buffer[2] = '\0';
1212  //String to integer conversion
1213  output[i] = (uint8_t) osStrtoul(buffer, NULL, 16);
1214  //Advance data pointer
1215  input += 3;
1216  }
1217  else
1218  {
1219  //Copy any other characters
1220  output[i] = *input;
1221  //Advance data pointer
1222  input++;
1223  }
1224  }
1225 
1226  //Check whether the output buffer runs out of space
1227  if(i >= outputSize)
1228  return ERROR_FAILURE;
1229 
1230  //Properly terminate the resulting string
1231  output[i] = '\0';
1232  //Successful processing
1233  return NO_ERROR;
1234 }
1235 
1236 
1237 /**
1238  * @brief Check whether a an UTF-8 stream is valid
1239  * @param[in] context UTF-8 decoding context
1240  * @param[in] data Pointer to the chunk of data to be processed
1241  * @param[in] length Data chunk length
1242  * @param[in] remaining number of remaining bytes in the UTF-8 stream
1243  * @return The function returns TRUE is the specified UTF-8 stream is
1244  * valid. Otherwise, FALSE is returned
1245  **/
1246 
1248  const uint8_t *data, size_t length, size_t remaining)
1249 {
1250  size_t i;
1251  bool_t valid;
1252 
1253  //Initialize flag
1254  valid = TRUE;
1255 
1256  //Interpret the byte stream as UTF-8
1257  for(i = 0; i < length && valid; i++)
1258  {
1259  //Leading or continuation byte?
1260  if(context->utf8CharIndex == 0)
1261  {
1262  //7-bit code point?
1263  if((data[i] & 0x80) == 0x00)
1264  {
1265  //The code point consist of a single byte
1266  context->utf8CharSize = 1;
1267  //Decode the first byte of the sequence
1268  context->utf8CodePoint = data[i] & 0x7F;
1269  }
1270  //11-bit code point?
1271  else if((data[i] & 0xE0) == 0xC0)
1272  {
1273  //The code point consist of a 2 bytes
1274  context->utf8CharSize = 2;
1275  //Decode the first byte of the sequence
1276  context->utf8CodePoint = (data[i] & 0x1F) << 6;
1277  }
1278  //16-bit code point?
1279  else if((data[i] & 0xF0) == 0xE0)
1280  {
1281  //The code point consist of a 3 bytes
1282  context->utf8CharSize = 3;
1283  //Decode the first byte of the sequence
1284  context->utf8CodePoint = (data[i] & 0x0F) << 12;
1285  }
1286  //21-bit code point?
1287  else if((data[i] & 0xF8) == 0xF0)
1288  {
1289  //The code point consist of a 3 bytes
1290  context->utf8CharSize = 4;
1291  //Decode the first byte of the sequence
1292  context->utf8CodePoint = (data[i] & 0x07) << 18;
1293  }
1294  else
1295  {
1296  //The UTF-8 stream is not valid
1297  valid = FALSE;
1298  }
1299 
1300  //This test only applies to frames that are not fragmented
1301  if(length <= remaining)
1302  {
1303  //Make sure the UTF-8 stream is properly terminated
1304  if((i + context->utf8CharSize) > remaining)
1305  {
1306  //The UTF-8 stream is not valid
1307  valid = FALSE;
1308  }
1309  }
1310 
1311  //Decode the next byte of the sequence
1312  context->utf8CharIndex = context->utf8CharSize - 1;
1313  }
1314  else
1315  {
1316  //Continuation bytes all have 10 in the high-order position
1317  if((data[i] & 0xC0) == 0x80)
1318  {
1319  //Decode the multi-byte sequence
1320  context->utf8CharIndex--;
1321  //All continuation bytes contain exactly 6 bits from the code point
1322  context->utf8CodePoint |= (data[i] & 0x3F) << (context->utf8CharIndex * 6);
1323 
1324  //The correct encoding of a code point use only the minimum number
1325  //of bytes required to hold the significant bits of the code point
1326  if(context->utf8CharSize == 2)
1327  {
1328  //Overlong encoding is not supported
1329  if((context->utf8CodePoint & ~0x7F) == 0)
1330  valid = FALSE;
1331  }
1332  if(context->utf8CharSize == 3 && context->utf8CharIndex < 2)
1333  {
1334  //Overlong encoding is not supported
1335  if((context->utf8CodePoint & ~0x7FF) == 0)
1336  valid = FALSE;
1337  }
1338  if(context->utf8CharSize == 4 && context->utf8CharIndex < 3)
1339  {
1340  //Overlong encoding is not supported
1341  if((context->utf8CodePoint & ~0xFFFF) == 0)
1342  valid = FALSE;
1343  }
1344 
1345  //According to the UTF-8 definition (RFC 3629) the high and low
1346  //surrogate halves used by UTF-16 (U+D800 through U+DFFF) are not
1347  //legal Unicode values, and their UTF-8 encoding should be treated
1348  //as an invalid byte sequence
1349  if(context->utf8CodePoint >= 0xD800 && context->utf8CodePoint < 0xE000)
1350  valid = FALSE;
1351 
1352  //Code points greater than U+10FFFF are not valid
1353  if(context->utf8CodePoint >= 0x110000)
1354  valid = FALSE;
1355  }
1356  else
1357  {
1358  //The start byte is not followed by enough continuation bytes
1359  valid = FALSE;
1360  }
1361  }
1362  }
1363 
1364  //The function returns TRUE is the specified UTF-8 stream is valid
1365  return valid;
1366 }
1367 
1368 #endif
#define WEB_SOCKET_BUFFER_SIZE
Definition: web_socket.h:82
@ WS_STATUS_CODE_GOING_AWAY
Definition: web_socket.h:280
@ WS_STATUS_CODE_UNSUPPORTED_DATA
Definition: web_socket.h:282
#define osStrchr(s, c)
Definition: os_port.h:195
String manipulation helper functions.
HTTP status code.
int bool_t
Definition: compiler_port.h:53
@ WS_AUTH_MODE_NONE
Definition: web_socket.h:214
void sha1Update(Sha1Context *context, const void *data, size_t length)
Update the SHA-1 context with a portion of the message being hashed.
#define WEB_SOCKET_CLIENT_KEY_SIZE
Definition: web_socket.h:171
SHA-1 (Secure Hash Algorithm 1)
uint8_t p
Definition: ndp.h:300
char_t * strTrimWhitespace(char_t *s)
Removes all leading and trailing whitespace from a string.
Definition: str.c:78
error_t webSocketFormatErrorResponse(WebSocket *webSocket, uint_t statusCode, const char_t *message)
Format HTTP error response.
WebSocket API (client and server)
@ WS_HTTP_VERSION_1_0
Definition: web_socket.h:203
uint8_t message[]
Definition: chap.h:154
HTTP authentication for WebSockets.
#define TRUE
Definition: os_port.h:50
bool_t webSocketCheckUtf8Stream(WebSocketUtf8Context *context, const uint8_t *data, size_t length, size_t remaining)
Check whether a an UTF-8 stream is valid.
uint8_t data[]
Definition: ethernet.h:222
void base64Encode(const void *input, size_t inputLen, char_t *output, size_t *outputLen)
Base64 encoding algorithm.
Definition: base64.c:142
Helper functions for WebSockets.
@ WS_STATUS_CODE_NO_STATUS_RCVD
Definition: web_socket.h:283
@ ERROR_INVALID_STATUS
Definition: error.h:102
error_t webSocketParseHeaderField(WebSocket *webSocket, char_t *line)
Parse a header field.
char_t name[]
#define osStrcmp(s1, s2)
Definition: os_port.h:171
@ ERROR_AUTH_REQUIRED
Definition: error.h:150
#define osStrlen(s)
Definition: os_port.h:165
@ ERROR_PRNG_NOT_READY
Definition: error.h:251
@ WS_HTTP_VERSION_0_9
Definition: web_socket.h:202
void webSocketParseConnectionField(WebSocket *webSocket, char_t *value)
Parse Connection header field.
WebSocket frame parsing and formatting.
const char_t webSocketGuid[]
@ ERROR_WRONG_STATE
Definition: error.h:209
@ WS_SUB_STATE_HANDSHAKE_HEADER_FIELD
Definition: web_socket.h:249
error_t base64Decode(const char_t *input, size_t inputLen, void *output, size_t *outputLen)
Base64 decoding algorithm.
Definition: base64.c:258
#define FALSE
Definition: os_port.h:46
char_t serverKey[WEB_SOCKET_SERVER_KEY_SIZE+1]
Definition: web_socket.h:388
@ ERROR_INVALID_PARAMETER
Invalid parameter.
Definition: error.h:47
void sha1Init(Sha1Context *context)
Initialize SHA-1 message digest context.
size_t bufferLen
Length of the data buffer.
Definition: web_socket.h:409
error_t
Error codes.
Definition: error.h:43
Frame encoding/decoding context.
Definition: web_socket.h:399
#define osSprintf(dest,...)
Definition: os_port.h:231
#define osStrtok_r(s, delim, last)
Definition: os_port.h:225
error_t webSocketGenerateClientKey(WebSocket *webSocket)
Generate client's key.
bool_t webSocketCheckStatusCode(uint16_t statusCode)
Check whether a status code is valid.
uint32_t utf8CodePoint
Definition: web_socket.h:422
@ ERROR_FAILURE
Generic error code.
Definition: error.h:45
error_t webSocketVerifyServerKey(WebSocket *webSocket)
Verify server's key.
error_t webSocketReceiveData(WebSocket *webSocket, void *data, size_t size, size_t *received, uint_t flags)
Receive data using the relevant transport protocol.
char_t clientKey[WEB_SOCKET_CLIENT_KEY_SIZE+1]
Definition: web_socket.h:387
Handshake context.
Definition: web_socket.h:380
WebSocketRandCallback webSockRandCallback
Definition: web_socket.c:52
@ WS_ENDPOINT_SERVER
Definition: web_socket.h:192
UTF-8 decoding context.
Definition: web_socket.h:419
#define osStrcasecmp(s1, s2)
Definition: os_port.h:183
error_t webSocketParseStatusLine(WebSocket *webSocket, char_t *line)
Parse the Status-Line of the server's handshake.
Base64 encoding scheme.
@ WS_STATUS_CODE_MESSAGE_TOO_BIG
Definition: web_socket.h:287
#define MSB(x)
Definition: os_port.h:59
uint8_t length
Definition: tcp.h:368
#define LSB(x)
Definition: os_port.h:55
@ WS_STATUS_CODE_INTERNAL_ERROR
Definition: web_socket.h:289
WebSocketState
WebSocket states.
Definition: web_socket.h:225
@ WS_ENDPOINT_CLIENT
Definition: web_socket.h:191
#define WebSocket
Definition: web_socket.h:177
@ WS_SUB_STATE_HANDSHAKE_LEADING_LINE
Definition: web_socket.h:248
error_t webSocketFormatClientHandshake(WebSocket *webSocket, uint16_t serverPort)
Format client's handshake.
error_t webSocketVerifyServerHandshake(WebSocket *webSocket)
Verify server's handshake.
@ WS_STATUS_CODE_POLICY_VIOLATION
Definition: web_socket.h:286
WebSocketSubState state
Definition: web_socket.h:400
#define osStrtoul(s, endptr, base)
Definition: os_port.h:255
@ WS_SUB_STATE_INIT
Definition: web_socket.h:246
#define TRACE_DEBUG(...)
Definition: debug.h:107
char char_t
Definition: compiler_port.h:48
#define SHA1_DIGEST_SIZE
Definition: sha1.h:45
error_t webSocketFormatServerHandshake(WebSocket *webSocket)
Format server's handshake.
error_t strSafeCopy(char_t *dest, const char_t *src, size_t destSize)
Copy string.
Definition: str.c:172
@ WS_STATUS_CODE_INVALID_PAYLOAD_DATA
Definition: web_socket.h:285
uint8_t n
uint8_t buffer[WEB_SOCKET_BUFFER_SIZE]
Data buffer.
Definition: web_socket.h:408
error_t webSocketVerifyClientHandshake(WebSocket *webSocket)
Verify client's handshake.
error_t webSocketParseRequestLine(WebSocket *webSocket, char_t *line)
Parse the Request-Line of the client's handshake.
error_t webSocketDecodePercentEncodedString(const char_t *input, char_t *output, size_t outputSize)
Decode a percent-encoded string.
bool_t fin
Final fragment in a message.
Definition: web_socket.h:403
uint8_t value[]
Definition: tcp.h:369
@ ERROR_INVALID_SYNTAX
Definition: error.h:68
@ WS_STATUS_CODE_MANDATORY_EXT
Definition: web_socket.h:288
SHA-1 algorithm context.
Definition: sha1.h:62
uint8_t s
Definition: igmp_common.h:234
@ WS_SUB_STATE_HANDSHAKE_LWSP
Definition: web_socket.h:250
#define WEB_SOCKET_QUERY_STRING_MAX_LEN
Definition: web_socket.h:117
@ WS_STATE_SERVER_HANDSHAKE
Definition: web_socket.h:231
#define WEB_SOCKET_SERVER_KEY_SIZE
Definition: web_socket.h:173
#define osStrncmp(s1, s2, length)
Definition: os_port.h:177
@ WS_STATUS_CODE_PROTOCOL_ERROR
Definition: web_socket.h:281
error_t webSocketParseHandshake(WebSocket *webSocket)
Parse client or server handshake.
WebSocket transport layer.
size_t bufferPos
Current position.
Definition: web_socket.h:410
size_t webSocketAddAuthorizationField(WebSocket *webSocket, char_t *output)
Format Authorization header field.
error_t webSocketVerifyClientKey(WebSocket *webSocket)
Verify client's key.
#define PRIuSIZE
unsigned int uint_t
Definition: compiler_port.h:50
TCP/IP stack core.
@ WS_HTTP_VERSION_1_1
Definition: web_socket.h:204
#define WEB_SOCKET_URI_MAX_LEN
Definition: web_socket.h:110
error_t webSocketGenerateServerKey(WebSocket *webSocket)
Generate server's key.
void sha1Final(Sha1Context *context, uint8_t *digest)
Finish the SHA-1 message digest.
error_t webSocketParseAuthenticateField(WebSocket *webSocket, char_t *value)
Parse WWW-Authenticate header field.
@ WS_STATUS_CODE_NORMAL_CLOSURE
Definition: web_socket.h:279
#define osStrcpy(s1, s2)
Definition: os_port.h:207
@ SOCKET_FLAG_BREAK_CRLF
Definition: socket.h:141
@ ERROR_INVALID_REQUEST
Definition: error.h:65
uint8_t digest[20]
Definition: sha1.h:66
uint8_t nonce[]
Definition: ntp_common.h:233
@ ERROR_INVALID_KEY
Definition: error.h:106
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
@ WS_STATE_OPEN
Definition: web_socket.h:233
uint8_t token[]
Definition: coap_common.h:181
void webSocketChangeState(WebSocket *webSocket, WebSocketState newState)
Update WebSocket state.
#define arraysize(a)
Definition: os_port.h:71
systime_t osGetSystemTime(void)
Retrieve system time.