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