http_server_misc.c
Go to the documentation of this file.
1 /**
2  * @file http_server_misc.c
3  * @brief HTTP server (miscellaneous functions)
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 HTTP_TRACE_LEVEL
33 
34 //Dependencies
35 #include <stdlib.h>
36 #include <limits.h>
37 #include "core/net.h"
38 #include "http/http_server.h"
39 #include "http/http_server_auth.h"
40 #include "http/http_server_misc.h"
41 #include "http/mime.h"
42 #include "str.h"
43 #include "path.h"
44 #include "debug.h"
45 
46 //Check TCP/IP stack configuration
47 #if (HTTP_SERVER_SUPPORT == ENABLED)
48 
49 
50 /**
51  * @brief HTTP status codes
52  **/
53 
54 static const HttpStatusCodeDesc statusCodeList[] =
55 {
56  //Success
57  {200, "OK"},
58  {201, "Created"},
59  {202, "Accepted"},
60  {204, "No Content"},
61  //Redirection
62  {301, "Moved Permanently"},
63  {302, "Found"},
64  {304, "Not Modified"},
65  //Client error
66  {400, "Bad Request"},
67  {401, "Unauthorized"},
68  {403, "Forbidden"},
69  {404, "Not Found"},
70  //Server error
71  {500, "Internal Server Error"},
72  {501, "Not Implemented"},
73  {502, "Bad Gateway"},
74  {503, "Service Unavailable"}
75 };
76 
77 
78 /**
79  * @brief Read HTTP request header and parse its contents
80  * @param[in] connection Structure representing an HTTP connection
81  * @return Error code
82  **/
83 
85 {
86  error_t error;
87  size_t length;
88 
89  //Set the maximum time the server will wait for an HTTP
90  //request before closing the connection
91  error = socketSetTimeout(connection->socket, HTTP_SERVER_IDLE_TIMEOUT);
92  //Any error to report?
93  if(error)
94  return error;
95 
96  //Read the first line of the request
97  error = httpReceive(connection, connection->buffer,
99  //Unable to read any data?
100  if(error)
101  return error;
102 
103  //Revert to default timeout
104  error = socketSetTimeout(connection->socket, HTTP_SERVER_TIMEOUT);
105  //Any error to report?
106  if(error)
107  return error;
108 
109  //Properly terminate the string with a NULL character
110  connection->buffer[length] = '\0';
111  //Debug message
112  TRACE_INFO("%s", connection->buffer);
113 
114  //Parse the Request-Line
115  error = httpParseRequestLine(connection, connection->buffer);
116  //Any error to report?
117  if(error)
118  return error;
119 
120  //Default value for properties
121  connection->request.chunkedEncoding = FALSE;
122  connection->request.contentLength = 0;
123 #if (HTTP_SERVER_WEB_SOCKET_SUPPORT == ENABLED)
124  connection->request.upgradeWebSocket = FALSE;
125  connection->request.connectionUpgrade = FALSE;
126  osStrcpy(connection->request.clientKey, "");
127 #endif
128 
129  //HTTP 0.9 does not support Full-Request
130  if(connection->request.version >= HTTP_VERSION_1_0)
131  {
132  //Local variables
133  char_t firstChar;
134  char_t *separator;
135  char_t *name;
136  char_t *value;
137 
138  //This variable is used to decode header fields that span multiple lines
139  firstChar = '\0';
140 
141  //Parse the header fields of the HTTP request
142  while(1)
143  {
144  //Decode multiple-line header field
145  error = httpReadHeaderField(connection, connection->buffer,
146  HTTP_SERVER_BUFFER_SIZE, &firstChar);
147  //Any error to report?
148  if(error)
149  return error;
150 
151  //Debug message
152  TRACE_DEBUG("%s", connection->buffer);
153 
154  //An empty line indicates the end of the header fields
155  if(!osStrcmp(connection->buffer, "\r\n"))
156  break;
157 
158  //Check whether a separator is present
159  separator = osStrchr(connection->buffer, ':');
160 
161  //Separator found?
162  if(separator != NULL)
163  {
164  //Split the line
165  *separator = '\0';
166 
167  //Trim whitespace characters
168  name = strTrimWhitespace(connection->buffer);
169  value = strTrimWhitespace(separator + 1);
170 
171  //Parse HTTP header field
172  httpParseHeaderField(connection, name, value);
173  }
174  }
175  }
176 
177  //Prepare to read the HTTP request body
178  if(connection->request.chunkedEncoding)
179  {
180  connection->request.byteCount = 0;
181  connection->request.firstChunk = TRUE;
182  connection->request.lastChunk = FALSE;
183  }
184  else
185  {
186  connection->request.byteCount = connection->request.contentLength;
187  }
188 
189  //The request header has been successfully parsed
190  return NO_ERROR;
191 }
192 
193 
194 /**
195  * @brief Parse Request-Line
196  * @param[in] connection Structure representing an HTTP connection
197  * @param[in] requestLine Pointer to the string that holds the Request-Line
198  * @return Error code
199  **/
200 
202 {
203  error_t error;
204  char_t *token;
205  char_t *p;
206  char_t *s;
207 
208  //The Request-Line begins with a method token
209  token = osStrtok_r(requestLine, " \r\n", &p);
210  //Unable to retrieve the method?
211  if(token == NULL)
212  return ERROR_INVALID_REQUEST;
213 
214  //The Method token indicates the method to be performed on the
215  //resource identified by the Request-URI
216  error = strSafeCopy(connection->request.method, token, HTTP_SERVER_METHOD_MAX_LEN);
217  //Any error to report?
218  if(error)
219  return ERROR_INVALID_REQUEST;
220 
221  //The Request-URI is following the method token
222  token = osStrtok_r(NULL, " \r\n", &p);
223  //Unable to retrieve the Request-URI?
224  if(token == NULL)
225  return ERROR_INVALID_REQUEST;
226 
227  //Check whether a query string is present
228  s = osStrchr(token, '?');
229 
230  //Query string found?
231  if(s != NULL)
232  {
233  //Split the string
234  *s = '\0';
235 
236  //Save the Request-URI
238  connection->request.uri, HTTP_SERVER_URI_MAX_LEN);
239  //Any error to report?
240  if(error)
241  return ERROR_INVALID_REQUEST;
242 
243  //Check the length of the query string
245  return ERROR_INVALID_REQUEST;
246 
247  //Save the query string
248  osStrcpy(connection->request.queryString, s + 1);
249  }
250  else
251  {
252  //Save the Request-URI
254  connection->request.uri, HTTP_SERVER_URI_MAX_LEN);
255  //Any error to report?
256  if(error)
257  return ERROR_INVALID_REQUEST;
258 
259  //No query string
260  connection->request.queryString[0] = '\0';
261  }
262 
263  //Redirect to the default home page if necessary
264  if(!osStrcasecmp(connection->request.uri, "/"))
265  osStrcpy(connection->request.uri, connection->settings->defaultDocument);
266 
267  //Clean the resulting path
268  pathCanonicalize(connection->request.uri);
269 
270  //The protocol version is following the Request-URI
271  token = osStrtok_r(NULL, " \r\n", &p);
272 
273  //HTTP version 0.9?
274  if(token == NULL)
275  {
276  //Save version number
277  connection->request.version = HTTP_VERSION_0_9;
278  //Persistent connections are not supported
279  connection->request.keepAlive = FALSE;
280  }
281  //HTTP version 1.0?
282  else if(!osStrcasecmp(token, "HTTP/1.0"))
283  {
284  //Save version number
285  connection->request.version = HTTP_VERSION_1_0;
286  //By default connections are not persistent
287  connection->request.keepAlive = FALSE;
288  }
289  //HTTP version 1.1?
290  else if(!osStrcasecmp(token, "HTTP/1.1"))
291  {
292  //Save version number
293  connection->request.version = HTTP_VERSION_1_1;
294  //HTTP 1.1 makes persistent connections the default
295  connection->request.keepAlive = TRUE;
296  }
297  //HTTP version not supported?
298  else
299  {
300  //Report an error
301  return ERROR_INVALID_REQUEST;
302  }
303 
304  //Successful processing
305  return NO_ERROR;
306 }
307 
308 
309 /**
310  * @brief Read multiple-line header field
311  * @param[in] connection Structure representing an HTTP connection
312  * @param[out] buffer Buffer where to store the header field
313  * @param[in] size Size of the buffer, in bytes
314  * @param[in,out] firstChar Leading character of the header line
315  * @return Error code
316  **/
317 
319  char_t *buffer, size_t size, char_t *firstChar)
320 {
321  error_t error;
322  size_t n;
323  size_t length;
324 
325  //This is the actual length of the header field
326  length = 0;
327 
328  //The process of moving from a multiple-line representation of a header
329  //field to its single line representation is called unfolding
330  do
331  {
332  //Check the length of the header field
333  if((length + 1) >= size)
334  {
335  //Report an error
336  error = ERROR_INVALID_REQUEST;
337  //Exit immediately
338  break;
339  }
340 
341  //NULL character found?
342  if(*firstChar == '\0')
343  {
344  //Prepare to decode the first header field
345  length = 0;
346  }
347  //LWSP character found?
348  else if(*firstChar == ' ' || *firstChar == '\t')
349  {
350  //Unfolding is accomplished by regarding CRLF immediately
351  //followed by a LWSP as equivalent to the LWSP character
352  buffer[length] = *firstChar;
353  //The current header field spans multiple lines
354  length++;
355  }
356  //Any other character?
357  else
358  {
359  //Restore the very first character of the header field
360  buffer[0] = *firstChar;
361  //Prepare to decode a new header field
362  length = 1;
363  }
364 
365  //Read data until a CLRF character is encountered
366  error = httpReceive(connection, buffer + length,
367  size - 1 - length, &n, SOCKET_FLAG_BREAK_CRLF);
368  //Any error to report?
369  if(error)
370  break;
371 
372  //Update the length of the header field
373  length += n;
374  //Properly terminate the string with a NULL character
375  buffer[length] = '\0';
376 
377  //An empty line indicates the end of the header fields
378  if(!osStrcmp(buffer, "\r\n"))
379  break;
380 
381  //Read the next character to detect if the CRLF is immediately
382  //followed by a LWSP character
383  error = httpReceive(connection, firstChar,
384  sizeof(char_t), &n, SOCKET_FLAG_WAIT_ALL);
385  //Any error to report?
386  if(error)
387  break;
388 
389  //LWSP character found?
390  if(*firstChar == ' ' || *firstChar == '\t')
391  {
392  //CRLF immediately followed by LWSP as equivalent to the LWSP character
393  if(length >= 2)
394  {
395  if(buffer[length - 2] == '\r' || buffer[length - 1] == '\n')
396  {
397  //Remove trailing CRLF sequence
398  length -= 2;
399  //Properly terminate the string with a NULL character
400  buffer[length] = '\0';
401  }
402  }
403  }
404 
405  //A header field may span multiple lines...
406  } while(*firstChar == ' ' || *firstChar == '\t');
407 
408  //Return status code
409  return error;
410 }
411 
412 
413 /**
414  * @brief Parse HTTP header field
415  * @param[in] connection Structure representing an HTTP connection
416  * @param[in] name Name of the header field
417  * @param[in] value Value of the header field
418  **/
419 
421  const char_t *name, char_t *value)
422 {
423  //Host header field?
424  if(!osStrcasecmp(name, "Host"))
425  {
426  //Save host name
427  strSafeCopy(connection->request.host, value,
429  }
430  //Connection header field?
431  else if(!osStrcasecmp(name, "Connection"))
432  {
433  //Parse Connection header field
434  httpParseConnectionField(connection, value);
435  }
436  //Transfer-Encoding header field?
437  else if(!osStrcasecmp(name, "Transfer-Encoding"))
438  {
439  //Check whether chunked encoding is used
440  if(!osStrcasecmp(value, "chunked"))
441  connection->request.chunkedEncoding = TRUE;
442  }
443  //Content-Type field header?
444  else if(!osStrcasecmp(name, "Content-Type"))
445  {
446  //Parse Content-Type header field
447  httpParseContentTypeField(connection, value);
448  }
449  //Content-Length header field?
450  else if(!osStrcasecmp(name, "Content-Length"))
451  {
452  //Get the length of the body data
453  connection->request.contentLength = atoi(value);
454  }
455  //Accept-Encoding field header?
456  else if(!osStrcasecmp(name, "Accept-Encoding"))
457  {
458  //Parse Content-Type header field
459  httpParseAcceptEncodingField(connection, value);
460  }
461  //Authorization header field?
462  else if(!osStrcasecmp(name, "Authorization"))
463  {
464  //Parse Authorization header field
465  httpParseAuthorizationField(connection, value);
466  }
467 #if (HTTP_SERVER_WEB_SOCKET_SUPPORT == ENABLED)
468  //Upgrade header field?
469  else if(!osStrcasecmp(name, "Upgrade"))
470  {
471  //WebSocket support?
472  if(!osStrcasecmp(value, "websocket"))
473  connection->request.upgradeWebSocket = TRUE;
474  }
475  //Sec-WebSocket-Key header field?
476  else if(!osStrcasecmp(name, "Sec-WebSocket-Key"))
477  {
478  //Save the contents of the Sec-WebSocket-Key header field
479  strSafeCopy(connection->request.clientKey, value,
481  }
482 #endif
483 #if (HTTP_SERVER_COOKIE_SUPPORT == ENABLED)
484  //Cookie header field?
485  else if(!osStrcasecmp(name, "Cookie"))
486  {
487  //Parse Cookie header field
488  httpParseCookieField(connection, value);
489  }
490 #endif
491 }
492 
493 
494 /**
495  * @brief Parse Connection header field
496  * @param[in] connection Structure representing an HTTP connection
497  * @param[in] value Connection field value
498  **/
499 
501  char_t *value)
502 {
503  char_t *p;
504  char_t *token;
505 
506  //Get the first value of the list
507  token = osStrtok_r(value, ",", &p);
508 
509  //Parse the comma-separated list
510  while(token != NULL)
511  {
512  //Trim whitespace characters
514 
515  //Check current value
516  if(!osStrcasecmp(value, "keep-alive"))
517  {
518  //The connection is persistent
519  connection->request.keepAlive = TRUE;
520  }
521  else if(!osStrcasecmp(value, "close"))
522  {
523  //The connection will be closed after completion of the response
524  connection->request.keepAlive = FALSE;
525  }
526 #if (HTTP_SERVER_WEB_SOCKET_SUPPORT == ENABLED)
527  else if(!osStrcasecmp(value, "upgrade"))
528  {
529  //Upgrade the connection
530  connection->request.connectionUpgrade = TRUE;
531  }
532 #endif
533 
534  //Get next value
535  token = osStrtok_r(NULL, ",", &p);
536  }
537 }
538 
539 
540 /**
541  * @brief Parse Content-Type header field
542  * @param[in] connection Structure representing an HTTP connection
543  * @param[in] value Content-Type field value
544  **/
545 
547  char_t *value)
548 {
549 #if (HTTP_SERVER_MULTIPART_TYPE_SUPPORT == ENABLED)
550  size_t n;
551  char_t *p;
552  char_t *token;
553 
554  //Retrieve type
555  token = osStrtok_r(value, "/", &p);
556  //Any parsing error?
557  if(token == NULL)
558  return;
559 
560  //The boundary parameter makes sense only for the multipart content-type
561  if(!osStrcasecmp(token, "multipart"))
562  {
563  //Skip subtype
564  token = osStrtok_r(NULL, ";", &p);
565  //Any parsing error?
566  if(token == NULL)
567  return;
568 
569  //Retrieve parameter name
570  token = osStrtok_r(NULL, "=", &p);
571  //Any parsing error?
572  if(token == NULL)
573  return;
574 
575  //Trim whitespace characters
577 
578  //Check parameter name
579  if(!osStrcasecmp(token, "boundary"))
580  {
581  //Retrieve parameter value
582  token = osStrtok_r(NULL, ";", &p);
583  //Any parsing error?
584  if(token == NULL)
585  return;
586 
587  //Trim whitespace characters
589  //Get the length of the boundary string
590  n = osStrlen(token);
591 
592  //Check the length of the boundary string
594  {
595  //Copy the boundary string
596  osStrncpy(connection->request.boundary, token, n);
597  //Properly terminate the string
598  connection->request.boundary[n] = '\0';
599 
600  //Save the length of the boundary string
601  connection->request.boundaryLength = n;
602  }
603  }
604  }
605 #endif
606 }
607 
608 
609 /**
610  * @brief Parse Accept-Encoding header field
611  * @param[in] connection Structure representing an HTTP connection
612  * @param[in] value Accept-Encoding field value
613  **/
614 
616  char_t *value)
617 {
618 #if (HTTP_SERVER_GZIP_TYPE_SUPPORT == ENABLED)
619  char_t *p;
620  char_t *token;
621 
622  //Get the first value of the list
623  token = osStrtok_r(value, ",", &p);
624 
625  //Parse the comma-separated list
626  while(token != NULL)
627  {
628  //Trim whitespace characters
630 
631  //Check current value
632  if(!osStrcasecmp(value, "gzip"))
633  {
634  //gzip compression is supported
635  connection->request.acceptGzipEncoding = TRUE;
636  }
637 
638  //Get next value
639  token = osStrtok_r(NULL, ",", &p);
640  }
641 #endif
642 }
643 
644 
645 /**
646  * @brief Parse Cookie header field
647  * @param[in] connection Structure representing an HTTP connection
648  * @param[in] value Accept-Encoding field value
649  **/
650 
652 {
653 #if (HTTP_SERVER_COOKIE_SUPPORT == ENABLED)
654  //Save the value of the header field
655  strSafeCopy(connection->request.cookie, value, HTTP_SERVER_COOKIE_MAX_LEN);
656 #endif
657 }
658 
659 
660 /**
661  * @brief Read chunk-size field from the input stream
662  * @param[in] connection Structure representing an HTTP connection
663  **/
664 
666 {
667  error_t error;
668  size_t n;
669  char_t *end;
670  char_t s[8];
671 
672  //First chunk to be received?
673  if(connection->request.firstChunk)
674  {
675  //Clear the flag
676  connection->request.firstChunk = FALSE;
677  }
678  else
679  {
680  //Read the CRLF that follows the previous chunk-data field
681  error = httpReceive(connection, s, sizeof(s) - 1, &n, SOCKET_FLAG_BREAK_CRLF);
682  //Any error to report?
683  if(error)
684  return error;
685 
686  //Properly terminate the string with a NULL character
687  s[n] = '\0';
688 
689  //The chunk data must be terminated by CRLF
690  if(osStrcmp(s, "\r\n"))
691  return ERROR_WRONG_ENCODING;
692  }
693 
694  //Read the chunk-size field
695  error = httpReceive(connection, s, sizeof(s) - 1, &n, SOCKET_FLAG_BREAK_CRLF);
696  //Any error to report?
697  if(error)
698  return error;
699 
700  //Properly terminate the string with a NULL character
701  s[n] = '\0';
702  //Remove extra whitespaces
704 
705  //Retrieve the size of the chunk
706  connection->request.byteCount = osStrtoul(s, &end, 16);
707 
708  //No valid conversion could be performed?
709  if(end == s || *end != '\0')
710  return ERROR_WRONG_ENCODING;
711 
712  //Any chunk whose size is zero terminates the data transfer
713  if(!connection->request.byteCount)
714  {
715  //The end of the HTTP request body has been reached
716  connection->request.lastChunk = TRUE;
717 
718  //Skip the trailer
719  while(1)
720  {
721  //Read a complete line
722  error = httpReceive(connection, s, sizeof(s) - 1, &n, SOCKET_FLAG_BREAK_CRLF);
723  //Unable to read any data?
724  if(error)
725  return error;
726 
727  //Properly terminate the string with a NULL character
728  s[n] = '\0';
729 
730  //The trailer is terminated by an empty line
731  if(!osStrcmp(s, "\r\n"))
732  break;
733  }
734  }
735 
736  //Successful processing
737  return NO_ERROR;
738 }
739 
740 
741 /**
742  * @brief Initialize response header
743  * @param[in] connection Structure representing an HTTP connection
744  **/
745 
747 {
748  //Default HTTP header fields
749  connection->response.version = connection->request.version;
750  connection->response.statusCode = 200;
751  connection->response.noCache = FALSE;
752  connection->response.maxAge = 0;
753  connection->response.location = NULL;
754  connection->response.contentType = mimeGetType(connection->request.uri);
755  connection->response.chunkedEncoding = TRUE;
756 
757 #if (HTTP_SERVER_GZIP_TYPE_SUPPORT == ENABLED)
758  //Do not use gzip encoding
759  connection->response.gzipEncoding = FALSE;
760 #endif
761 
762 #if (HTTP_SERVER_PERSISTENT_CONN_SUPPORT == ENABLED)
763  //Persistent connections are accepted
764  connection->response.keepAlive = connection->request.keepAlive;
765 #else
766  //Connections are not persistent by default
767  connection->response.keepAlive = FALSE;
768 #endif
769 }
770 
771 
772 /**
773  * @brief Format HTTP response header
774  * @param[in] connection Structure representing an HTTP connection
775  * @param[out] buffer Pointer to the buffer where to format the HTTP header
776  * @return Error code
777  **/
778 
780 {
781  uint_t i;
782  char_t *p;
783 
784  //HTTP version 0.9?
785  if(connection->response.version == HTTP_VERSION_0_9)
786  {
787  //Enforce default parameters
788  connection->response.keepAlive = FALSE;
789  connection->response.chunkedEncoding = FALSE;
790  //The size of the response body is not limited
791  connection->response.byteCount = UINT_MAX;
792  //We are done since HTTP 0.9 does not support Full-Response format
793  return NO_ERROR;
794  }
795 
796  //When generating dynamic web pages with HTTP 1.0, the only way to
797  //signal the end of the body is to close the connection
798  if(connection->response.version == HTTP_VERSION_1_0 &&
799  connection->response.chunkedEncoding)
800  {
801  //Make the connection non persistent
802  connection->response.keepAlive = FALSE;
803  connection->response.chunkedEncoding = FALSE;
804  //The size of the response body is not limited
805  connection->response.byteCount = UINT_MAX;
806  }
807  else
808  {
809  //Limit the size of the response body
810  connection->response.byteCount = connection->response.contentLength;
811  }
812 
813  //Point to the beginning of the buffer
814  p = buffer;
815 
816  //The first line of a response message is the Status-Line, consisting
817  //of the protocol version followed by a numeric status code and its
818  //associated textual phrase
819  p += osSprintf(p, "HTTP/%u.%u %u ", MSB(connection->response.version),
820  LSB(connection->response.version), connection->response.statusCode);
821 
822  //Retrieve the Reason-Phrase that corresponds to the Status-Code
823  for(i = 0; i < arraysize(statusCodeList); i++)
824  {
825  //Check the status code
826  if(statusCodeList[i].value == connection->response.statusCode)
827  {
828  //Append the textual phrase to the Status-Line
829  p += osSprintf(p, "%s", statusCodeList[i].message);
830  //Break the loop and continue processing
831  break;
832  }
833  }
834 
835  //Properly terminate the Status-Line
836  p += osSprintf(p, "\r\n");
837 
838  //Valid location?
839  if(connection->response.location != NULL)
840  {
841  //Set Location field
842  p += osSprintf(p, "Location: %s\r\n", connection->response.location);
843  }
844 
845  //Persistent connection?
846  if(connection->response.keepAlive)
847  {
848  //Set Connection field
849  p += osSprintf(p, "Connection: keep-alive\r\n");
850 
851  //Set Keep-Alive field
852  p += osSprintf(p, "Keep-Alive: timeout=%u, max=%u\r\n",
854  }
855  else
856  {
857  //Set Connection field
858  p += osSprintf(p, "Connection: close\r\n");
859  }
860 
861  //Specify the caching policy
862  if(connection->response.noCache)
863  {
864  //Set Pragma field
865  p += osSprintf(p, "Pragma: no-cache\r\n");
866  //Set Cache-Control field
867  p += osSprintf(p, "Cache-Control: no-store, no-cache, must-revalidate\r\n");
868  p += osSprintf(p, "Cache-Control: max-age=0, post-check=0, pre-check=0\r\n");
869  }
870  else if(connection->response.maxAge != 0)
871  {
872  //Set Cache-Control field
873  p += osSprintf(p, "Cache-Control: max-age=%u\r\n", connection->response.maxAge);
874  }
875 
876 #if (HTTP_SERVER_TLS_SUPPORT == ENABLED && HTTP_SERVER_HSTS_SUPPORT == ENABLED)
877  //TLS-secured connection?
878  if(connection->serverContext->settings.tlsInitCallback != NULL)
879  {
880  //Set Strict-Transport-Security field
881  p += osSprintf(p, "Strict-Transport-Security: max-age=31536000\r\n");
882  }
883 #endif
884 
885 #if (HTTP_SERVER_BASIC_AUTH_SUPPORT == ENABLED || HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED)
886  //Check whether authentication is required
887  if(connection->response.auth.mode != HTTP_AUTH_MODE_NONE)
888  {
889  //Add WWW-Authenticate header field
890  p += httpAddAuthenticateField(connection, p);
891  }
892 #endif
893 
894 #if (HTTP_SERVER_COOKIE_SUPPORT == ENABLED)
895  //Valid cookie
896  if(connection->response.setCookie[0] != '\0')
897  {
898  //Add Set-Cookie header field
899  p += osSprintf(p, "Set-Cookie: %s\r\n", connection->response.setCookie);
900  }
901 #endif
902 
903  //Valid content type?
904  if(connection->response.contentType != NULL)
905  {
906  //Content type
907  p += osSprintf(p, "Content-Type: %s\r\n", connection->response.contentType);
908  }
909 
910 #if (HTTP_SERVER_GZIP_TYPE_SUPPORT == ENABLED)
911  //Use gzip encoding?
912  if(connection->response.gzipEncoding)
913  {
914  //Set Transfer-Encoding field
915  p += osSprintf(p, "Content-Encoding: gzip\r\n");
916  }
917 #endif
918 
919  //Use chunked encoding transfer?
920  if(connection->response.chunkedEncoding)
921  {
922  //Set Transfer-Encoding field
923  p += osSprintf(p, "Transfer-Encoding: chunked\r\n");
924  }
925  //Persistent connection?
926  else if(connection->response.keepAlive)
927  {
928  //Set Content-Length field
929  p += osSprintf(p, "Content-Length: %" PRIuSIZE "\r\n", connection->response.contentLength);
930  }
931 
932  //Terminate the header with an empty line
933  p += osSprintf(p, "\r\n");
934 
935  //Successful processing
936  return NO_ERROR;
937 }
938 
939 
940 /**
941  * @brief Send data to the client
942  * @param[in] connection Structure representing an HTTP connection
943  * @param[in] data Pointer to a buffer containing the data to be transmitted
944  * @param[in] length Number of bytes to be transmitted
945  * @param[in] flags Set of flags that influences the behavior of this function
946  **/
947 
949  const void *data, size_t length, uint_t flags)
950 {
951 #if (NET_RTOS_SUPPORT == ENABLED)
952  error_t error;
953 
954 #if (HTTP_SERVER_TLS_SUPPORT == ENABLED)
955  //Check whether a secure connection is being used
956  if(connection->tlsContext != NULL)
957  {
958  //Use TLS to transmit data to the client
959  error = tlsWrite(connection->tlsContext, data, length, NULL, flags);
960  }
961  else
962 #endif
963  {
964  //Transmit data to the client
965  error = socketSend(connection->socket, data, length, NULL, flags);
966  }
967 
968  //Return status code
969  return error;
970 #else
971  //Prevent buffer overflow
972  if((connection->bufferLen + length) > HTTP_SERVER_BUFFER_SIZE)
973  return ERROR_BUFFER_OVERFLOW;
974 
975  //Copy user data
976  osMemcpy(connection->buffer + connection->bufferLen, data, length);
977  //Adjust the length of the buffer
978  connection->bufferLen += length;
979 
980  //Successful processing
981  return NO_ERROR;
982 #endif
983 }
984 
985 
986 /**
987  * @brief Receive data from the client
988  * @param[in] connection Structure representing an HTTP connection
989  * @param[out] data Buffer into which received data will be placed
990  * @param[in] size Maximum number of bytes that can be received
991  * @param[out] received Actual number of bytes that have been received
992  * @param[in] flags Set of flags that influences the behavior of this function
993  * @return Error code
994  **/
995 
997  void *data, size_t size, size_t *received, uint_t flags)
998 {
999 #if (NET_RTOS_SUPPORT == ENABLED)
1000  error_t error;
1001 
1002 #if (HTTP_SERVER_TLS_SUPPORT == ENABLED)
1003  //Check whether a secure connection is being used
1004  if(connection->tlsContext != NULL)
1005  {
1006  //Use TLS to receive data from the client
1007  error = tlsRead(connection->tlsContext, data, size, received, flags);
1008  }
1009  else
1010 #endif
1011  {
1012  //Receive data from the client
1013  error = socketReceive(connection->socket, data, size, received, flags);
1014  }
1015 
1016  //Return status code
1017  return error;
1018 #else
1019  error_t error;
1020  char_t c;
1021  size_t i;
1022  size_t n;
1023 
1024  //Number of data bytes that are pending in the receive buffer
1025  n = connection->bufferLen - connection->bufferPos;
1026 
1027  //Any data to be copied?
1028  if(n > 0)
1029  {
1030  //Limit the number of bytes to read at a time
1031  n = MIN(n, size);
1032 
1033  //The HTTP_FLAG_BREAK_CHAR flag causes the function to stop reading
1034  //data as soon as the specified break character is encountered
1035  if((flags & HTTP_FLAG_BREAK_CHAR) != 0)
1036  {
1037  //Retrieve the break character code
1038  c = LSB(flags);
1039 
1040  //Search for the specified break character
1041  for(i = 0; i < n && connection->buffer[connection->bufferPos + i] != c; i++)
1042  {
1043  }
1044 
1045  //Adjust the number of data to read
1046  n = MIN(n, i + 1);
1047  }
1048 
1049  //Copy data to user buffer
1050  osMemcpy(data, connection->buffer + connection->bufferPos, n);
1051 
1052  //Advance current position
1053  connection->bufferPos += n;
1054  //Total number of data that have been read
1055  *received = n;
1056 
1057  //Successful processing
1058  error = NO_ERROR;
1059  }
1060  else
1061  {
1062  //No more data available...
1063  error = ERROR_END_OF_STREAM;
1064  }
1065 
1066  //Return status code
1067  return error;
1068 #endif
1069 }
1070 
1071 
1072 /**
1073  * @brief Retrieve the full pathname to the specified resource
1074  * @param[in] connection Structure representing an HTTP connection
1075  * @param[in] relative String containing the relative path to the resource
1076  * @param[out] absolute Resulting string containing the absolute path
1077  * @param[in] maxLen Maximum acceptable path length
1078  **/
1079 
1081  const char_t *relative, char_t *absolute, size_t maxLen)
1082 {
1083  //Copy the root directory
1084  osStrcpy(absolute, connection->settings->rootDirectory);
1085 
1086  //Append the specified path
1087  pathCombine(absolute, relative, maxLen);
1088 
1089  //Clean the resulting path
1090  pathCanonicalize(absolute);
1091 }
1092 
1093 
1094 /**
1095  * @brief Compare filename extension
1096  * @param[in] filename Filename whose extension is to be checked
1097  * @param[in] extension String defining the extension to be checked
1098  * @return TRUE is the filename matches the given extension, else FALSE
1099  **/
1100 
1101 bool_t httpCompExtension(const char_t *filename, const char_t *extension)
1102 {
1103  uint_t n;
1104  uint_t m;
1105 
1106  //Get the length of the specified filename
1107  n = osStrlen(filename);
1108  //Get the length of the extension
1109  m = osStrlen(extension);
1110 
1111  //Check the length of the filename
1112  if(n < m)
1113  return FALSE;
1114 
1115  //Compare extensions
1116  if(!osStrncasecmp(filename + n - m, extension, m))
1117  {
1118  return TRUE;
1119  }
1120  else
1121  {
1122  return FALSE;
1123  }
1124 }
1125 
1126 
1127 /**
1128  * @brief Decode a percent-encoded string
1129  * @param[in] input NULL-terminated string to be decoded
1130  * @param[out] output NULL-terminated string resulting from the decoding process
1131  * @param[in] outputSize Size of the output buffer in bytes
1132  * @return Error code
1133  **/
1134 
1136  char_t *output, size_t outputSize)
1137 {
1138  size_t i;
1139  char_t buffer[3];
1140 
1141  //Check parameters
1142  if(input == NULL || output == NULL)
1143  return ERROR_INVALID_PARAMETER;
1144 
1145  //Decode the percent-encoded string
1146  for(i = 0; *input != '\0' && i < outputSize; i++)
1147  {
1148  //Check current character
1149  if(*input == '+')
1150  {
1151  //Replace '+' characters with spaces
1152  output[i] = ' ';
1153  //Advance data pointer
1154  input++;
1155  }
1156  else if(input[0] == '%' && input[1] != '\0' && input[2] != '\0')
1157  {
1158  //Process percent-encoded characters
1159  buffer[0] = input[1];
1160  buffer[1] = input[2];
1161  buffer[2] = '\0';
1162  //String to integer conversion
1163  output[i] = (uint8_t) osStrtoul(buffer, NULL, 16);
1164  //Advance data pointer
1165  input += 3;
1166  }
1167  else
1168  {
1169  //Copy any other characters
1170  output[i] = *input;
1171  //Advance data pointer
1172  input++;
1173  }
1174  }
1175 
1176  //Check whether the output buffer runs out of space
1177  if(i >= outputSize)
1178  return ERROR_FAILURE;
1179 
1180  //Properly terminate the resulting string
1181  output[i] = '\0';
1182  //Successful processing
1183  return NO_ERROR;
1184 }
1185 
1186 
1187 /**
1188  * @brief Convert byte array to hex string
1189  * @param[in] input Point to the byte array
1190  * @param[in] inputLen Length of the byte array
1191  * @param[out] output NULL-terminated string resulting from the conversion
1192  **/
1193 
1194 void httpConvertArrayToHexString(const uint8_t *input,
1195  size_t inputLen, char_t *output)
1196 {
1197  size_t i;
1198 
1199  //Hex conversion table
1200  static const char_t hexDigit[16] =
1201  {
1202  '0', '1', '2', '3', '4', '5', '6', '7',
1203  '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
1204  };
1205 
1206  //Process byte array
1207  for(i = 0; i < inputLen; i++)
1208  {
1209  //Convert upper nibble
1210  output[i * 2] = hexDigit[(input[i] >> 4) & 0x0F];
1211  //Then convert lower nibble
1212  output[i * 2 + 1] = hexDigit[input[i] & 0x0F];
1213  }
1214 
1215  //Properly terminate the string with a NULL character
1216  output[i * 2] = '\0';
1217 }
1218 
1219 #endif
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
#define TRACE_INFO(...)
Definition: debug.h:95
uint8_t n
error_t
Error codes.
Definition: error.h:43
@ ERROR_WRONG_ENCODING
Definition: error.h:122
@ ERROR_INVALID_REQUEST
Definition: error.h:65
@ ERROR_END_OF_STREAM
Definition: error.h:210
@ NO_ERROR
Success.
Definition: error.h:44
@ ERROR_BUFFER_OVERFLOW
Definition: error.h:142
@ ERROR_FAILURE
Generic error code.
Definition: error.h:45
@ ERROR_INVALID_PARAMETER
Invalid parameter.
Definition: error.h:47
uint8_t data[]
Definition: ethernet.h:222
@ HTTP_FLAG_BREAK_CHAR
Definition: http_common.h:98
@ HTTP_VERSION_1_0
Definition: http_common.h:62
@ HTTP_VERSION_0_9
Definition: http_common.h:61
@ HTTP_VERSION_1_1
Definition: http_common.h:63
@ HTTP_AUTH_MODE_NONE
Definition: http_common.h:73
HTTP server (HyperText Transfer Protocol)
#define HTTP_SERVER_BOUNDARY_MAX_LEN
Definition: http_server.h:272
#define HTTP_SERVER_METHOD_MAX_LEN
Definition: http_server.h:195
#define HttpConnection
Definition: http_server.h:332
#define HTTP_SERVER_HOST_MAX_LEN
Definition: http_server.h:216
#define HTTP_SERVER_BUFFER_SIZE
Definition: http_server.h:174
#define HTTP_SERVER_MAX_REQUESTS
Definition: http_server.h:167
#define HTTP_SERVER_URI_MAX_LEN
Definition: http_server.h:202
#define HTTP_SERVER_IDLE_TIMEOUT
Definition: http_server.h:153
#define HTTP_SERVER_TIMEOUT
Definition: http_server.h:145
#define HTTP_SERVER_COOKIE_MAX_LEN
Definition: http_server.h:279
#define HTTP_SERVER_QUERY_STRING_MAX_LEN
Definition: http_server.h:209
void httpParseAuthorizationField(HttpConnection *connection, char_t *value)
Parse Authorization header field.
size_t httpAddAuthenticateField(HttpConnection *connection, char_t *output)
Format WWW-Authenticate header field.
HTTP authentication.
error_t httpReceive(HttpConnection *connection, void *data, size_t size, size_t *received, uint_t flags)
Receive data from the client.
void httpParseHeaderField(HttpConnection *connection, const char_t *name, char_t *value)
Parse HTTP header field.
void httpGetAbsolutePath(HttpConnection *connection, const char_t *relative, char_t *absolute, size_t maxLen)
Retrieve the full pathname to the specified resource.
void httpConvertArrayToHexString(const uint8_t *input, size_t inputLen, char_t *output)
Convert byte array to hex string.
error_t httpFormatResponseHeader(HttpConnection *connection, char_t *buffer)
Format HTTP response header.
error_t httpSend(HttpConnection *connection, const void *data, size_t length, uint_t flags)
Send data to the client.
void httpParseAcceptEncodingField(HttpConnection *connection, char_t *value)
Parse Accept-Encoding header field.
error_t httpParseRequestLine(HttpConnection *connection, char_t *requestLine)
Parse Request-Line.
void httpParseCookieField(HttpConnection *connection, char_t *value)
Parse Cookie header field.
error_t httpReadHeaderField(HttpConnection *connection, char_t *buffer, size_t size, char_t *firstChar)
Read multiple-line header field.
void httpInitResponseHeader(HttpConnection *connection)
Initialize response header.
void httpParseContentTypeField(HttpConnection *connection, char_t *value)
Parse Content-Type header field.
void httpParseConnectionField(HttpConnection *connection, char_t *value)
Parse Connection header field.
error_t httpReadChunkSize(HttpConnection *connection)
Read chunk-size field from the input stream.
error_t httpDecodePercentEncodedString(const char_t *input, char_t *output, size_t outputSize)
Decode a percent-encoded string.
error_t httpReadRequestHeader(HttpConnection *connection)
Read HTTP request header and parse its contents.
bool_t httpCompExtension(const char_t *filename, const char_t *extension)
Compare filename extension.
HTTP server (miscellaneous functions)
const char_t * mimeGetType(const char_t *filename)
Get the MIME type from a given extension.
Definition: mime.c:113
MIME (Multipurpose Internet Mail Extensions)
uint8_t c
Definition: ndp.h:514
uint8_t s
Definition: ndp.h:345
uint8_t p
Definition: ndp.h:300
uint8_t m
Definition: ndp.h:304
TCP/IP stack core.
#define osStrcmp(s1, s2)
Definition: os_port.h:171
#define osMemcpy(dest, src, length)
Definition: os_port.h:141
#define osStrcasecmp(s1, s2)
Definition: os_port.h:183
#define LSB(x)
Definition: os_port.h:55
#define MIN(a, b)
Definition: os_port.h:63
#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 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 osStrncasecmp(s1, s2, length)
Definition: os_port.h:189
#define MSB(x)
Definition: os_port.h:59
#define osStrncpy(s1, s2, length)
Definition: os_port.h:213
#define osStrtok_r(s, delim, last)
Definition: os_port.h:225
void pathCombine(char_t *path, const char_t *more, size_t maxLen)
Concatenate two paths.
Definition: path.c:370
void pathCanonicalize(char_t *path)
Simplify a path.
Definition: path.c:150
Path manipulation helper functions.
char_t name[]
error_t socketReceive(Socket *socket, void *data, size_t size, size_t *received, uint_t flags)
Receive data from a connected socket.
Definition: socket.c:1152
error_t socketSend(Socket *socket, const void *data, size_t length, size_t *written, uint_t flags)
Send data to a connected socket.
Definition: socket.c:946
error_t socketSetTimeout(Socket *socket, systime_t timeout)
Set timeout value for blocking operations.
Definition: socket.c:148
@ SOCKET_FLAG_WAIT_ALL
Definition: socket.h:128
@ SOCKET_FLAG_BREAK_CRLF
Definition: socket.h:131
void strRemoveTrailingSpace(char_t *s)
Removes all trailing whitespace from a string.
Definition: str.c:115
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.
HTTP status code.
Definition: http_server.h:432
uint8_t length
Definition: tcp.h:368
uint8_t value[]
Definition: tcp.h:369
uint8_t flags
Definition: tcp.h:351
char_t filename[]
Definition: tftp_common.h:93
error_t tlsWrite(TlsContext *context, const void *data, size_t length, size_t *written, uint_t flags)
Send application data to the remote host using TLS.
Definition: tls.c:1849
error_t tlsRead(TlsContext *context, void *data, size_t size, size_t *received, uint_t flags)
Receive application data from a the remote host using TLS.
Definition: tls.c:1984
#define WEB_SOCKET_CLIENT_KEY_SIZE
Definition: web_socket.h:171