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