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