Webserver+3d print

Dependents:   Nucleo

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers http_server.c Source File

http_server.c

Go to the documentation of this file.
00001 /**
00002  * @file http_server.c
00003  * @brief HTTP server (HyperText Transfer Protocol)
00004  *
00005  * @section License
00006  *
00007  * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
00008  *
00009  * This file is part of CycloneTCP Open.
00010  *
00011  * This program is free software; you can redistribute it and/or
00012  * modify it under the terms of the GNU General Public License
00013  * as published by the Free Software Foundation; either version 2
00014  * of the License, or (at your option) any later version.
00015  *
00016  * This program is distributed in the hope that it will be useful,
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  * GNU General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU General Public License
00022  * along with this program; if not, write to the Free Software Foundation,
00023  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00024  *
00025  * @section Description
00026  *
00027  * Using the HyperText Transfer Protocol, the HTTP server delivers web pages
00028  * to browsers as well as other data files to web-based applications. Refers
00029  * to the following RFCs for complete details:
00030  * - RFC 1945: Hypertext Transfer Protocol - HTTP/1.0
00031  * - RFC 2616: Hypertext Transfer Protocol - HTTP/1.1
00032  * - RFC 2617: HTTP Authentication: Basic and Digest Access Authentication
00033  * - RFC 2818: HTTP Over TLS
00034  *
00035  * @author Oryx Embedded SARL (www.oryx-embedded.com)
00036  * @version 1.7.6
00037  **/
00038 
00039 //Switch to the appropriate trace level
00040 #define TRACE_LEVEL HTTP_TRACE_LEVEL
00041 
00042 //Dependencies
00043 #include <stdlib.h>
00044 #include "core/net.h"
00045 #include "http/http_server.h"
00046 #include "http/http_server_auth.h"
00047 #include "http/http_server_misc.h"
00048 #include "http/mime.h"
00049 #include "http/ssi.h"
00050 #include "debug.h"
00051 
00052 //Check TCP/IP stack configuration
00053 #if (HTTP_SERVER_SUPPORT == ENABLED)
00054 
00055 
00056 /**
00057  * @brief Initialize settings with default values
00058  * @param[out] settings Structure that contains HTTP server settings
00059  **/
00060 
00061 void httpServerGetDefaultSettings(HttpServerSettings *settings)
00062 {
00063    //The HTTP server is not bound to any interface
00064    settings->interface = NULL;
00065 
00066    //Listen to port 80
00067    settings->port = HTTP_PORT;
00068    //Maximum length of the pending connection queue
00069    settings->backlog = HTTP_SERVER_BACKLOG;
00070    //Client connections
00071    settings->maxConnections = 0;
00072    settings->connections = NULL;
00073    //Specify the server's root directory
00074    strcpy(settings->rootDirectory, "/");
00075    //Set default home page
00076    strcpy(settings->defaultDocument, "index.htm");
00077 
00078 #if (HTTP_SERVER_TLS_SUPPORT == ENABLED)
00079    //SSL/TLS initialization callback function
00080    settings->tlsInitCallback = NULL;
00081 #endif
00082 
00083 #if (HTTP_SERVER_BASIC_AUTH_SUPPORT == ENABLED || HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED)
00084    //Random data generation callback function
00085    settings->randCallback = NULL;
00086    //HTTP authentication callback function
00087    settings->authCallback = NULL;
00088 #endif
00089 
00090    //CGI callback function
00091    settings->cgiCallback = NULL;
00092    //HTTP request callback function
00093    settings->requestCallback = NULL;
00094    //URI not found callback function
00095    settings->uriNotFoundCallback = NULL;
00096 }
00097 
00098 
00099 /**
00100  * @brief HTTP server initialization
00101  * @param[in] context Pointer to the HTTP server context
00102  * @param[in] settings HTTP server specific settings
00103  * @return Error code
00104  **/
00105 
00106 error_t httpServerInit(HttpServerContext *context, const HttpServerSettings *settings)
00107 {
00108    error_t error;
00109    uint_t i;
00110    HttpConnection *connection;
00111 
00112    //Debug message
00113    TRACE_INFO("Initializing HTTP server...\r\n");
00114 
00115    //Ensure the parameters are valid
00116    if(context == NULL || settings == NULL)
00117       return ERROR_INVALID_PARAMETER;
00118 
00119    //Check user settings
00120    if(settings->maxConnections == 0 || settings->connections == NULL)
00121       return ERROR_INVALID_PARAMETER;
00122 
00123    //Clear the HTTP server context
00124    memset(context, 0, sizeof(HttpServerContext));
00125 
00126    //Save user settings
00127    context->settings = *settings;
00128    //Client connections
00129    context->connections = settings->connections;
00130 
00131    //Create a semaphore to limit the number of simultaneous connections
00132    if(!osCreateSemaphore(&context->semaphore, context->settings.maxConnections))
00133       return ERROR_OUT_OF_RESOURCES;
00134 
00135    //Loop through client connections
00136    for(i = 0; i < context->settings.maxConnections; i++)
00137    {
00138       //Point to the structure representing the client connection
00139       connection = &context->connections[i];
00140 
00141       //Initialize the structure
00142       memset(connection, 0, sizeof(HttpConnection));
00143 
00144       //Create an event object to manage connection lifetime
00145       if(!osCreateEvent(&connection->startEvent))
00146          return ERROR_OUT_OF_RESOURCES;
00147    }
00148 
00149 #if (HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED)
00150    //Create a mutex to prevent simultaneous access to the nonce cache
00151    if(!osCreateMutex(&context->nonceCacheMutex))
00152       return ERROR_OUT_OF_RESOURCES;
00153 #endif
00154 
00155    //Open a TCP socket
00156    context->socket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP);
00157    //Failed to open socket?
00158    if(!context->socket)
00159       return ERROR_OPEN_FAILED;
00160 
00161    //Set timeout for blocking functions
00162    error = socketSetTimeout(context->socket, INFINITE_DELAY);
00163    //Any error to report?
00164    if(error)
00165       return error;
00166 
00167    //Associate the socket with the relevant interface
00168    error = socketBindToInterface(context->socket, settings->interface);
00169    //Unable to bind the socket to the desired interface?
00170    if(error)
00171       return error;
00172 
00173    //Bind newly created socket to port 80
00174    error = socketBind(context->socket, &IP_ADDR_ANY, settings->port);
00175    //Failed to bind socket to port 80?
00176    if(error)
00177       return error;
00178 
00179    //Place socket in listening state
00180    error = socketListen(context->socket, settings->backlog);
00181    //Any failure to report?
00182    if(error)
00183       return error;
00184 
00185    //Successful initialization
00186    return NO_ERROR;
00187 }
00188 
00189 
00190 /**
00191  * @brief Start HTTP server
00192  * @param[in] context Pointer to the HTTP server context
00193  * @return Error code
00194  **/
00195 
00196 error_t httpServerStart(HttpServerContext *context)
00197 {
00198    uint_t i;
00199 
00200    //Debug message
00201    TRACE_INFO("Starting HTTP server...\r\n");
00202 
00203    //Make sure the HTTP server context is valid
00204    if(context == NULL)
00205       return ERROR_INVALID_PARAMETER;
00206 
00207    //Loop through client connections
00208    for(i = 0; i < context->settings.maxConnections; i++)
00209    {
00210       //Create a task to service a given HTTP client connection
00211       context->connections[i].taskHandle = osCreateTask("HTTP Connection",
00212          httpConnectionTask, &context->connections[i],
00213          HTTP_SERVER_STACK_SIZE, HTTP_SERVER_PRIORITY);
00214 
00215       //Unable to create the task?
00216       if(context->connections[i].taskHandle == OS_INVALID_HANDLE)
00217          return ERROR_OUT_OF_RESOURCES;
00218    }
00219 
00220    //Create the HTTP server listener task
00221    context->taskHandle = osCreateTask("HTTP Listener", httpListenerTask,
00222       context, HTTP_SERVER_STACK_SIZE, HTTP_SERVER_PRIORITY);
00223 
00224    //Unable to create the task?
00225    if(context->taskHandle == OS_INVALID_HANDLE)
00226       return ERROR_OUT_OF_RESOURCES;
00227 
00228    //The HTTP server has successfully started
00229    return NO_ERROR;
00230 }
00231 
00232 
00233 /**
00234  * @brief HTTP server listener task
00235  * @param[in] param Pointer to the HTTP server context
00236  **/
00237 
00238 void httpListenerTask(void *param)
00239 {
00240    uint_t i;
00241    uint_t counter;
00242    uint16_t clientPort;
00243    IpAddr clientIpAddr;
00244    HttpServerContext *context;
00245    HttpConnection *connection;
00246    Socket *socket;
00247 
00248    //Retrieve the HTTP server context
00249    context = (HttpServerContext *) param;
00250 
00251    //Process incoming connections to the server
00252    for(counter = 1; ; counter++)
00253    {
00254       //Debug message
00255       TRACE_INFO("Ready to accept a new connection...\r\n");
00256 
00257       //Limit the number of simultaneous connections to the HTTP server
00258       osWaitForSemaphore(&context->semaphore, INFINITE_DELAY);
00259 
00260       //Loop through available client connections
00261       for(i = 0; i < context->settings.maxConnections; i++)
00262       {
00263          //Point to the current connection
00264          connection = &context->connections[i];
00265 
00266          //Ready to service the client request?
00267          if(!connection->running)
00268          {
00269             //Accept an incoming connection
00270             socket = socketAccept(context->socket, &clientIpAddr, &clientPort);
00271 
00272             //Make sure the socket handle is valid
00273             if(socket != NULL)
00274             {
00275                //Debug message
00276                TRACE_INFO("Connection #%u established with client %s port %" PRIu16 "...\r\n",
00277                   counter, ipAddrToString(&clientIpAddr, NULL), clientPort);
00278 
00279                //Reference to the HTTP server settings
00280                connection->settings = &context->settings;
00281                //Reference to the HTTP server context
00282                connection->serverContext = context;
00283                //Reference to the new socket
00284                connection->socket = socket;
00285 
00286                //Set timeout for blocking functions
00287                socketSetTimeout(connection->socket, HTTP_SERVER_TIMEOUT);
00288 
00289                //The client connection task is now running...
00290                connection->running = TRUE;
00291                //Service the current connection request
00292                osSetEvent(&connection->startEvent);
00293 
00294                //We are done
00295                break;
00296             }
00297          }
00298       }
00299    }
00300 }
00301 
00302 
00303 /**
00304  * @brief Task that services requests from an active connection
00305  * @param[in] param Structure representing an HTTP connection with a client
00306  **/
00307 
00308 void httpConnectionTask(void *param)
00309 {
00310    error_t error;
00311    uint_t counter;
00312    HttpConnection *connection;
00313 
00314    //Point to the structure representing the HTTP connection
00315    connection = (HttpConnection *) param;
00316 
00317    //Endless loop
00318    while(1)
00319    {
00320       //Wait for an incoming connection attempt
00321       osWaitForEvent(&connection->startEvent, INFINITE_DELAY);
00322 
00323       //Initialize status code
00324       error = NO_ERROR;
00325 
00326 #if (HTTP_SERVER_TLS_SUPPORT == ENABLED)
00327       //Use SSL/TLS to secure the connection?
00328       if(connection->settings->useTls)
00329       {
00330          //Debug message
00331          TRACE_INFO("Initializing SSL/TLS session...\r\n");
00332 
00333          //Start of exception handling block
00334          do
00335          {
00336             //Allocate SSL/TLS context
00337             connection->tlsContext = tlsInit();
00338             //Initialization failed?
00339             if(connection->tlsContext == NULL)
00340             {
00341                //Report an error
00342                error = ERROR_OUT_OF_MEMORY;
00343                //Exit immediately
00344                break;
00345             }
00346 
00347             //Select server operation mode
00348             error = tlsSetConnectionEnd(connection->tlsContext, TLS_CONNECTION_END_SERVER);
00349             //Any error to report?
00350             if(error)
00351                break;
00352 
00353             //Bind TLS to the relevant socket
00354             error = tlsSetSocket(connection->tlsContext, connection->socket);
00355             //Any error to report?
00356             if(error)
00357                break;
00358 
00359             //Invoke user-defined callback, if any
00360             if(connection->settings->tlsInitCallback != NULL)
00361             {
00362                //Perform SSL/TLS related initialization
00363                error = connection->settings->tlsInitCallback(connection, connection->tlsContext);
00364                //Any error to report?
00365                if(error)
00366                   break;
00367             }
00368 
00369             //Establish a secure session
00370             error = tlsConnect(connection->tlsContext);
00371             //Any error to report?
00372             if(error)
00373                break;
00374 
00375             //End of exception handling block
00376          } while(0);
00377       }
00378       else
00379       {
00380          //Do not use SSL/TLS
00381          connection->tlsContext = NULL;
00382       }
00383 #endif
00384 
00385       //Check status code
00386       if(!error)
00387       {
00388          //Process incoming requests
00389          for(counter = 0; counter < HTTP_SERVER_MAX_REQUESTS; counter++)
00390          {
00391             //Debug message
00392             TRACE_INFO("Waiting for request...\r\n");
00393 
00394             //Clear request header
00395             memset(&connection->request, 0, sizeof(HttpRequest));
00396             //Clear response header
00397             memset(&connection->response, 0, sizeof(HttpResponse));
00398 
00399             //Read the HTTP request header and parse its contents
00400             error = httpReadRequestHeader(connection);
00401             //Any error to report?
00402             if(error)
00403             {
00404                //Debug message
00405                TRACE_INFO("No HTTP request received or parsing error...\r\n");
00406                break;
00407             }
00408 
00409 #if (HTTP_SERVER_BASIC_AUTH_SUPPORT == ENABLED || HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED)
00410             //No Authorization header found?
00411             if(!connection->request.auth.found)
00412             {
00413                //Invoke user-defined callback, if any
00414                if(connection->settings->authCallback != NULL)
00415                {
00416                   //Check whether the access to the specified URI is authorized
00417                   connection->status = connection->settings->authCallback(connection,
00418                      connection->request.auth.user, connection->request.uri);
00419                }
00420                else
00421                {
00422                   //Access to the specified URI is allowed
00423                   connection->status = HTTP_ACCESS_ALLOWED;
00424                }
00425             }
00426 
00427             //Check access status
00428             if(connection->status == HTTP_ACCESS_ALLOWED)
00429             {
00430                //Access to the specified URI is allowed
00431                error = NO_ERROR;
00432             }
00433             else if(connection->status == HTTP_ACCESS_BASIC_AUTH_REQUIRED)
00434             {
00435                //Basic access authentication is required
00436                connection->response.auth.mode = HTTP_AUTH_MODE_BASIC;
00437                //Report an error
00438                error = ERROR_AUTH_REQUIRED;
00439             }
00440             else if(connection->status == HTTP_ACCESS_DIGEST_AUTH_REQUIRED)
00441             {
00442                //Digest access authentication is required
00443                connection->response.auth.mode = HTTP_AUTH_MODE_DIGEST;
00444                //Report an error
00445                error = ERROR_AUTH_REQUIRED;
00446             }
00447             else
00448             {
00449                //Access to the specified URI is denied
00450                error = ERROR_NOT_FOUND;
00451             }
00452 #endif
00453             //Debug message
00454             TRACE_INFO("Sending HTTP response to the client...\r\n");
00455 
00456             //Check status code
00457             if(!error)
00458             {
00459                //Default HTTP header fields
00460                httpInitResponseHeader(connection);
00461 
00462                //Invoke user-defined callback, if any
00463                if(connection->settings->requestCallback != NULL)
00464                {
00465                   error = connection->settings->requestCallback(connection,
00466                      connection->request.uri);
00467                }
00468                else
00469                {
00470                   //Keep processing...
00471                   error = ERROR_NOT_FOUND;
00472                }
00473 
00474                //Check status code
00475                if(error == ERROR_NOT_FOUND)
00476                {
00477 #if (HTTP_SERVER_SSI_SUPPORT == ENABLED)
00478                   //Use server-side scripting to dynamically generate HTML code?
00479                   if(httpCompExtension(connection->request.uri, ".stm") ||
00480                      httpCompExtension(connection->request.uri, ".shtm") ||
00481                      httpCompExtension(connection->request.uri, ".shtml"))
00482                   {
00483                      //SSI processing (Server Side Includes)
00484                      error = ssiExecuteScript(connection, connection->request.uri, 0);
00485                   }
00486                   else
00487 #endif
00488                   {
00489                      //Set the maximum age for static resources
00490                      connection->response.maxAge = HTTP_SERVER_MAX_AGE;
00491 
00492                      //Send the contents of the requested page
00493                      error = httpSendResponse(connection, connection->request.uri);
00494                   }
00495                }
00496 
00497                //The requested resource is not available?
00498                if(error == ERROR_NOT_FOUND)
00499                {
00500                   //Default HTTP header fields
00501                   httpInitResponseHeader(connection);
00502 
00503                   //Invoke user-defined callback, if any
00504                   if(connection->settings->uriNotFoundCallback != NULL)
00505                   {
00506                      error = connection->settings->uriNotFoundCallback(connection,
00507                         connection->request.uri);
00508                   }
00509                }
00510             }
00511 
00512             //Check status code
00513             if(error)
00514             {
00515                //Default HTTP header fields
00516                httpInitResponseHeader(connection);
00517 
00518                //Bad request?
00519                if(error == ERROR_INVALID_REQUEST)
00520                {
00521                   //Send an error 400 and close the connection immediately
00522                   httpSendErrorResponse(connection, 400,
00523                      "The request is badly formed");
00524                }
00525                //Authorization required?
00526                else if(error == ERROR_AUTH_REQUIRED)
00527                {
00528                   //Send an error 401 and keep the connection alive
00529                   error = httpSendErrorResponse(connection, 401,
00530                      "Authorization required");
00531                }
00532                //Page not found?
00533                else if(error == ERROR_NOT_FOUND)
00534                {
00535                   //Send an error 404 and keep the connection alive
00536                   error = httpSendErrorResponse(connection, 404,
00537                      "The requested page could not be found");
00538                }
00539             }
00540 
00541             //Internal error?
00542             if(error)
00543             {
00544                //Close the connection immediately
00545                break;
00546             }
00547 
00548             //Check whether the connection is persistent or not
00549             if(!connection->request.keepAlive || !connection->response.keepAlive)
00550             {
00551                //Close the connection immediately
00552                break;
00553             }
00554          }
00555       }
00556 
00557 #if (HTTP_SERVER_TLS_SUPPORT == ENABLED)
00558       //Valid SSL/TLS context?
00559       if(connection->tlsContext != NULL)
00560       {
00561          //Debug message
00562          TRACE_INFO("Closing SSL/TLS session...\r\n");
00563 
00564          //Gracefully close SSL/TLS session
00565          tlsShutdown(connection->tlsContext);
00566          //Release context
00567          tlsFree(connection->tlsContext);
00568       }
00569 #endif
00570 
00571       //Valid socket handle?
00572       if(connection->socket != NULL)
00573       {
00574          //Debug message
00575          TRACE_INFO("Graceful shutdown...\r\n");
00576          //Graceful shutdown
00577          socketShutdown(connection->socket, SOCKET_SD_BOTH);
00578 
00579          //Debug message
00580          TRACE_INFO("Closing socket...\r\n");
00581          //Close socket
00582          socketClose(connection->socket);
00583       }
00584 
00585       //Ready to serve the next connection request...
00586       connection->running = FALSE;
00587       //Release semaphore
00588       osReleaseSemaphore(&connection->serverContext->semaphore);
00589    }
00590 }
00591 
00592 
00593 /**
00594  * @brief Send HTTP response header
00595  * @param[in] connection Structure representing an HTTP connection
00596  * @return Error code
00597  **/
00598 
00599 error_t httpWriteHeader(HttpConnection *connection)
00600 {
00601    error_t error;
00602 
00603    //Format HTTP response header
00604    error = httpFormatResponseHeader(connection, connection->buffer);
00605 
00606    //Check status code
00607    if(!error)
00608    {
00609       //Debug message
00610       TRACE_DEBUG("HTTP response header:\r\n%s", connection->buffer);
00611 
00612       //Send HTTP response header to the client
00613       error = httpSend(connection, connection->buffer,
00614          strlen(connection->buffer), HTTP_FLAG_DELAY);
00615    }
00616 
00617    //Return status code
00618    return error;
00619 }
00620 
00621 
00622 /**
00623  * @brief Read data from client request
00624  * @param[in] connection Structure representing an HTTP connection
00625  * @param[out] data Buffer where to store the incoming data
00626  * @param[in] size Maximum number of bytes that can be received
00627  * @param[out] received Number of bytes that have been received
00628  * @param[in] flags Set of flags that influences the behavior of this function
00629  * @return Error code
00630  **/
00631 
00632 error_t httpReadStream(HttpConnection *connection,
00633    void *data, size_t size, size_t *received, uint_t flags)
00634 {
00635    error_t error;
00636    size_t n;
00637 
00638    //No data has been read yet
00639    *received = 0;
00640 
00641    //Chunked encoding transfer is used?
00642    if(connection->request.chunkedEncoding)
00643    {
00644       //Point to the output buffer
00645       char_t *p = data;
00646 
00647       //Read as much data as possible
00648       while(*received < size)
00649       {
00650          //End of HTTP request body?
00651          if(connection->request.lastChunk)
00652             return ERROR_END_OF_STREAM;
00653 
00654          //Acquire a new chunk when the current chunk
00655          //has been completely consumed
00656          if(connection->request.byteCount == 0)
00657          {
00658             //The size of each chunk is sent right before the chunk itself
00659             error = httpReadChunkSize(connection);
00660             //Failed to decode the chunk-size field?
00661             if(error)
00662                return error;
00663 
00664             //Any chunk whose size is zero terminates the data transfer
00665             if(!connection->request.byteCount)
00666             {
00667                //The user must be satisfied with data already on hand
00668                return (*received > 0) ? NO_ERROR : ERROR_END_OF_STREAM;
00669             }
00670          }
00671 
00672          //Limit the number of bytes to read at a time
00673          n = MIN(size - *received, connection->request.byteCount);
00674 
00675          //Read data
00676          error = httpReceive(connection, p, n, &n, flags);
00677          //Any error to report?
00678          if(error)
00679             return error;
00680 
00681          //Total number of data that have been read
00682          *received += n;
00683          //Remaining data still available in the current chunk
00684          connection->request.byteCount -= n;
00685 
00686          //The HTTP_FLAG_BREAK_CHAR flag causes the function to stop reading
00687          //data as soon as the specified break character is encountered
00688          if(flags & HTTP_FLAG_BREAK_CRLF)
00689          {
00690             //Check whether a break character has been received
00691             if(p[n - 1] == LSB(flags))
00692                break;
00693          }
00694          //The HTTP_FLAG_WAIT_ALL flag causes the function to return
00695          //only when the requested number of bytes have been read
00696          else if(!(flags & HTTP_FLAG_WAIT_ALL))
00697          {
00698             break;
00699          }
00700 
00701          //Advance data pointer
00702          p += n;
00703       }
00704    }
00705    //Default encoding?
00706    else
00707    {
00708       //Return immediately if the end of the request body has been reached
00709       if(!connection->request.byteCount)
00710          return ERROR_END_OF_STREAM;
00711 
00712       //Limit the number of bytes to read
00713       n = MIN(size, connection->request.byteCount);
00714 
00715       //Read data
00716       error = httpReceive(connection, data, n, received, flags);
00717       //Any error to report
00718       if(error)
00719          return error;
00720 
00721       //Decrement the count of remaining bytes to read
00722       connection->request.byteCount -= *received;
00723    }
00724 
00725    //Successful read operation
00726    return NO_ERROR;
00727 }
00728 
00729 
00730 /**
00731  * @brief Write data to the client
00732  * @param[in] connection Structure representing an HTTP connection
00733  * @param[in] data Buffer containing the data to be transmitted
00734  * @param[in] length Number of bytes to be transmitted
00735  * @return Error code
00736  **/
00737 
00738 error_t httpWriteStream(HttpConnection *connection,
00739    const void *data, size_t length)
00740 {
00741    error_t error;
00742    uint_t n;
00743 
00744    //Use chunked encoding transfer?
00745    if(connection->response.chunkedEncoding)
00746    {
00747       //Any data to send?
00748       if(length > 0)
00749       {
00750          char_t s[8];
00751 
00752          //The chunk-size field is a string of hex digits
00753          //indicating the size of the chunk
00754          n = sprintf(s, "%X\r\n", length);
00755 
00756          //Send the chunk-size field
00757          error = httpSend(connection, s, n, HTTP_FLAG_DELAY);
00758          //Failed to send data?
00759          if(error)
00760             return error;
00761 
00762          //Send the chunk-data
00763          error = httpSend(connection, data, length, HTTP_FLAG_DELAY);
00764          //Failed to send data?
00765          if(error)
00766             return error;
00767 
00768          //Terminate the chunk-data by CRLF
00769          error = httpSend(connection, "\r\n", 2, HTTP_FLAG_DELAY);
00770       }
00771       else
00772       {
00773          //Any chunk whose size is zero may terminate the data
00774          //transfer and must be discarded
00775          error = NO_ERROR;
00776       }
00777    }
00778    //Default encoding?
00779    else
00780    {
00781       //The length of the body shall not exceed the value
00782       //specified in the Content-Length field
00783       length = MIN(length, connection->response.byteCount);
00784 
00785       //Send user data
00786       error = httpSend(connection, data, length, HTTP_FLAG_DELAY);
00787 
00788       //Decrement the count of remaining bytes to be transferred
00789       connection->response.byteCount -= length;
00790    }
00791 
00792    //Return status code
00793    return error;
00794 }
00795 
00796 
00797 /**
00798  * @brief Close output stream
00799  * @param[in] connection Structure representing an HTTP connection
00800  * @return Error code
00801  **/
00802 
00803 error_t httpCloseStream(HttpConnection *connection)
00804 {
00805    error_t error;
00806 
00807    //Use chunked encoding transfer?
00808    if(connection->response.chunkedEncoding)
00809    {
00810       //The chunked encoding is ended by any chunk whose size is zero
00811       error = httpSend(connection, "0\r\n\r\n", 5, HTTP_FLAG_NO_DELAY);
00812    }
00813    else
00814    {
00815       //Flush the send buffer
00816       error = httpSend(connection, "", 0, HTTP_FLAG_NO_DELAY);
00817    }
00818 
00819    //Return status code
00820    return error;
00821 }
00822 
00823 
00824 /**
00825  * @brief Send HTTP response
00826  * @param[in] connection Structure representing an HTTP connection
00827  * @param[in] uri NULL-terminated string containing the file to be sent in response
00828  * @return Error code
00829  **/
00830 
00831 error_t httpSendResponse(HttpConnection *connection, const char_t *uri)
00832 {
00833 #if (HTTP_SERVER_FS_SUPPORT == ENABLED)
00834    error_t error;
00835    uint32_t length;
00836    size_t n;
00837    FsFile *file;
00838 
00839    //Retrieve the full pathname
00840    httpGetAbsolutePath(connection, uri,
00841       connection->buffer, HTTP_SERVER_BUFFER_SIZE);
00842 
00843    //Retrieve the size of the specified file
00844    error = fsGetFileSize(connection->buffer, &length);
00845    //The specified URI cannot be found?
00846    if(error)
00847       return ERROR_NOT_FOUND;
00848 
00849    //Open the file for reading
00850    file = fsOpenFile(connection->buffer, FS_FILE_MODE_READ);
00851    //Failed to open the file?
00852    if(file == NULL)
00853       return ERROR_NOT_FOUND;
00854 #else
00855    error_t error;
00856    size_t length;
00857    uint8_t *data;
00858 
00859    //Retrieve the full pathname
00860    httpGetAbsolutePath(connection, uri,
00861       connection->buffer, HTTP_SERVER_BUFFER_SIZE);
00862 
00863    //Get the resource data associated with the URI
00864    error = resGetData(connection->buffer, &data, &length);
00865    //The specified URI cannot be found?
00866    if(error)
00867       return error;
00868 #endif
00869 
00870    //Format HTTP response header
00871    connection->response.statusCode = 200;
00872    connection->response.contentType = mimeGetType(uri);
00873    connection->response.chunkedEncoding = FALSE;
00874    connection->response.contentLength = length;
00875 
00876    //Send the header to the client
00877    error = httpWriteHeader(connection);
00878    //Any error to report?
00879    if(error)
00880    {
00881 #if (HTTP_SERVER_FS_SUPPORT == ENABLED)
00882       //Close the file
00883       fsCloseFile(file);
00884 #endif
00885       //Return status code
00886       return error;
00887    }
00888 
00889 #if (HTTP_SERVER_FS_SUPPORT == ENABLED)
00890    //Send response body
00891    while(length > 0)
00892    {
00893       //Limit the number of bytes to read at a time
00894       n = MIN(length, HTTP_SERVER_BUFFER_SIZE);
00895 
00896       //Read data from the specified file
00897       error = fsReadFile(file, connection->buffer, n, &n);
00898       //End of input stream?
00899       if(error)
00900          break;
00901 
00902       //Send data to the client
00903       error = httpWriteStream(connection, connection->buffer, n);
00904       //Any error to report?
00905       if(error)
00906          break;
00907 
00908       //Decrement the count of remaining bytes to be transferred
00909       length -= n;
00910    }
00911 
00912    //Close the file
00913    fsCloseFile(file);
00914 
00915    //Successful file transfer?
00916    if(error == NO_ERROR || error == ERROR_END_OF_FILE)
00917    {
00918       if(length == 0)
00919       {
00920          //Properly close the output stream
00921          error = httpCloseStream(connection);
00922       }
00923    }
00924 #else
00925    //Send response body
00926    error = httpWriteStream(connection, data, length);
00927    //Any error to report?
00928    if(error)
00929       return error;
00930 
00931    //Properly close output stream
00932    error = httpCloseStream(connection);
00933 #endif
00934 
00935    //Return status code
00936    return error;
00937 }
00938 
00939 
00940 /**
00941  * @brief Send error response to the client
00942  * @param[in] connection Structure representing an HTTP connection
00943  * @param[in] statusCode HTTP status code
00944  * @param[in] message User message
00945  * @return Error code
00946  **/
00947 
00948 error_t httpSendErrorResponse(HttpConnection *connection,
00949    uint_t statusCode, const char_t *message)
00950 {
00951    error_t error;
00952    size_t length;
00953 
00954    //HTML response template
00955    static const char_t template[] =
00956       "<!doctype html>\r\n"
00957       "<html>\r\n"
00958       "<head><title>Error %03d</title></head>\r\n"
00959       "<body>\r\n"
00960       "<h2>Error %03d</h2>\r\n"
00961       "<p>%s</p>\r\n"
00962       "</body>\r\n"
00963       "</html>\r\n";
00964 
00965    //Compute the length of the response
00966    length = strlen(template) + strlen(message) - 4;
00967 
00968    //Format HTTP response header
00969    connection->response.statusCode = statusCode;
00970    connection->response.contentType = mimeGetType(".htm");
00971    connection->response.chunkedEncoding = FALSE;
00972    connection->response.contentLength = length;
00973 
00974    //Send the header to the client
00975    error = httpWriteHeader(connection);
00976    //Any error to report?
00977    if(error)
00978       return error;
00979 
00980    //Format HTML response
00981    sprintf(connection->buffer, template, statusCode, statusCode, message);
00982 
00983    //Send response body
00984    error = httpWriteStream(connection, connection->buffer, length);
00985    //Any error to report?
00986    if(error)
00987       return error;
00988 
00989    //Properly close output stream
00990    error = httpCloseStream(connection);
00991    //Return status code
00992    return error;
00993 }
00994 
00995 
00996 /**
00997  * @brief Send redirect response to the client
00998  * @param[in] connection Structure representing an HTTP connection
00999  * @param[in] statusCode HTTP status code (301 for permanent redirects)
01000  * @param[in] uri NULL-terminated string containing the redirect URI
01001  * @return Error code
01002  **/
01003 
01004 error_t httpSendRedirectResponse(HttpConnection *connection,
01005    uint_t statusCode, const char_t *uri)
01006 {
01007    error_t error;
01008    size_t length;
01009 
01010    //HTML response template
01011    static const char_t template[] =
01012       "<!doctype html>\r\n"
01013       "<html>\r\n"
01014       "<head><title>Moved</title></head>\r\n"
01015       "<body>\r\n"
01016       "<h2>Moved</h2>\r\n"
01017       "<p>This page has moved to <a href=\"%s\">%s</a>.</p>"
01018       "</body>\r\n"
01019       "</html>\r\n";
01020 
01021    //Compute the length of the response
01022    length = strlen(template) + 2 * strlen(uri) - 4;
01023 
01024    //Format HTTP response header
01025    connection->response.statusCode = statusCode;
01026    connection->response.location = uri;
01027    connection->response.contentType = mimeGetType(".htm");
01028    connection->response.chunkedEncoding = FALSE;
01029    connection->response.contentLength = length;
01030 
01031    //Send the header to the client
01032    error = httpWriteHeader(connection);
01033    //Any error to report?
01034    if(error)
01035       return error;
01036 
01037    //Format HTML response
01038    sprintf(connection->buffer, template, uri, uri);
01039 
01040    //Send response body
01041    error = httpWriteStream(connection, connection->buffer, length);
01042    //Any error to report?
01043    if(error)
01044       return error;
01045 
01046    //Properly close output stream
01047    error = httpCloseStream(connection);
01048    //Return status code
01049    return error;
01050 }
01051 
01052 
01053 /**
01054  * @brief Check whether the client's handshake is valid
01055  * @param[in] connection Structure representing an HTTP connection
01056  * @return TRUE if the WebSocket handshake is valid, else FALSE
01057  **/
01058 
01059 bool_t httpCheckWebSocketHandshake(HttpConnection *connection)
01060 {
01061 #if (HTTP_SERVER_WEB_SOCKET_SUPPORT == ENABLED)
01062    error_t error;
01063    size_t n;
01064 
01065    //The request must contain an Upgrade header field whose value
01066    //must include the "websocket" keyword
01067    if(!connection->request.upgradeWebSocket)
01068       return FALSE;
01069 
01070    //The request must contain a Connection header field whose value
01071    //must include the "Upgrade" token
01072    if(!connection->request.connectionUpgrade)
01073       return FALSE;
01074 
01075    //Retrieve the length of the client's key
01076    n = strlen(connection->request.clientKey);
01077 
01078    //The request must include a header field with the name Sec-WebSocket-Key
01079    if(n == 0)
01080       return FALSE;
01081 
01082    //The value of the Sec-WebSocket-Key header field must be a 16-byte
01083    //value that has been Base64-encoded
01084    error = base64Decode(connection->request.clientKey, n, connection->buffer, &n);
01085    //Decoding failed?
01086    if(error)
01087       return FALSE;
01088 
01089    //Check the length of the resulting value
01090    if(n != 16)
01091       return FALSE;
01092 
01093    //The client's handshake is valid
01094    return TRUE;
01095 #else
01096    //WebSocket are not supported
01097    return FALSE;
01098 #endif
01099 }
01100 
01101 
01102 /**
01103  * @brief Upgrade an existing HTTP connection to a WebSocket
01104  * @param[in] connection Structure representing an HTTP connection
01105  * @return Handle referencing the new WebSocket
01106  **/
01107 
01108 WebSocket *httpUpgradeToWebSocket(HttpConnection *connection)
01109 {
01110    WebSocket *webSocket;
01111 
01112 #if (HTTP_SERVER_WEB_SOCKET_SUPPORT == ENABLED)
01113 #if (HTTP_SERVER_TLS_SUPPORT == ENABLED)
01114    //Check whether a secure connection is being used
01115    if(connection->tlsContext != NULL)
01116    {
01117       //Upgrade the secure connection to a WebSocket
01118       webSocket = webSocketUpgradeSecureSocket(connection->socket,
01119          connection->tlsContext);
01120    }
01121    else
01122 #endif
01123    {
01124       //Upgrade the connection to a WebSocket
01125       webSocket = webSocketUpgradeSocket(connection->socket);
01126    }
01127 
01128    //Succesful upgrade?
01129    if(webSocket != NULL)
01130    {
01131       error_t error;
01132 
01133       //Copy client's key
01134       error = webSocketSetClientKey(webSocket, connection->request.clientKey);
01135 
01136       //Check status code
01137       if(!error)
01138       {
01139 #if (HTTP_SERVER_TLS_SUPPORT == ENABLED)
01140          //Detach the SSL/TLS context from the HTTP connection
01141          connection->tlsContext = NULL;
01142 #endif
01143          //Detach the socket from the HTTP connection
01144          connection->socket = NULL;
01145       }
01146       else
01147       {
01148          //Clean up side effects
01149          webSocketClose(webSocket);
01150          webSocket = NULL;
01151       }
01152    }
01153 #else
01154    //WebSockets are not supported
01155    webSocket = NULL;
01156 #endif
01157 
01158    //Return a handle to the freshly created WebSocket
01159    return webSocket;
01160 }
01161 
01162 
01163 #endif
01164