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-2019 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 1.9.6
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  strcpy(webSocket->authContext.nonce, "");
162  strcpy(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  strcpy(webSocket->handshakeContext.serverKey, "");
171 
172  //Debug message
173  TRACE_DEBUG("WebSocket: server handshake\r\n");
174  }
175  else
176  {
177  //Clear client key
178  strcpy(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(strncmp((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(strncmp((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(!strcmp((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 = strtok_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(strcasecmp(token, "GET"))
384  return ERROR_INVALID_REQUEST;
385 
386  //The Request-URI is following the method token
387  token = strtok_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 = strchr(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
409  if(strlen(s + 1) > WEB_SOCKET_QUERY_STRING_MAX_LEN)
410  return ERROR_INVALID_REQUEST;
411 
412  //Save the query string
413  strcpy(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 = strtok_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(!strcasecmp(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(!strcasecmp(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 = strtok_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 = strtok_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 = strtoul(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 = strchr(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(!strcasecmp(name, "Upgrade"))
540  {
541  if(!strcasecmp(value, "websocket"))
542  handshakeContext->upgradeWebSocket = TRUE;
543 
544  }
545  //Connection header field found?
546  else if(!strcasecmp(name, "Connection"))
547  {
548  //Parse Connection header field
550  }
551  //Sec-WebSocket-Key header field found?
552  else if(!strcasecmp(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(!strcasecmp(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(!strcasecmp(name, "WWW-Authenticate"))
576  {
577  //Parse WWW-Authenticate header field
579  }
580 #endif
581  //Content-Length header field found?
582  else if(!strcasecmp(name, "Content-Length"))
583  {
584  handshakeContext->contentLength = strtoul(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 = strtok_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(!strcasecmp(value, "keep-alive"))
615  {
616  //The connection is persistent
617  webSocket->handshakeContext.connectionClose = FALSE;
618  }
619  else if(!strcasecmp(value, "close"))
620  {
621  //The connection will be closed after completion of the response
622  webSocket->handshakeContext.connectionClose = TRUE;
623  }
624  else if(!strcasecmp(value, "upgrade"))
625  {
626  //Upgrade the connection
627  webSocket->handshakeContext.connectionUpgrade = TRUE;
628  }
629 
630  //Get next value
631  token = strtok_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 += sprintf(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 += sprintf(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 += sprintf(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  p += sprintf(p, "Origin: %s\r\n", webSocket->origin);
683  else
684  p += sprintf(p, "Origin: null\r\n");
685 
686  //Add Upgrade header field
687  p += sprintf(p, "Upgrade: websocket\r\n");
688  //Add Connection header field
689  p += sprintf(p, "Connection: Upgrade\r\n");
690 
691  //Add Sec-WebSocket-Protocol header field
692  if(webSocket->subProtocol[0] != '\0')
693  p += sprintf(p, "Sec-WebSocket-Protocol: %s\r\n", webSocket->subProtocol);
694 
695  //Add Sec-WebSocket-Key header field
696  p += sprintf(p, "Sec-WebSocket-Key: %s\r\n",
697  webSocket->handshakeContext.clientKey);
698 
699  //Add Sec-WebSocket-Version header field
700  p += sprintf(p, "Sec-WebSocket-Version: 13\r\n");
701  //An empty line indicates the end of the header fields
702  p += sprintf(p, "\r\n");
703 
704  //Debug message
705  TRACE_DEBUG("\r\n");
706  TRACE_DEBUG("WebSocket: client handshake\r\n");
707  TRACE_DEBUG("%s", txContext->buffer);
708 
709  //Rewind to the beginning of the buffer
710  txContext->bufferPos = 0;
711  //Update the number of data buffered but not yet sent
712  txContext->bufferLen = strlen((char_t *) txContext->buffer);
713 
714  //Successful processing
715  return NO_ERROR;
716 }
717 
718 
719 /**
720  * @brief Format server's handshake
721  * @param[in] webSocket Handle to a WebSocket
722  * @return Error code
723  **/
724 
726 {
727  char_t *p;
728  WebSocketFrameContext *txContext;
729 
730  //Point to the TX context
731  txContext = &webSocket->txContext;
732  //Point to the buffer where to format the client's handshake
733  p = (char_t *) txContext->buffer;
734 
735  //The first line is an HTTP Status-Line, with the status code 101
736  p += sprintf(p, "HTTP/1.1 101 Switching Protocols\r\n");
737 
738  //Add Upgrade header field
739  p += sprintf(p, "Upgrade: websocket\r\n");
740  //Add Connection header field
741  p += sprintf(p, "Connection: Upgrade\r\n");
742 
743  //Add Sec-WebSocket-Protocol header field
744  if(webSocket->subProtocol[0] != '\0')
745  p += sprintf(p, "Sec-WebSocket-Protocol: %s\r\n", webSocket->subProtocol);
746 
747  //Add Sec-WebSocket-Accept header field
748  p += sprintf(p, "Sec-WebSocket-Accept: %s\r\n",
749  webSocket->handshakeContext.serverKey);
750 
751  //An empty line indicates the end of the header fields
752  p += sprintf(p, "\r\n");
753 
754  //Debug message
755  TRACE_DEBUG("\r\n");
756  TRACE_DEBUG("WebSocket: server handshake\r\n");
757  TRACE_DEBUG("%s", txContext->buffer);
758 
759  //Rewind to the beginning of the buffer
760  txContext->bufferPos = 0;
761  //Update the number of data buffered but not yet sent
762  txContext->bufferLen = strlen((char_t *) txContext->buffer);
763 
764  //Successful processing
765  return NO_ERROR;
766 }
767 
768 
769 /**
770  * @brief Format HTTP error response
771  * @param[in] webSocket Handle to a WebSocket
772  * @param[in] statusCode HTTP status code
773  * @param[in] message User message
774  * @return Error code
775  **/
776 
779 {
780  uint_t i;
781  size_t length;
782  char_t *p;
783  WebSocketFrameContext *txContext;
784 
785  //HTML response template
786  static const char_t template[] =
787  "<!doctype html>\r\n"
788  "<html>\r\n"
789  "<head><title>Error %03d</title></head>\r\n"
790  "<body>\r\n"
791  "<h2>Error %03d</h2>\r\n"
792  "<p>%s</p>\r\n"
793  "</body>\r\n"
794  "</html>\r\n";
795 
796  //Point to the TX context
797  txContext = &webSocket->txContext;
798  //Point to the buffer where to format the client's handshake
799  p = (char_t *) txContext->buffer;
800 
801  //The first line of a response message is the Status-Line, consisting
802  //of the protocol version followed by a numeric status code and its
803  //associated textual phrase
804  p += sprintf(p, "HTTP/%u.%u %u ", MSB(webSocket->handshakeContext.version),
805  LSB(webSocket->handshakeContext.version), statusCode);
806 
807  //Retrieve the Reason-Phrase that corresponds to the Status-Code
808  for(i = 0; i < arraysize(statusCodeList); i++)
809  {
810  //Check the status code
811  if(statusCodeList[i].value == statusCode)
812  {
813  //Append the textual phrase to the Status-Line
814  p += sprintf(p, statusCodeList[i].message);
815  //Break the loop and continue processing
816  break;
817  }
818  }
819 
820  //Properly terminate the Status-Line
821  p += sprintf(p, "\r\n");
822 
823  //Content type
824  p += sprintf(p, "Content-Type: %s\r\n", "text/html");
825 
826  //Compute the length of the response
827  length = strlen(template) + strlen(message) - 4;
828  //Set Content-Length field
829  p += sprintf(p, "Content-Length: %" PRIuSIZE "\r\n", length);
830 
831  //Terminate the header with an empty line
832  p += sprintf(p, "\r\n");
833 
834  //Format HTML response
835  p += sprintf(p, template, statusCode, statusCode, message);
836 
837  //Rewind to the beginning of the buffer
838  txContext->bufferPos = 0;
839  //Update the number of data buffered but not yet sent
840  txContext->bufferLen = strlen((char_t *) txContext->buffer);
841 
842  //Successful processing
843  return NO_ERROR;
844 }
845 
846 
847 /**
848  * @brief Verify client's handshake
849  * @param[in] webSocket Handle to a WebSocket
850  * @return Error code
851  **/
852 
854 {
855  error_t error;
856  WebSocketHandshakeContext *handshakeContext;
857 
858  //Debug message
859  TRACE_DEBUG("WebSocket: verifying client handshake\r\n");
860 
861  //Point to the handshake context
862  handshakeContext = &webSocket->handshakeContext;
863 
864  //The HTTP version must be at least 1.1
865  if(handshakeContext->version < WS_HTTP_VERSION_1_1)
866  return ERROR_INVALID_REQUEST;
867 
868  //The request must contain an Upgrade header field whose value
869  //must include the "websocket" keyword
870  if(!handshakeContext->upgradeWebSocket)
871  return ERROR_INVALID_REQUEST;
872 
873  //The request must contain a Connection header field whose value
874  //must include the "Upgrade" token
875  if(!handshakeContext->connectionUpgrade)
876  return ERROR_INVALID_REQUEST;
877 
878  //The request must include a header field with the name Sec-WebSocket-Key
879  if(handshakeContext->clientKey[0] == 0)
880  return ERROR_INVALID_REQUEST;
881 
882  //Check the Sec-WebSocket-Key header field
883  error = webSocketVerifyClientKey(webSocket);
884  //Verification failed?
885  if(error)
886  return error;
887 
888  //Generate the server part of the handshake
890 
891  //Successful processing
892  return NO_ERROR;
893 }
894 
895 
896 /**
897  * @brief Verify server's handshake
898  * @param[in] webSocket Handle to a WebSocket
899  * @return Error code
900  **/
901 
903 {
904  error_t error;
905  WebSocketHandshakeContext *handshakeContext;
906 
907  //Debug message
908  TRACE_DEBUG("WebSocket: verifying server handshake\r\n");
909 
910  //Point to the handshake context
911  handshakeContext = &webSocket->handshakeContext;
912 
913  //If the status code received from the server is not 101, the client
914  //handles the response per HTTP procedures
915  if(handshakeContext->statusCode == 401)
916  {
917  //Authorization required
918  return ERROR_AUTH_REQUIRED;
919  }
920  else if(handshakeContext->statusCode != 101)
921  {
922  //Unknown status code
923  return ERROR_INVALID_STATUS;
924  }
925 
926  //If the response lacks an Upgrade header field or the Upgrade header field
927  //contains a value that is not an ASCII case-insensitive match for the
928  //value "websocket", the client must fail the WebSocket connection
929  if(!handshakeContext->upgradeWebSocket)
930  return ERROR_INVALID_SYNTAX;
931 
932  //If the response lacks a Connection header field or the Connection header
933  //field doesn't contain a token that is an ASCII case-insensitive match for
934  //the value "Upgrade", the client must fail the WebSocket connection
935  if(!handshakeContext->connectionUpgrade)
936  return ERROR_INVALID_SYNTAX;
937 
938  //If the response lacks a Sec-WebSocket-Accept header field, the client
939  //must fail the WebSocket connection
940  if(strlen(handshakeContext->serverKey) == 0)
941  return ERROR_INVALID_SYNTAX;
942 
943  //Check the Sec-WebSocket-Accept header field
944  error = webSocketVerifyServerKey(webSocket);
945  //Verification failed?
946  if(error)
947  return error;
948 
949  //If the server's response is validated as provided for above, it is
950  //said that the WebSocket connection is established and that the
951  //WebSocket connection is in the OPEN state
953 
954  //Successful processing
955  return NO_ERROR;
956 }
957 
958 
959 /**
960  * @brief Generate client's key
961  * @param[in] webSocket Handle to a WebSocket
962  * @return Error code
963  **/
964 
966 {
967  error_t error;
968  size_t n;
969  uint8_t nonce[16];
970  WebSocketHandshakeContext *handshakeContext;
971 
972  //Debug message
973  TRACE_DEBUG("WebSocket: Generating client's key...\r\n");
974 
975  //Point to the handshake context
976  handshakeContext = &webSocket->handshakeContext;
977 
978  //Make sure that the RNG callback function has been registered
979  if(webSockRandCallback == NULL)
980  {
981  //A cryptographically strong random number generator
982  //must be used to generate the nonce
983  return ERROR_PRNG_NOT_READY;
984  }
985 
986  //A nonce must be selected randomly for each connection
987  error = webSockRandCallback(nonce, sizeof(nonce));
988  //Any error to report?
989  if(error)
990  return error;
991 
992  //Encode the client's key
993  base64Encode(nonce, sizeof(nonce), handshakeContext->clientKey, &n);
994 
995  //Debug message
996  TRACE_DEBUG(" Client key: %s\r\n", handshakeContext->clientKey);
997 
998  //Successful processing
999  return NO_ERROR;
1000 }
1001 
1002 
1003 /**
1004  * @brief Generate server's key
1005  * @param[in] webSocket Handle to a WebSocket
1006  * @return Error code
1007  **/
1008 
1010 {
1011  size_t n;
1012  WebSocketHandshakeContext *handshakeContext;
1013  Sha1Context sha1Context;
1014 
1015  //Debug message
1016  TRACE_DEBUG("WebSocket: Generating server's key...\r\n");
1017 
1018  //Point to the handshake context
1019  handshakeContext = &webSocket->handshakeContext;
1020 
1021  //Retrieve the length of the client key
1022  n = strlen(handshakeContext->clientKey);
1023 
1024  //Concatenate the Sec-WebSocket-Key with the GUID string and digest
1025  //the resulting string using SHA-1
1026  sha1Init(&sha1Context);
1027  sha1Update(&sha1Context, handshakeContext->clientKey, n);
1028  sha1Update(&sha1Context, webSocketGuid, strlen(webSocketGuid));
1029  sha1Final(&sha1Context, NULL);
1030 
1031  //Encode the result using Base64
1032  base64Encode(sha1Context.digest, SHA1_DIGEST_SIZE,
1033  handshakeContext->serverKey, &n);
1034 
1035  //Debug message
1036  TRACE_DEBUG(" Server key: %s\r\n", handshakeContext->serverKey);
1037 
1038  //Successful processing
1039  return NO_ERROR;
1040 }
1041 
1042 
1043 /**
1044  * @brief Verify client's key
1045  * @param[in] webSocket Handle to a WebSocket
1046  * @return Error code
1047  **/
1048 
1050 {
1051  error_t error;
1052  size_t n;
1053  char_t *buffer;
1054  WebSocketHandshakeContext *handshakeContext;
1055 
1056  //Debug message
1057  TRACE_DEBUG("WebSocket: Verifying client's key...\r\n");
1058 
1059  //Temporary buffer
1060  buffer = (char_t *) webSocket->txContext.buffer;
1061 
1062  //Point to the handshake context
1063  handshakeContext = &webSocket->handshakeContext;
1064 
1065  //Retrieve the length of the client's key
1066  n = strlen(handshakeContext->clientKey);
1067 
1068  //The value of the Sec-WebSocket-Key header field must be a 16-byte
1069  //value that has been Base64-encoded
1070  error = base64Decode(handshakeContext->clientKey, n, buffer, &n);
1071  //Decoding failed?
1072  if(error)
1073  return ERROR_INVALID_KEY;
1074 
1075  //Check the length of the resulting value
1076  if(n != 16)
1077  return ERROR_INVALID_KEY;
1078 
1079  //Successful verification
1080  return NO_ERROR;
1081 }
1082 
1083 
1084 /**
1085  * @brief Verify server's key
1086  * @param[in] webSocket Handle to a WebSocket
1087  * @return Error code
1088  **/
1089 
1091 {
1092  size_t n;
1093  char_t *buffer;
1094  WebSocketHandshakeContext *handshakeContext;
1095  Sha1Context sha1Context;
1096 
1097  //Debug message
1098  TRACE_DEBUG("WebSocket: Verifying server's key...\r\n");
1099 
1100  //Temporary buffer
1101  buffer = (char_t *) webSocket->txContext.buffer;
1102 
1103  //Point to the handshake context
1104  handshakeContext = &webSocket->handshakeContext;
1105 
1106  //Retrieve the length of the client's key
1107  n = strlen(handshakeContext->clientKey);
1108 
1109  //Concatenate the Sec-WebSocket-Key with the GUID string and digest
1110  //the resulting string using SHA-1
1111  sha1Init(&sha1Context);
1112  sha1Update(&sha1Context, handshakeContext->clientKey, n);
1113  sha1Update(&sha1Context, webSocketGuid, strlen(webSocketGuid));
1114  sha1Final(&sha1Context, NULL);
1115 
1116  //Encode the result using Base64
1117  base64Encode(sha1Context.digest, SHA1_DIGEST_SIZE, buffer, &n);
1118 
1119  //Debug message
1120  TRACE_DEBUG(" Client key: %s\r\n", handshakeContext->clientKey);
1121  TRACE_DEBUG(" Server key: %s\r\n", handshakeContext->serverKey);
1122  TRACE_DEBUG(" Calculated key: %s\r\n", webSocket->txContext.buffer);
1123 
1124  //Check whether the server's key is valid
1125  if(strcmp(handshakeContext->serverKey, buffer))
1126  return ERROR_INVALID_KEY;
1127 
1128  //Successful verification
1129  return NO_ERROR;
1130 }
1131 
1132 
1133 /**
1134  * @brief Check whether a status code is valid
1135  * @param[in] statusCode Status code
1136  * @return The function returns TRUE is the specified status code is
1137  * valid. Otherwise, FALSE is returned
1138  **/
1139 
1141 {
1142  bool_t valid;
1143 
1144  //Check status code
1154  {
1155  valid = TRUE;
1156  }
1157  else if(statusCode >= 3000)
1158  {
1159  valid = TRUE;
1160  }
1161  else
1162  {
1163  valid = FALSE;
1164  }
1165 
1166  //The function returns TRUE is the specified status code is valid
1167  return valid;
1168 }
1169 
1170 
1171 /**
1172  * @brief Decode a percent-encoded string
1173  * @param[in] input NULL-terminated string to be decoded
1174  * @param[out] output NULL-terminated string resulting from the decoding process
1175  * @param[in] outputSize Size of the output buffer in bytes
1176  * @return Error code
1177  **/
1178 
1180  char_t *output, size_t outputSize)
1181 {
1182  size_t i;
1183  char_t buffer[3];
1184 
1185  //Check parameters
1186  if(input == NULL || output == NULL)
1187  return ERROR_INVALID_PARAMETER;
1188 
1189  //Decode the percent-encoded string
1190  for(i = 0; *input != '\0' && i < outputSize; i++)
1191  {
1192  //Check current character
1193  if(*input == '+')
1194  {
1195  //Replace '+' characters with spaces
1196  output[i] = ' ';
1197  //Advance data pointer
1198  input++;
1199  }
1200  else if(input[0] == '%' && input[1] != '\0' && input[2] != '\0')
1201  {
1202  //Process percent-encoded characters
1203  buffer[0] = input[1];
1204  buffer[1] = input[2];
1205  buffer[2] = '\0';
1206  //String to integer conversion
1207  output[i] = (uint8_t) strtoul(buffer, NULL, 16);
1208  //Advance data pointer
1209  input += 3;
1210  }
1211  else
1212  {
1213  //Copy any other characters
1214  output[i] = *input;
1215  //Advance data pointer
1216  input++;
1217  }
1218  }
1219 
1220  //Check whether the output buffer runs out of space
1221  if(i >= outputSize)
1222  return ERROR_FAILURE;
1223 
1224  //Properly terminate the resulting string
1225  output[i] = '\0';
1226  //Successful processing
1227  return NO_ERROR;
1228 }
1229 
1230 
1231 /**
1232  * @brief Check whether a an UTF-8 stream is valid
1233  * @param[in] context UTF-8 decoding context
1234  * @param[in] data Pointer to the chunk of data to be processed
1235  * @param[in] length Data chunk length
1236  * @param[in] remaining number of remaining bytes in the UTF-8 stream
1237  * @return The function returns TRUE is the specified UTF-8 stream is
1238  * valid. Otherwise, FALSE is returned
1239  **/
1240 
1242  const uint8_t *data, size_t length, size_t remaining)
1243 {
1244  size_t i;
1245  bool_t valid;
1246 
1247  //Initialize flag
1248  valid = TRUE;
1249 
1250  //Interpret the byte stream as UTF-8
1251  for(i = 0; i < length && valid; i++)
1252  {
1253  //Leading or continuation byte?
1254  if(context->utf8CharIndex == 0)
1255  {
1256  //7-bit code point?
1257  if((data[i] & 0x80) == 0x00)
1258  {
1259  //The code point consist of a single byte
1260  context->utf8CharSize = 1;
1261  //Decode the first byte of the sequence
1262  context->utf8CodePoint = data[i] & 0x7F;
1263  }
1264  //11-bit code point?
1265  else if((data[i] & 0xE0) == 0xC0)
1266  {
1267  //The code point consist of a 2 bytes
1268  context->utf8CharSize = 2;
1269  //Decode the first byte of the sequence
1270  context->utf8CodePoint = (data[i] & 0x1F) << 6;
1271  }
1272  //16-bit code point?
1273  else if((data[i] & 0xF0) == 0xE0)
1274  {
1275  //The code point consist of a 3 bytes
1276  context->utf8CharSize = 3;
1277  //Decode the first byte of the sequence
1278  context->utf8CodePoint = (data[i] & 0x0F) << 12;
1279  }
1280  //21-bit code point?
1281  else if((data[i] & 0xF8) == 0xF0)
1282  {
1283  //The code point consist of a 3 bytes
1284  context->utf8CharSize = 4;
1285  //Decode the first byte of the sequence
1286  context->utf8CodePoint = (data[i] & 0x07) << 18;
1287  }
1288  else
1289  {
1290  //The UTF-8 stream is not valid
1291  valid = FALSE;
1292  }
1293 
1294  //This test only applies to frames that are not fragmented
1295  if(length <= remaining)
1296  {
1297  //Make sure the UTF-8 stream is properly terminated
1298  if((i + context->utf8CharSize) > remaining)
1299  {
1300  //The UTF-8 stream is not valid
1301  valid = FALSE;
1302  }
1303  }
1304 
1305  //Decode the next byte of the sequence
1306  context->utf8CharIndex = context->utf8CharSize - 1;
1307  }
1308  else
1309  {
1310  //Continuation bytes all have 10 in the high-order position
1311  if((data[i] & 0xC0) == 0x80)
1312  {
1313  //Decode the multi-byte sequence
1314  context->utf8CharIndex--;
1315  //All continuation bytes contain exactly 6 bits from the code point
1316  context->utf8CodePoint |= (data[i] & 0x3F) << (context->utf8CharIndex * 6);
1317 
1318  //The correct encoding of a code point use only the minimum number
1319  //of bytes required to hold the significant bits of the code point
1320  if(context->utf8CharSize == 2)
1321  {
1322  //Overlong encoding is not supported
1323  if((context->utf8CodePoint & ~0x7F) == 0)
1324  valid = FALSE;
1325  }
1326  if(context->utf8CharSize == 3 && context->utf8CharIndex < 2)
1327  {
1328  //Overlong encoding is not supported
1329  if((context->utf8CodePoint & ~0x7FF) == 0)
1330  valid = FALSE;
1331  }
1332  if(context->utf8CharSize == 4 && context->utf8CharIndex < 3)
1333  {
1334  //Overlong encoding is not supported
1335  if((context->utf8CodePoint & ~0xFFFF) == 0)
1336  valid = FALSE;
1337  }
1338 
1339  //According to the UTF-8 definition (RFC 3629) the high and low
1340  //surrogate halves used by UTF-16 (U+D800 through U+DFFF) are not
1341  //legal Unicode values, and their UTF-8 encoding should be treated
1342  //as an invalid byte sequence
1343  if(context->utf8CodePoint >= 0xD800 && context->utf8CodePoint < 0xE000)
1344  valid = FALSE;
1345 
1346  //Code points greater than U+10FFFF are not valid
1347  if(context->utf8CodePoint >= 0x110000)
1348  valid = FALSE;
1349  }
1350  else
1351  {
1352  //The start byte is not followed by enough continuation bytes
1353  valid = FALSE;
1354  }
1355  }
1356  }
1357 
1358  //The function returns TRUE is the specified UTF-8 stream is valid
1359  return valid;
1360 }
1361 
1362 #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
void sha1Update(Sha1Context *context, const void *data, size_t length)
Update the SHA-1 context with a portion of the message being hashed.
Definition: sha1.c:152
uint8_t length
Definition: dtls_misc.h:149
String manipulation helper functions.
HTTP status code.
int bool_t
Definition: compiler_port.h:49
@ WS_AUTH_MODE_NONE
Definition: web_socket.h:214
#define WEB_SOCKET_CLIENT_KEY_SIZE
Definition: web_socket.h:171
SHA-1 (Secure Hash Algorithm 1)
uint8_t p
Definition: ndp.h:298
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
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.
void base64Encode(const void *input, size_t inputLen, char_t *output, size_t *outputLen)
Base64 encoding algorithm.
Definition: base64.c:78
Helper functions for WebSockets.
@ WS_STATUS_CODE_NO_STATUS_RCVD
Definition: web_socket.h:283
@ ERROR_INVALID_STATUS
Definition: error.h:101
error_t webSocketParseHeaderField(WebSocket *webSocket, char_t *line)
Parse a header field.
char_t name[]
@ ERROR_AUTH_REQUIRED
Definition: error.h:148
#define strtok_r(str, delim, p)
@ ERROR_PRNG_NOT_READY
Definition: error.h:245
@ 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:207
@ 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:194
#define FALSE
Definition: os_port.h:46
char_t serverKey[WEB_SOCKET_SERVER_KEY_SIZE+1]
Definition: web_socket.h:384
void sha1Init(Sha1Context *context)
Initialize SHA-1 message digest context.
Definition: sha1.c:129
@ ERROR_INVALID_PARAMETER
Invalid parameter.
Definition: error.h:47
size_t bufferLen
Length of the data buffer.
Definition: web_socket.h:405
error_t
Error codes.
Definition: error.h:42
Frame encoding/decoding context.
Definition: web_socket.h:394
error_t webSocketGenerateClientKey(WebSocket *webSocket)
Generate client's key.
bool_t webSocketCheckStatusCode(uint16_t statusCode)
Check whether a status code is valid.
uint16_t statusCode
uint32_t utf8CodePoint
Definition: web_socket.h:418
@ 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:383
Handshake context.
Definition: web_socket.h:375
WebSocketRandCallback webSockRandCallback
Definition: web_socket.c:52
@ WS_ENDPOINT_SERVER
Definition: web_socket.h:192
UTF-8 decoding context.
Definition: web_socket.h:414
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:58
#define LSB(x)
Definition: os_port.h:54
@ WS_STATUS_CODE_INTERNAL_ERROR
Definition: web_socket.h:289
WebSocketState
WebSocket states.
Definition: web_socket.h:224
@ 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:396
@ WS_SUB_STATE_INIT
Definition: web_socket.h:246
#define TRACE_DEBUG(...)
Definition: debug.h:106
char char_t
Definition: compiler_port.h:43
#define SHA1_DIGEST_SIZE
Definition: sha1.h:40
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:167
@ 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:404
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:399
#define strcasecmp
uint8_t s
uint8_t message[]
Definition: chap.h:152
@ ERROR_INVALID_SYNTAX
Definition: error.h:68
@ WS_STATUS_CODE_MANDATORY_EXT
Definition: web_socket.h:288
SHA-1 algorithm context.
Definition: sha1.h:58
@ 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
@ 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:406
size_t webSocketAddAuthorizationField(WebSocket *webSocket, char_t *output)
Format Authorization header field.
error_t webSocketVerifyClientKey(WebSocket *webSocket)
Verify client's key.
uint8_t value[]
Definition: dtls_misc.h:150
#define PRIuSIZE
Definition: compiler_port.h:78
unsigned int uint_t
Definition: compiler_port.h:45
TCP/IP stack core.
@ WS_HTTP_VERSION_1_1
Definition: web_socket.h:204
uint8_t data[]
Definition: dtls_misc.h:176
#define WEB_SOCKET_URI_MAX_LEN
Definition: web_socket.h:110
error_t webSocketGenerateServerKey(WebSocket *webSocket)
Generate server's key.
error_t webSocketParseAuthenticateField(WebSocket *webSocket, char_t *value)
Parse WWW-Authenticate header field.
@ WS_STATUS_CODE_NORMAL_CLOSURE
Definition: web_socket.h:279
@ SOCKET_FLAG_BREAK_CRLF
Definition: socket.h:123
@ ERROR_INVALID_REQUEST
Definition: error.h:65
void sha1Final(Sha1Context *context, uint8_t *digest)
Finish the SHA-1 message digest.
Definition: sha1.c:191
uint8_t digest[20]
Definition: sha1.h:63
@ ERROR_INVALID_KEY
Definition: error.h:104
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
@ WS_STATE_OPEN
Definition: web_socket.h:233
uint8_t token[]
Definition: coap_common.h:183
void webSocketChangeState(WebSocket *webSocket, WebSocketState newState)
Update WebSocket state.
#define arraysize(a)
Definition: os_port.h:70
systime_t osGetSystemTime(void)
Retrieve system time.