http_server.c
Go to the documentation of this file.
1 /**
2  * @file http_server.c
3  * @brief HTTP server (HyperText Transfer Protocol)
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  * @section Description
28  *
29  * Using the HyperText Transfer Protocol, the HTTP server delivers web pages
30  * to browsers as well as other data files to web-based applications. Refers
31  * to the following RFCs for complete details:
32  * - RFC 1945: Hypertext Transfer Protocol - HTTP/1.0
33  * - RFC 2616: Hypertext Transfer Protocol - HTTP/1.1
34  * - RFC 2617: HTTP Authentication: Basic and Digest Access Authentication
35  * - RFC 2818: HTTP Over TLS
36  *
37  * @author Oryx Embedded SARL (www.oryx-embedded.com)
38  * @version 1.9.6
39  **/
40 
41 //Switch to the appropriate trace level
42 #define TRACE_LEVEL HTTP_TRACE_LEVEL
43 
44 //Dependencies
45 #include <stdlib.h>
46 #include "core/net.h"
47 #include "http/http_server.h"
48 #include "http/http_server_auth.h"
49 #include "http/http_server_misc.h"
50 #include "http/mime.h"
51 #include "http/ssi.h"
52 #include "debug.h"
53 
54 //Check TCP/IP stack configuration
55 #if (HTTP_SERVER_SUPPORT == ENABLED)
56 
57 
58 /**
59  * @brief Initialize settings with default values
60  * @param[out] settings Structure that contains HTTP server settings
61  **/
62 
64 {
65  //The HTTP server is not bound to any interface
66  settings->interface = NULL;
67 
68  //Listen to port 80
69  settings->port = HTTP_PORT;
70  //Maximum length of the pending connection queue
71  settings->backlog = HTTP_SERVER_BACKLOG;
72 
73  //Client connections
74  settings->maxConnections = 0;
75  settings->connections = NULL;
76 
77  //Specify the server's root directory
78  strcpy(settings->rootDirectory, "/");
79  //Set default home page
80  strcpy(settings->defaultDocument, "index.htm");
81 
82 #if (HTTP_SERVER_TLS_SUPPORT == ENABLED)
83  //TLS initialization callback function
84  settings->tlsInitCallback = NULL;
85 #endif
86 
87 #if (HTTP_SERVER_BASIC_AUTH_SUPPORT == ENABLED || HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED)
88  //Random data generation callback function
89  settings->randCallback = NULL;
90  //HTTP authentication callback function
91  settings->authCallback = NULL;
92 #endif
93 
94  //CGI callback function
95  settings->cgiCallback = NULL;
96  //HTTP request callback function
97  settings->requestCallback = NULL;
98  //URI not found callback function
99  settings->uriNotFoundCallback = NULL;
100 }
101 
102 
103 /**
104  * @brief HTTP server initialization
105  * @param[in] context Pointer to the HTTP server context
106  * @param[in] settings HTTP server specific settings
107  * @return Error code
108  **/
109 
111 {
112  error_t error;
113  uint_t i;
114  HttpConnection *connection;
115 
116  //Debug message
117  TRACE_INFO("Initializing HTTP server...\r\n");
118 
119  //Ensure the parameters are valid
120  if(context == NULL || settings == NULL)
122 
123  //Check settings
124  if(settings->maxConnections == 0 || settings->connections == NULL)
126 
127  //Clear the HTTP server context
128  memset(context, 0, sizeof(HttpServerContext));
129 
130  //Save user settings
131  context->settings = *settings;
132  //Client connections
133  context->connections = settings->connections;
134 
135  //Create a semaphore to limit the number of simultaneous connections
136  if(!osCreateSemaphore(&context->semaphore, context->settings.maxConnections))
137  return ERROR_OUT_OF_RESOURCES;
138 
139  //Loop through client connections
140  for(i = 0; i < context->settings.maxConnections; i++)
141  {
142  //Point to the structure representing the client connection
143  connection = &context->connections[i];
144 
145  //Initialize the structure
146  memset(connection, 0, sizeof(HttpConnection));
147 
148  //Create an event object to manage connection lifetime
149  if(!osCreateEvent(&connection->startEvent))
150  return ERROR_OUT_OF_RESOURCES;
151  }
152 
153 #if (HTTP_SERVER_TLS_SUPPORT == ENABLED && TLS_TICKET_SUPPORT == ENABLED)
154  //Initialize ticket encryption context
155  error = tlsInitTicketContext(&context->tlsTicketContext);
156  //Any error to report?
157  if(error)
158  return error;
159 #endif
160 
161 #if (HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED)
162  //Create a mutex to prevent simultaneous access to the nonce cache
163  if(!osCreateMutex(&context->nonceCacheMutex))
164  return ERROR_OUT_OF_RESOURCES;
165 #endif
166 
167  //Open a TCP socket
169  //Failed to open socket?
170  if(context->socket == NULL)
171  return ERROR_OPEN_FAILED;
172 
173  //Set timeout for blocking functions
174  error = socketSetTimeout(context->socket, INFINITE_DELAY);
175  //Any error to report?
176  if(error)
177  return error;
178 
179  //Associate the socket with the relevant interface
180  error = socketBindToInterface(context->socket, settings->interface);
181  //Unable to bind the socket to the desired interface?
182  if(error)
183  return error;
184 
185  //Bind newly created socket to port 80
186  error = socketBind(context->socket, &IP_ADDR_ANY, settings->port);
187  //Failed to bind socket to port 80?
188  if(error)
189  return error;
190 
191  //Place socket in listening state
192  error = socketListen(context->socket, settings->backlog);
193  //Any failure to report?
194  if(error)
195  return error;
196 
197  //Successful initialization
198  return NO_ERROR;
199 }
200 
201 
202 /**
203  * @brief Start HTTP server
204  * @param[in] context Pointer to the HTTP server context
205  * @return Error code
206  **/
207 
209 {
210  uint_t i;
211 
212  //Debug message
213  TRACE_INFO("Starting HTTP server...\r\n");
214 
215  //Make sure the HTTP server context is valid
216  if(context == NULL)
218 
219  //Loop through client connections
220  for(i = 0; i < context->settings.maxConnections; i++)
221  {
222  //Create a task to service a given HTTP client connection
223  context->connections[i].taskHandle = osCreateTask("HTTP Connection",
224  httpConnectionTask, &context->connections[i],
226 
227  //Unable to create the task?
228  if(context->connections[i].taskHandle == OS_INVALID_HANDLE)
229  return ERROR_OUT_OF_RESOURCES;
230  }
231 
232  //Create the HTTP server listener task
233  context->taskHandle = osCreateTask("HTTP Listener", httpListenerTask,
235 
236  //Unable to create the task?
237  if(context->taskHandle == OS_INVALID_HANDLE)
238  return ERROR_OUT_OF_RESOURCES;
239 
240  //The HTTP server has successfully started
241  return NO_ERROR;
242 }
243 
244 
245 /**
246  * @brief HTTP server listener task
247  * @param[in] param Pointer to the HTTP server context
248  **/
249 
250 void httpListenerTask(void *param)
251 {
252  uint_t i;
253  uint_t counter;
254  uint16_t clientPort;
255  IpAddr clientIpAddr;
256  HttpServerContext *context;
257  HttpConnection *connection;
258  Socket *socket;
259 
260  //Task prologue
261  osEnterTask();
262 
263  //Retrieve the HTTP server context
264  context = (HttpServerContext *) param;
265 
266  //Process incoming connections to the server
267  for(counter = 1; ; counter++)
268  {
269  //Debug message
270  TRACE_INFO("Ready to accept a new connection...\r\n");
271 
272  //Limit the number of simultaneous connections to the HTTP server
273  osWaitForSemaphore(&context->semaphore, INFINITE_DELAY);
274 
275  //Loop through the connection table
276  for(i = 0; i < context->settings.maxConnections; i++)
277  {
278  //Point to the current connection
279  connection = &context->connections[i];
280 
281  //Ready to service the client request?
282  if(!connection->running)
283  {
284  //Accept an incoming connection
285  socket = socketAccept(context->socket, &clientIpAddr, &clientPort);
286 
287  //Make sure the socket handle is valid
288  if(socket != NULL)
289  {
290  //Debug message
291  TRACE_INFO("Connection #%u established with client %s port %" PRIu16 "...\r\n",
292  counter, ipAddrToString(&clientIpAddr, NULL), clientPort);
293 
294  //Reference to the HTTP server settings
295  connection->settings = &context->settings;
296  //Reference to the HTTP server context
297  connection->serverContext = context;
298  //Reference to the new socket
299  connection->socket = socket;
300 
301  //Set timeout for blocking functions
302  socketSetTimeout(connection->socket, HTTP_SERVER_TIMEOUT);
303 
304  //The client connection task is now running...
305  connection->running = TRUE;
306  //Service the current connection request
307  osSetEvent(&connection->startEvent);
308 
309  //We are done
310  break;
311  }
312  }
313  }
314  }
315 }
316 
317 
318 /**
319  * @brief Task that services requests from an active connection
320  * @param[in] param Structure representing an HTTP connection with a client
321  **/
322 
323 void httpConnectionTask(void *param)
324 {
325  error_t error;
326  uint_t counter;
327  HttpConnection *connection;
328 
329  //Task prologue
330  osEnterTask();
331 
332  //Point to the structure representing the HTTP connection
333  connection = (HttpConnection *) param;
334 
335  //Endless loop
336  while(1)
337  {
338  //Wait for an incoming connection attempt
339  osWaitForEvent(&connection->startEvent, INFINITE_DELAY);
340 
341  //Initialize status code
342  error = NO_ERROR;
343 
344 #if (HTTP_SERVER_TLS_SUPPORT == ENABLED)
345  //TLS-secured connection?
346  if(connection->settings->tlsInitCallback != NULL)
347  {
348  //Debug message
349  TRACE_INFO("Initializing TLS session...\r\n");
350 
351  //Start of exception handling block
352  do
353  {
354  //Allocate TLS context
355  connection->tlsContext = tlsInit();
356  //Initialization failed?
357  if(connection->tlsContext == NULL)
358  {
359  //Report an error
360  error = ERROR_OUT_OF_MEMORY;
361  //Exit immediately
362  break;
363  }
364 
365  //Select server operation mode
366  error = tlsSetConnectionEnd(connection->tlsContext,
368  //Any error to report?
369  if(error)
370  break;
371 
372  //Bind TLS to the relevant socket
373  error = tlsSetSocket(connection->tlsContext, connection->socket);
374  //Any error to report?
375  if(error)
376  break;
377 
378 #if (TLS_TICKET_SUPPORT == ENABLED)
379  //Enable session ticket mechanism
380  error = tlsSetTicketCallbacks(connection->tlsContext, tlsEncryptTicket,
381  tlsDecryptTicket, &connection->serverContext->tlsTicketContext);
382  //Any error to report?
383  if(error)
384  break;
385 #endif
386  //Invoke user-defined callback, if any
387  if(connection->settings->tlsInitCallback != NULL)
388  {
389  //Perform TLS related initialization
390  error = connection->settings->tlsInitCallback(connection,
391  connection->tlsContext);
392  //Any error to report?
393  if(error)
394  break;
395  }
396 
397  //Establish a secure session
398  error = tlsConnect(connection->tlsContext);
399  //Any error to report?
400  if(error)
401  break;
402 
403  //End of exception handling block
404  } while(0);
405  }
406  else
407  {
408  //Do not use TLS
409  connection->tlsContext = NULL;
410  }
411 #endif
412 
413  //Check status code
414  if(!error)
415  {
416  //Process incoming requests
417  for(counter = 0; counter < HTTP_SERVER_MAX_REQUESTS; counter++)
418  {
419  //Debug message
420  TRACE_INFO("Waiting for request...\r\n");
421 
422  //Clear request header
423  memset(&connection->request, 0, sizeof(HttpRequest));
424  //Clear response header
425  memset(&connection->response, 0, sizeof(HttpResponse));
426 
427  //Read the HTTP request header and parse its contents
428  error = httpReadRequestHeader(connection);
429  //Any error to report?
430  if(error)
431  {
432  //Debug message
433  TRACE_INFO("No HTTP request received or parsing error...\r\n");
434  break;
435  }
436 
437 #if (HTTP_SERVER_BASIC_AUTH_SUPPORT == ENABLED || HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED)
438  //No Authorization header found?
439  if(!connection->request.auth.found)
440  {
441  //Invoke user-defined callback, if any
442  if(connection->settings->authCallback != NULL)
443  {
444  //Check whether the access to the specified URI is authorized
445  connection->status = connection->settings->authCallback(connection,
446  connection->request.auth.user, connection->request.uri);
447  }
448  else
449  {
450  //Access to the specified URI is allowed
451  connection->status = HTTP_ACCESS_ALLOWED;
452  }
453  }
454 
455  //Check access status
456  if(connection->status == HTTP_ACCESS_ALLOWED)
457  {
458  //Access to the specified URI is allowed
459  error = NO_ERROR;
460  }
461  else if(connection->status == HTTP_ACCESS_BASIC_AUTH_REQUIRED)
462  {
463  //Basic access authentication is required
464  connection->response.auth.mode = HTTP_AUTH_MODE_BASIC;
465  //Report an error
466  error = ERROR_AUTH_REQUIRED;
467  }
468  else if(connection->status == HTTP_ACCESS_DIGEST_AUTH_REQUIRED)
469  {
470  //Digest access authentication is required
471  connection->response.auth.mode = HTTP_AUTH_MODE_DIGEST;
472  //Report an error
473  error = ERROR_AUTH_REQUIRED;
474  }
475  else
476  {
477  //Access to the specified URI is denied
478  error = ERROR_NOT_FOUND;
479  }
480 #endif
481  //Debug message
482  TRACE_INFO("Sending HTTP response to the client...\r\n");
483 
484  //Check status code
485  if(!error)
486  {
487  //Default HTTP header fields
488  httpInitResponseHeader(connection);
489 
490  //Invoke user-defined callback, if any
491  if(connection->settings->requestCallback != NULL)
492  {
493  error = connection->settings->requestCallback(connection,
494  connection->request.uri);
495  }
496  else
497  {
498  //Keep processing...
499  error = ERROR_NOT_FOUND;
500  }
501 
502  //Check status code
503  if(error == ERROR_NOT_FOUND)
504  {
505 #if (HTTP_SERVER_SSI_SUPPORT == ENABLED)
506  //Use server-side scripting to dynamically generate HTML code?
507  if(httpCompExtension(connection->request.uri, ".stm") ||
508  httpCompExtension(connection->request.uri, ".shtm") ||
509  httpCompExtension(connection->request.uri, ".shtml"))
510  {
511  //SSI processing (Server Side Includes)
512  error = ssiExecuteScript(connection, connection->request.uri, 0);
513  }
514  else
515 #endif
516  {
517  //Set the maximum age for static resources
518  connection->response.maxAge = HTTP_SERVER_MAX_AGE;
519 
520  //Send the contents of the requested page
521  error = httpSendResponse(connection, connection->request.uri);
522  }
523  }
524 
525  //The requested resource is not available?
526  if(error == ERROR_NOT_FOUND)
527  {
528  //Default HTTP header fields
529  httpInitResponseHeader(connection);
530 
531  //Invoke user-defined callback, if any
532  if(connection->settings->uriNotFoundCallback != NULL)
533  {
534  error = connection->settings->uriNotFoundCallback(connection,
535  connection->request.uri);
536  }
537  }
538  }
539 
540  //Check status code
541  if(error)
542  {
543  //Default HTTP header fields
544  httpInitResponseHeader(connection);
545 
546  //Bad request?
547  if(error == ERROR_INVALID_REQUEST)
548  {
549  //Send an error 400 and close the connection immediately
550  httpSendErrorResponse(connection, 400,
551  "The request is badly formed");
552  }
553  //Authorization required?
554  else if(error == ERROR_AUTH_REQUIRED)
555  {
556  //Send an error 401 and keep the connection alive
557  error = httpSendErrorResponse(connection, 401,
558  "Authorization required");
559  }
560  //Page not found?
561  else if(error == ERROR_NOT_FOUND)
562  {
563  //Send an error 404 and keep the connection alive
564  error = httpSendErrorResponse(connection, 404,
565  "The requested page could not be found");
566  }
567  }
568 
569  //Internal error?
570  if(error)
571  {
572  //Close the connection immediately
573  break;
574  }
575 
576  //Check whether the connection is persistent or not
577  if(!connection->request.keepAlive || !connection->response.keepAlive)
578  {
579  //Close the connection immediately
580  break;
581  }
582  }
583  }
584 
585 #if (HTTP_SERVER_TLS_SUPPORT == ENABLED)
586  //Valid TLS context?
587  if(connection->tlsContext != NULL)
588  {
589  //Debug message
590  TRACE_INFO("Closing TLS session...\r\n");
591 
592  //Gracefully close TLS session
593  tlsShutdown(connection->tlsContext);
594  //Release context
595  tlsFree(connection->tlsContext);
596  }
597 #endif
598 
599  //Valid socket handle?
600  if(connection->socket != NULL)
601  {
602  //Debug message
603  TRACE_INFO("Graceful shutdown...\r\n");
604  //Graceful shutdown
605  socketShutdown(connection->socket, SOCKET_SD_BOTH);
606 
607  //Debug message
608  TRACE_INFO("Closing socket...\r\n");
609  //Close socket
610  socketClose(connection->socket);
611  }
612 
613  //Ready to serve the next connection request...
614  connection->running = FALSE;
615  //Release semaphore
616  osReleaseSemaphore(&connection->serverContext->semaphore);
617  }
618 }
619 
620 
621 /**
622  * @brief Send HTTP response header
623  * @param[in] connection Structure representing an HTTP connection
624  * @return Error code
625  **/
626 
628 {
629  error_t error;
630 
631  //Format HTTP response header
632  error = httpFormatResponseHeader(connection, connection->buffer);
633 
634  //Check status code
635  if(!error)
636  {
637  //Debug message
638  TRACE_DEBUG("HTTP response header:\r\n%s", connection->buffer);
639 
640  //Send HTTP response header to the client
641  error = httpSend(connection, connection->buffer,
642  strlen(connection->buffer), HTTP_FLAG_DELAY);
643  }
644 
645  //Return status code
646  return error;
647 }
648 
649 
650 /**
651  * @brief Read data from client request
652  * @param[in] connection Structure representing an HTTP connection
653  * @param[out] data Buffer where to store the incoming data
654  * @param[in] size Maximum number of bytes that can be received
655  * @param[out] received Number of bytes that have been received
656  * @param[in] flags Set of flags that influences the behavior of this function
657  * @return Error code
658  **/
659 
661  void *data, size_t size, size_t *received, uint_t flags)
662 {
663  error_t error;
664  size_t n;
665 
666  //No data has been read yet
667  *received = 0;
668 
669  //Chunked encoding transfer is used?
670  if(connection->request.chunkedEncoding)
671  {
672  //Point to the output buffer
673  char_t *p = data;
674 
675  //Read as much data as possible
676  while(*received < size)
677  {
678  //End of HTTP request body?
679  if(connection->request.lastChunk)
680  return ERROR_END_OF_STREAM;
681 
682  //Acquire a new chunk when the current chunk
683  //has been completely consumed
684  if(connection->request.byteCount == 0)
685  {
686  //The size of each chunk is sent right before the chunk itself
687  error = httpReadChunkSize(connection);
688  //Failed to decode the chunk-size field?
689  if(error)
690  return error;
691 
692  //Any chunk whose size is zero terminates the data transfer
693  if(!connection->request.byteCount)
694  {
695  //The user must be satisfied with data already on hand
696  return (*received > 0) ? NO_ERROR : ERROR_END_OF_STREAM;
697  }
698  }
699 
700  //Limit the number of bytes to read at a time
701  n = MIN(size - *received, connection->request.byteCount);
702 
703  //Read data
704  error = httpReceive(connection, p, n, &n, flags);
705  //Any error to report?
706  if(error)
707  return error;
708 
709  //Total number of data that have been read
710  *received += n;
711  //Number of bytes left to process in the current chunk
712  connection->request.byteCount -= n;
713 
714  //The HTTP_FLAG_BREAK_CHAR flag causes the function to stop reading
715  //data as soon as the specified break character is encountered
717  {
718  //Check whether a break character has been received
719  if(p[n - 1] == LSB(flags))
720  break;
721  }
722  //The HTTP_FLAG_WAIT_ALL flag causes the function to return
723  //only when the requested number of bytes have been read
724  else if(!(flags & HTTP_FLAG_WAIT_ALL))
725  {
726  break;
727  }
728 
729  //Advance data pointer
730  p += n;
731  }
732  }
733  //Default encoding?
734  else
735  {
736  //Return immediately if the end of the request body has been reached
737  if(!connection->request.byteCount)
738  return ERROR_END_OF_STREAM;
739 
740  //Limit the number of bytes to read
741  n = MIN(size, connection->request.byteCount);
742 
743  //Read data
744  error = httpReceive(connection, data, n, received, flags);
745  //Any error to report?
746  if(error)
747  return error;
748 
749  //Decrement the count of remaining bytes to read
750  connection->request.byteCount -= *received;
751  }
752 
753  //Successful read operation
754  return NO_ERROR;
755 }
756 
757 
758 /**
759  * @brief Write data to the client
760  * @param[in] connection Structure representing an HTTP connection
761  * @param[in] data Buffer containing the data to be transmitted
762  * @param[in] length Number of bytes to be transmitted
763  * @return Error code
764  **/
765 
767  const void *data, size_t length)
768 {
769  error_t error;
770  uint_t n;
771 
772  //Use chunked encoding transfer?
773  if(connection->response.chunkedEncoding)
774  {
775  //Any data to send?
776  if(length > 0)
777  {
778  char_t s[8];
779 
780  //The chunk-size field is a string of hex digits
781  //indicating the size of the chunk
782  n = sprintf(s, "%X\r\n", length);
783 
784  //Send the chunk-size field
785  error = httpSend(connection, s, n, HTTP_FLAG_DELAY);
786  //Failed to send data?
787  if(error)
788  return error;
789 
790  //Send the chunk-data
791  error = httpSend(connection, data, length, HTTP_FLAG_DELAY);
792  //Failed to send data?
793  if(error)
794  return error;
795 
796  //Terminate the chunk-data by CRLF
797  error = httpSend(connection, "\r\n", 2, HTTP_FLAG_DELAY);
798  }
799  else
800  {
801  //Any chunk whose size is zero may terminate the data
802  //transfer and must be discarded
803  error = NO_ERROR;
804  }
805  }
806  //Default encoding?
807  else
808  {
809  //The length of the body shall not exceed the value
810  //specified in the Content-Length field
811  length = MIN(length, connection->response.byteCount);
812 
813  //Send user data
814  error = httpSend(connection, data, length, HTTP_FLAG_DELAY);
815 
816  //Decrement the count of remaining bytes to be transferred
817  connection->response.byteCount -= length;
818  }
819 
820  //Return status code
821  return error;
822 }
823 
824 
825 /**
826  * @brief Close output stream
827  * @param[in] connection Structure representing an HTTP connection
828  * @return Error code
829  **/
830 
832 {
833  error_t error;
834 
835  //Use chunked encoding transfer?
836  if(connection->response.chunkedEncoding)
837  {
838  //The chunked encoding is ended by any chunk whose size is zero
839  error = httpSend(connection, "0\r\n\r\n", 5, HTTP_FLAG_NO_DELAY);
840  }
841  else
842  {
843  //Flush the send buffer
844  error = httpSend(connection, "", 0, HTTP_FLAG_NO_DELAY);
845  }
846 
847  //Return status code
848  return error;
849 }
850 
851 
852 /**
853  * @brief Send HTTP response
854  * @param[in] connection Structure representing an HTTP connection
855  * @param[in] uri NULL-terminated string containing the file to be sent in response
856  * @return Error code
857  **/
858 
860 {
861 #if (HTTP_SERVER_FS_SUPPORT == ENABLED)
862  error_t error;
863  size_t n;
864  uint32_t length;
865  FsFile *file;
866 
867  //Retrieve the full pathname
868  httpGetAbsolutePath(connection, uri, connection->buffer,
870 
871 #if (HTTP_SERVER_GZIP_TYPE_SUPPORT == ENABLED)
872  //Check whether gzip compression is supported by the client
873  if(connection->request.acceptGzipEncoding)
874  {
875  //Calculate the length of the pathname
876  n = strlen(connection->buffer);
877 
878  //Sanity check
879  if(n < (HTTP_SERVER_BUFFER_SIZE - 4))
880  {
881  //Append gzip extension
882  strcpy(connection->buffer + n, ".gz");
883  //Retrieve the size of the compressed resource, if any
884  error = fsGetFileSize(connection->buffer, &length);
885  }
886  else
887  {
888  //Report an error
889  error = ERROR_NOT_FOUND;
890  }
891 
892  //Check whether the gzip-compressed file exists
893  if(!error)
894  {
895  //Use gzip format
896  connection->response.gzipEncoding = TRUE;
897  }
898  else
899  {
900  //Strip the gzip extension
901  connection->buffer[n] = '\0';
902 
903  //Retrieve the size of the non-compressed resource
904  error = fsGetFileSize(connection->buffer, &length);
905  //The specified URI cannot be found?
906  if(error)
907  return ERROR_NOT_FOUND;
908  }
909  }
910  else
911 #endif
912  {
913  //Retrieve the size of the specified file
914  error = fsGetFileSize(connection->buffer, &length);
915  //The specified URI cannot be found?
916  if(error)
917  return ERROR_NOT_FOUND;
918  }
919 
920  //Open the file for reading
921  file = fsOpenFile(connection->buffer, FS_FILE_MODE_READ);
922  //Failed to open the file?
923  if(file == NULL)
924  return ERROR_NOT_FOUND;
925 #else
926  error_t error;
927  size_t length;
928  const uint8_t *data;
929 
930  //Retrieve the full pathname
931  httpGetAbsolutePath(connection, uri, connection->buffer,
933 
934 #if (HTTP_SERVER_GZIP_TYPE_SUPPORT == ENABLED)
935  //Check whether gzip compression is supported by the client
936  if(connection->request.acceptGzipEncoding)
937  {
938  size_t n;
939 
940  //Calculate the length of the pathname
941  n = strlen(connection->buffer);
942 
943  //Sanity check
944  if(n < (HTTP_SERVER_BUFFER_SIZE - 4))
945  {
946  //Append gzip extension
947  strcpy(connection->buffer + n, ".gz");
948  //Get the compressed resource data associated with the URI, if any
949  error = resGetData(connection->buffer, &data, &length);
950  }
951  else
952  {
953  //Report an error
954  error = ERROR_NOT_FOUND;
955  }
956 
957  //Check whether the gzip-compressed resource exists
958  if(!error)
959  {
960  //Use gzip format
961  connection->response.gzipEncoding = TRUE;
962  }
963  else
964  {
965  //Strip the gzip extension
966  connection->buffer[n] = '\0';
967 
968  //Get the non-compressed resource data associated with the URI
969  error = resGetData(connection->buffer, &data, &length);
970  //The specified URI cannot be found?
971  if(error)
972  return error;
973  }
974  }
975  else
976 #endif
977  {
978  //Get the resource data associated with the URI
979  error = resGetData(connection->buffer, &data, &length);
980  //The specified URI cannot be found?
981  if(error)
982  return error;
983  }
984 #endif
985 
986  //Format HTTP response header
987  connection->response.statusCode = 200;
988  connection->response.contentType = mimeGetType(uri);
989  connection->response.chunkedEncoding = FALSE;
990  connection->response.contentLength = length;
991 
992  //Send the header to the client
993  error = httpWriteHeader(connection);
994  //Any error to report?
995  if(error)
996  {
997 #if (HTTP_SERVER_FS_SUPPORT == ENABLED)
998  //Close the file
999  fsCloseFile(file);
1000 #endif
1001  //Return status code
1002  return error;
1003  }
1004 
1005 #if (HTTP_SERVER_FS_SUPPORT == ENABLED)
1006  //Send response body
1007  while(length > 0)
1008  {
1009  //Limit the number of bytes to read at a time
1011 
1012  //Read data from the specified file
1013  error = fsReadFile(file, connection->buffer, n, &n);
1014  //End of input stream?
1015  if(error)
1016  break;
1017 
1018  //Send data to the client
1019  error = httpWriteStream(connection, connection->buffer, n);
1020  //Any error to report?
1021  if(error)
1022  break;
1023 
1024  //Decrement the count of remaining bytes to be transferred
1025  length -= n;
1026  }
1027 
1028  //Close the file
1029  fsCloseFile(file);
1030 
1031  //Successful file transfer?
1032  if(error == NO_ERROR || error == ERROR_END_OF_FILE)
1033  {
1034  if(length == 0)
1035  {
1036  //Properly close the output stream
1037  error = httpCloseStream(connection);
1038  }
1039  }
1040 #else
1041  //Send response body
1042  error = httpWriteStream(connection, data, length);
1043  //Any error to report?
1044  if(error)
1045  return error;
1046 
1047  //Properly close output stream
1048  error = httpCloseStream(connection);
1049 #endif
1050 
1051  //Return status code
1052  return error;
1053 }
1054 
1055 
1056 /**
1057  * @brief Send error response to the client
1058  * @param[in] connection Structure representing an HTTP connection
1059  * @param[in] statusCode HTTP status code
1060  * @param[in] message User message
1061  * @return Error code
1062  **/
1063 
1065  uint_t statusCode, const char_t *message)
1066 {
1067  error_t error;
1068  size_t length;
1069 
1070  //HTML response template
1071  static const char_t template[] =
1072  "<!doctype html>\r\n"
1073  "<html>\r\n"
1074  "<head><title>Error %03d</title></head>\r\n"
1075  "<body>\r\n"
1076  "<h2>Error %03d</h2>\r\n"
1077  "<p>%s</p>\r\n"
1078  "</body>\r\n"
1079  "</html>\r\n";
1080 
1081  //Compute the length of the response
1082  length = strlen(template) + strlen(message) - 4;
1083 
1084  //Check whether the HTTP request has a body
1085  if(strcasecmp(connection->request.method, "GET") &&
1086  strcasecmp(connection->request.method, "HEAD") &&
1087  strcasecmp(connection->request.method, "DELETE"))
1088  {
1089  //Drop the HTTP request body and close the connection after sending
1090  //the HTTP response
1091  connection->response.keepAlive = FALSE;
1092  }
1093 
1094  //Format HTTP response header
1095  connection->response.statusCode = statusCode;
1096  connection->response.contentType = mimeGetType(".htm");
1097  connection->response.chunkedEncoding = FALSE;
1098  connection->response.contentLength = length;
1099 
1100  //Send the header to the client
1101  error = httpWriteHeader(connection);
1102  //Any error to report?
1103  if(error)
1104  return error;
1105 
1106  //Format HTML response
1107  sprintf(connection->buffer, template, statusCode, statusCode, message);
1108 
1109  //Send response body
1110  error = httpWriteStream(connection, connection->buffer, length);
1111  //Any error to report?
1112  if(error)
1113  return error;
1114 
1115  //Properly close output stream
1116  error = httpCloseStream(connection);
1117  //Return status code
1118  return error;
1119 }
1120 
1121 
1122 /**
1123  * @brief Send redirect response to the client
1124  * @param[in] connection Structure representing an HTTP connection
1125  * @param[in] statusCode HTTP status code (301 for permanent redirects)
1126  * @param[in] uri NULL-terminated string containing the redirect URI
1127  * @return Error code
1128  **/
1129 
1131  uint_t statusCode, const char_t *uri)
1132 {
1133  error_t error;
1134  size_t length;
1135 
1136  //HTML response template
1137  static const char_t template[] =
1138  "<!doctype html>\r\n"
1139  "<html>\r\n"
1140  "<head><title>Moved</title></head>\r\n"
1141  "<body>\r\n"
1142  "<h2>Moved</h2>\r\n"
1143  "<p>This page has moved to <a href=\"%s\">%s</a>.</p>"
1144  "</body>\r\n"
1145  "</html>\r\n";
1146 
1147  //Compute the length of the response
1148  length = strlen(template) + 2 * strlen(uri) - 4;
1149 
1150  //Check whether the HTTP request has a body
1151  if(strcasecmp(connection->request.method, "GET") &&
1152  strcasecmp(connection->request.method, "HEAD") &&
1153  strcasecmp(connection->request.method, "DELETE"))
1154  {
1155  //Drop the HTTP request body and close the connection after sending
1156  //the HTTP response
1157  connection->response.keepAlive = FALSE;
1158  }
1159 
1160  //Format HTTP response header
1161  connection->response.statusCode = statusCode;
1162  connection->response.location = uri;
1163  connection->response.contentType = mimeGetType(".htm");
1164  connection->response.chunkedEncoding = FALSE;
1165  connection->response.contentLength = length;
1166 
1167  //Send the header to the client
1168  error = httpWriteHeader(connection);
1169  //Any error to report?
1170  if(error)
1171  return error;
1172 
1173  //Format HTML response
1174  sprintf(connection->buffer, template, uri, uri);
1175 
1176  //Send response body
1177  error = httpWriteStream(connection, connection->buffer, length);
1178  //Any error to report?
1179  if(error)
1180  return error;
1181 
1182  //Properly close output stream
1183  error = httpCloseStream(connection);
1184  //Return status code
1185  return error;
1186 }
1187 
1188 
1189 /**
1190  * @brief Check whether the client's handshake is valid
1191  * @param[in] connection Structure representing an HTTP connection
1192  * @return TRUE if the WebSocket handshake is valid, else FALSE
1193  **/
1194 
1196 {
1197 #if (HTTP_SERVER_WEB_SOCKET_SUPPORT == ENABLED)
1198  error_t error;
1199  size_t n;
1200 
1201  //The request must contain an Upgrade header field whose value
1202  //must include the "websocket" keyword
1203  if(!connection->request.upgradeWebSocket)
1204  return FALSE;
1205 
1206  //The request must contain a Connection header field whose value
1207  //must include the "Upgrade" token
1208  if(!connection->request.connectionUpgrade)
1209  return FALSE;
1210 
1211  //Retrieve the length of the client's key
1212  n = strlen(connection->request.clientKey);
1213 
1214  //The request must include a header field with the name Sec-WebSocket-Key
1215  if(n == 0)
1216  return FALSE;
1217 
1218  //The value of the Sec-WebSocket-Key header field must be a 16-byte
1219  //value that has been Base64-encoded
1220  error = base64Decode(connection->request.clientKey, n, connection->buffer, &n);
1221  //Decoding failed?
1222  if(error)
1223  return FALSE;
1224 
1225  //Check the length of the resulting value
1226  if(n != 16)
1227  return FALSE;
1228 
1229  //The client's handshake is valid
1230  return TRUE;
1231 #else
1232  //WebSocket are not supported
1233  return FALSE;
1234 #endif
1235 }
1236 
1237 
1238 /**
1239  * @brief Upgrade an existing HTTP connection to a WebSocket
1240  * @param[in] connection Structure representing an HTTP connection
1241  * @return Handle referencing the new WebSocket
1242  **/
1243 
1245 {
1246  WebSocket *webSocket;
1247 
1248 #if (HTTP_SERVER_WEB_SOCKET_SUPPORT == ENABLED)
1249 #if (HTTP_SERVER_TLS_SUPPORT == ENABLED)
1250  //Check whether a secure connection is being used
1251  if(connection->tlsContext != NULL)
1252  {
1253  //Upgrade the secure connection to a WebSocket
1254  webSocket = webSocketUpgradeSecureSocket(connection->socket,
1255  connection->tlsContext);
1256  }
1257  else
1258 #endif
1259  {
1260  //Upgrade the connection to a WebSocket
1261  webSocket = webSocketUpgradeSocket(connection->socket);
1262  }
1263 
1264  //Succesful upgrade?
1265  if(webSocket != NULL)
1266  {
1267  error_t error;
1268 
1269  //Copy client's key
1270  error = webSocketSetClientKey(webSocket, connection->request.clientKey);
1271 
1272  //Check status code
1273  if(!error)
1274  {
1275 #if (HTTP_SERVER_TLS_SUPPORT == ENABLED)
1276  //Detach the TLS context from the HTTP connection
1277  connection->tlsContext = NULL;
1278 #endif
1279  //Detach the socket from the HTTP connection
1280  connection->socket = NULL;
1281  }
1282  else
1283  {
1284  //Clean up side effects
1285  webSocketClose(webSocket);
1286  webSocket = NULL;
1287  }
1288  }
1289 #else
1290  //WebSockets are not supported
1291  webSocket = NULL;
1292 #endif
1293 
1294  //Return a handle to the freshly created WebSocket
1295  return webSocket;
1296 }
1297 
1298 
1299 #endif
TlsContext * tlsInit(void)
TLS context initialization.
Definition: tls.c:65
void httpListenerTask(void *param)
HTTP server listener task.
Definition: http_server.c:250
uint8_t length
Definition: dtls_misc.h:149
error_t socketBind(Socket *socket, const IpAddr *localIpAddr, uint16_t localPort)
Associate a local address with a socket.
Definition: socket.c:355
error_t tlsInitTicketContext(TlsTicketContext *ticketContext)
Initialize ticket encryption context.
Definition: tls_ticket.c:50
error_t tlsSetConnectionEnd(TlsContext *context, TlsConnectionEnd entity)
Set operation mode (client or server)
Definition: tls.c:312
error_t httpFormatResponseHeader(HttpConnection *connection, char_t *buffer)
Format HTTP response header.
int bool_t
Definition: compiler_port.h:49
bool_t osCreateMutex(OsMutex *mutex)
Create a mutex object.
#define HTTP_SERVER_BACKLOG
Definition: http_server.h:146
error_t httpWriteHeader(HttpConnection *connection)
Send HTTP response header.
Definition: http_server.c:627
error_t tlsDecryptTicket(TlsContext *context, const uint8_t *ciphertext, size_t ciphertextLen, uint8_t *plaintext, size_t *plaintextLen, void *param)
Session ticket decryption.
Definition: tls_ticket.c:211
HTTP server (HyperText Transfer Protocol)
IP network address.
Definition: ip.h:71
error_t fsGetFileSize(const char_t *path, uint32_t *size)
Retrieve the size of the specified file.
HttpRandCallback randCallback
Random data generation callback function.
Definition: http_server.h:546
void osReleaseSemaphore(OsSemaphore *semaphore)
Release the specified semaphore object.
uint8_t p
Definition: ndp.h:298
#define HTTP_SERVER_TIMEOUT
Definition: http_server.h:131
bool_t httpCheckWebSocketHandshake(HttpConnection *connection)
Check whether the client's handshake is valid.
Definition: http_server.c:1195
#define TRUE
Definition: os_port.h:50
#define HttpServerContext
Definition: http_server.h:314
void socketClose(Socket *socket)
Close an existing socket.
Definition: socket.c:822
char_t * ipAddrToString(const IpAddr *ipAddr, char_t *str)
Convert a binary IP address to a string representation.
Definition: ip.c:726
HttpRequestCallback requestCallback
HTTP request callback function.
Definition: http_server.h:550
HttpConnection * connections
Client connections.
Definition: http_server.h:538
error_t ssiExecuteScript(HttpConnection *connection, const char_t *uri, uint_t level)
Execute SSI script.
Definition: ssi.c:67
error_t httpServerStart(HttpServerContext *context)
Start HTTP server.
Definition: http_server.c:208
bool_t osWaitForSemaphore(OsSemaphore *semaphore, systime_t timeout)
Wait for the specified semaphore to be available.
error_t httpWriteStream(HttpConnection *connection, const void *data, size_t length)
Write data to the client.
Definition: http_server.c:766
error_t httpSendErrorResponse(HttpConnection *connection, uint_t statusCode, const char_t *message)
Send error response to the client.
Definition: http_server.c:1064
#define HTTP_SERVER_MAX_REQUESTS
Definition: http_server.h:153
error_t fsReadFile(FsFile *file, void *data, size_t size, size_t *length)
Read data from the specified file.
HTTP response.
Definition: http_server.h:504
bool_t osCreateSemaphore(OsSemaphore *semaphore, uint_t count)
Create a semaphore object.
const IpAddr IP_ADDR_ANY
Definition: ip.c:45
error_t httpServerInit(HttpServerContext *context, const HttpServerSettings *settings)
HTTP server initialization.
Definition: http_server.c:110
#define HTTP_SERVER_PRIORITY
Definition: http_server.h:126
error_t base64Decode(const char_t *input, size_t inputLen, void *output, size_t *outputLen)
Base64 decoding algorithm.
Definition: base64.c:194
error_t tlsShutdown(TlsContext *context)
Gracefully close TLS session.
Definition: tls.c:2112
#define FALSE
Definition: os_port.h:46
TlsInitCallback tlsInitCallback
TLS initialization callback function.
Definition: http_server.h:543
Invalid parameter.
Definition: error.h:47
OsTask * osCreateTask(const char_t *name, OsTaskCode taskCode, void *param, size_t stackSize, int_t priority)
Create a new task.
#define tlsSetSocket(context, socket)
Definition: tls.h:823
error_t
Error codes.
Definition: error.h:42
#define HttpConnection
Definition: http_server.h:318
error_t webSocketSetClientKey(WebSocket *webSocket, const char_t *clientKey)
Set client's key.
Definition: web_socket.c:658
error_t tlsSetTicketCallbacks(TlsContext *context, TlsTicketEncryptCallback ticketEncryptCallback, TlsTicketDecryptCallback ticketDecryptCallback, void *param)
Set ticket encryption/decryption callbacks.
Definition: tls.c:1326
int_t socket(int_t family, int_t type, int_t protocol)
Create a socket that is bound to a specific transport service provider.
Definition: bsd_socket.c:108
void fsCloseFile(FsFile *file)
Close a file.
uint16_t statusCode
error_t httpReceive(HttpConnection *connection, void *data, size_t size, size_t *received, uint_t flags)
Receive data from the client.
error_t httpReadStream(HttpConnection *connection, void *data, size_t size, size_t *received, uint_t flags)
Read data from client request.
Definition: http_server.c:660
const char_t * mimeGetType(const char_t *filename)
Get the MIME type from a given extension.
Definition: mime.c:113
HTTP server (miscellaneous functions)
char_t defaultDocument[HTTP_SERVER_DEFAULT_DOC_MAX_LEN+1]
Default home page.
Definition: http_server.h:540
FsFile * fsOpenFile(const char_t *path, uint_t mode)
Open the specified file for reading or writing.
HTTP server settings.
Definition: http_server.h:532
uint16_t port
HTTP server port number.
Definition: http_server.h:535
error_t socketShutdown(Socket *socket, uint_t how)
Disable reception, transmission, or both.
Definition: socket.c:785
#define TRACE_INFO(...)
Definition: debug.h:94
WebSocket * httpUpgradeToWebSocket(HttpConnection *connection)
Upgrade an existing HTTP connection to a WebSocket.
Definition: http_server.c:1244
#define LSB(x)
Definition: os_port.h:54
Socket * socketOpen(uint_t type, uint_t protocol)
Create a socket (UDP or TCP)
Definition: socket.c:95
error_t resGetData(const char_t *path, const uint8_t **data, size_t *length)
error_t httpSend(HttpConnection *connection, const void *data, size_t length, uint_t flags)
Send data to the client.
#define MIN(a, b)
Definition: os_port.h:62
#define WebSocket
Definition: web_socket.h:177
#define osEnterTask()
#define socketBindToInterface
Definition: net_legacy.h:270
uint_t backlog
Maximum length of the pending connection queue.
Definition: http_server.h:536
Socket * socketAccept(Socket *socket, IpAddr *clientIpAddr, uint16_t *clientPort)
Permit an incoming connection attempt on a socket.
Definition: socket.c:481
HttpCgiCallback cgiCallback
CGI callback function.
Definition: http_server.h:549
WebSocket * webSocketUpgradeSocket(Socket *socket)
Upgrade a socket to a WebSocket.
Definition: web_socket.c:155
uint8_t flags
Definition: tcp.h:314
#define TRACE_DEBUG(...)
Definition: debug.h:106
HTTP authentication.
char char_t
Definition: compiler_port.h:43
#define HTTP_PORT
Definition: http_common.h:38
#define OS_INVALID_HANDLE
Definition: os_port.h:79
HTTP request.
Definition: http_server.h:466
WebSocket * webSocketUpgradeSecureSocket(Socket *socket, TlsContext *tlsContext)
Upgrade a secure socket to a secure WebSocket.
Definition: web_socket.c:194
uint8_t n
bool_t osWaitForEvent(OsEvent *event, systime_t timeout)
Wait until the specified event is in the signaled state.
void httpServerGetDefaultSettings(HttpServerSettings *settings)
Initialize settings with default values.
Definition: http_server.c:63
HttpAuthCallback authCallback
HTTP authentication callback function.
Definition: http_server.h:547
void httpGetAbsolutePath(HttpConnection *connection, const char_t *relative, char_t *absolute, size_t maxLen)
Retrieve the full pathname to the specified resource.
void webSocketClose(WebSocket *webSocket)
Close a WebSocket connection.
Definition: web_socket.c:1555
error_t tlsEncryptTicket(TlsContext *context, const uint8_t *plaintext, size_t plaintextLen, uint8_t *ciphertext, size_t *ciphertextLen, void *param)
Session ticket encryption.
Definition: tls_ticket.c:82
uint8_t file[128]
Definition: dhcp_common.h:213
#define Socket
Definition: socket.h:36
error_t httpSendResponse(HttpConnection *connection, const char_t *uri)
Send HTTP response.
Definition: http_server.c:859
uint_t maxConnections
Maximum number of client connections.
Definition: http_server.h:537
bool_t osCreateEvent(OsEvent *event)
Create an event object.
NetInterface * interface
Underlying network interface.
Definition: http_server.h:534
#define strcasecmp
uint8_t s
uint8_t message[]
Definition: chap.h:152
bool_t httpCompExtension(const char_t *filename, const char_t *extension)
Compare filename extension.
error_t httpReadRequestHeader(HttpConnection *connection)
Read HTTP request header and parse its contents.
error_t httpCloseStream(HttpConnection *connection)
Close output stream.
Definition: http_server.c:831
#define HTTP_SERVER_BUFFER_SIZE
Definition: http_server.h:160
void osSetEvent(OsEvent *event)
Set the specified event object to the signaled state.
void tlsFree(TlsContext *context)
Release TLS context.
Definition: tls.c:2272
error_t httpSendRedirectResponse(HttpConnection *connection, uint_t statusCode, const char_t *uri)
Send redirect response to the client.
Definition: http_server.c:1130
MIME (Multipurpose Internet Mail Extensions)
error_t httpReadChunkSize(HttpConnection *connection)
Read chunk-size field from the input stream.
unsigned int uint_t
Definition: compiler_port.h:45
TCP/IP stack core.
uint8_t data[]
Definition: dtls_misc.h:176
#define HTTP_SERVER_STACK_SIZE
Definition: http_server.h:119
error_t socketSetTimeout(Socket *socket, systime_t timeout)
Set timeout value for blocking operations.
Definition: socket.c:219
#define HTTP_SERVER_MAX_AGE
Definition: http_server.h:230
SSI (Server Side Includes)
error_t tlsConnect(TlsContext *context)
Initiate the TLS handshake.
Definition: tls.c:1572
void httpConnectionTask(void *param)
Task that services requests from an active connection.
Definition: http_server.c:323
char_t rootDirectory[HTTP_SERVER_ROOT_DIR_MAX_LEN+1]
Web root directory.
Definition: http_server.h:539
Success.
Definition: error.h:44
Debugging facilities.
#define INFINITE_DELAY
Definition: os_port.h:74
void httpInitResponseHeader(HttpConnection *connection)
Initialize response header.
void FsFile
File descriptor.
Definition: fs_port_fatfs.h:60
HttpUriNotFoundCallback uriNotFoundCallback
URI not found callback function.
Definition: http_server.h:551
error_t socketListen(Socket *socket, uint_t backlog)
Place a socket in the listening state.
Definition: socket.c:444