Webserver+3d print

Dependents:   Nucleo

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers http_server_misc.c Source File

http_server_misc.c

Go to the documentation of this file.
00001 /**
00002  * @file http_server_misc.c
00003  * @brief HTTP server (miscellaneous functions)
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  * @author Oryx Embedded SARL (www.oryx-embedded.com)
00026  * @version 1.7.6
00027  **/
00028 
00029 //Switch to the appropriate trace level
00030 #define TRACE_LEVEL HTTP_TRACE_LEVEL
00031 
00032 //Dependencies
00033 #include <stdlib.h>
00034 #include <limits.h>
00035 #include "core/net.h"
00036 #include "http/http_server.h"
00037 #include "http/http_server_auth.h"
00038 #include "http/http_server_misc.h"
00039 #include "http/mime.h"
00040 #include "str.h"
00041 #include "path.h"
00042 #include "debug.h"
00043 
00044 //Check TCP/IP stack configuration
00045 #if (HTTP_SERVER_SUPPORT == ENABLED)
00046 
00047 
00048 /**
00049  * @brief HTTP status codes
00050  **/
00051 
00052 static const HttpStatusCodeDesc statusCodeList[] =
00053 {
00054    //Success
00055    {200, "OK"},
00056    {201, "Created"},
00057    {202, "Accepted"},
00058    {204, "No Content"},
00059    //Redirection
00060    {301, "Moved Permanently"},
00061    {302, "Found"},
00062    {304, "Not Modified"},
00063    //Client error
00064    {400, "Bad Request"},
00065    {401, "Unauthorized"},
00066    {403, "Forbidden"},
00067    {404, "Not Found"},
00068    //Server error
00069    {500, "Internal Server Error"},
00070    {501, "Not Implemented"},
00071    {502, "Bad Gateway"},
00072    {503, "Service Unavailable"}
00073 };
00074 
00075 
00076 /**
00077  * @brief Read HTTP request header and parse its contents
00078  * @param[in] connection Structure representing an HTTP connection
00079  * @return Error code
00080  **/
00081 
00082 error_t httpReadRequestHeader(HttpConnection *connection)
00083 {
00084    error_t error;
00085    size_t length;
00086 
00087    //Set the maximum time the server will wait for an HTTP
00088    //request before closing the connection
00089    error = socketSetTimeout(connection->socket, HTTP_SERVER_IDLE_TIMEOUT);
00090    //Any error to report?
00091    if(error)
00092       return error;
00093 
00094    //Read the first line of the request
00095    error = httpReceive(connection, connection->buffer,
00096       HTTP_SERVER_BUFFER_SIZE - 1, &length, SOCKET_FLAG_BREAK_CRLF);
00097    //Unable to read any data?
00098    if(error)
00099       return error;
00100 
00101    //Revert to default timeout
00102    error = socketSetTimeout(connection->socket, HTTP_SERVER_TIMEOUT);
00103    //Any error to report?
00104    if(error)
00105       return error;
00106 
00107    //Properly terminate the string with a NULL character
00108    connection->buffer[length] = '\0';
00109    //Debug message
00110    TRACE_INFO("%s", connection->buffer);
00111 
00112    //Parse the Request-Line
00113    error = httpParseRequestLine(connection, connection->buffer);
00114    //Any error to report?
00115    if(error)
00116       return error;
00117 
00118    //Default value for properties
00119    connection->request.chunkedEncoding = FALSE;
00120    connection->request.contentLength = 0;
00121 #if (HTTP_SERVER_WEB_SOCKET_SUPPORT == ENABLED)
00122    connection->request.upgradeWebSocket = FALSE;
00123    connection->request.connectionUpgrade = FALSE;
00124    strcpy(connection->request.clientKey, "");
00125 #endif
00126 
00127    //HTTP 0.9 does not support Full-Request
00128    if(connection->request.version >= HTTP_VERSION_1_0)
00129    {
00130       //Local variables
00131       char_t firstChar;
00132       char_t *separator;
00133       char_t *name;
00134       char_t *value;
00135 
00136       //This variable is used to decode header fields that span multiple lines
00137       firstChar = '\0';
00138 
00139       //Parse the header fields of the HTTP request
00140       while(1)
00141       {
00142          //Decode multiple-line header field
00143          error = httpReadHeaderField(connection, connection->buffer,
00144             HTTP_SERVER_BUFFER_SIZE, &firstChar);
00145          //Any error to report?
00146          if(error)
00147             return error;
00148 
00149          //Debug message
00150          TRACE_DEBUG("%s", connection->buffer);
00151 
00152          //An empty line indicates the end of the header fields
00153          if(!strcmp(connection->buffer, "\r\n"))
00154             break;
00155 
00156          //Check whether a separator is present
00157          separator = strchr(connection->buffer, ':');
00158 
00159          //Separator found?
00160          if(separator != NULL)
00161          {
00162             //Split the line
00163             *separator = '\0';
00164 
00165             //Trim whitespace characters
00166             name = strTrimWhitespace(connection->buffer);
00167             value = strTrimWhitespace(separator + 1);
00168 
00169             //Parse HTTP header field
00170             httpParseHeaderField(connection, name, value);
00171          }
00172       }
00173    }
00174 
00175    //Prepare to read the HTTP request body
00176    if(connection->request.chunkedEncoding)
00177    {
00178       connection->request.byteCount = 0;
00179       connection->request.firstChunk = TRUE;
00180       connection->request.lastChunk = FALSE;
00181    }
00182    else
00183    {
00184       connection->request.byteCount = connection->request.contentLength;
00185    }
00186 
00187    //The request header has been successfully parsed
00188    return NO_ERROR;
00189 }
00190 
00191 
00192 /**
00193  * @brief Parse Request-Line
00194  * @param[in] connection Structure representing an HTTP connection
00195  * @param[in] requestLine Pointer to the string that holds the Request-Line
00196  * @return Error code
00197  **/
00198 
00199 error_t httpParseRequestLine(HttpConnection *connection, char_t *requestLine)
00200 {
00201    error_t error;
00202    char_t *token;
00203    char_t *p;
00204    char_t *s;
00205 
00206    //The Request-Line begins with a method token
00207    token = strtok_r(requestLine, " \r\n", &p);
00208    //Unable to retrieve the method?
00209    if(token == NULL)
00210       return ERROR_INVALID_REQUEST;
00211 
00212    //The Method token indicates the method to be performed on the
00213    //resource identified by the Request-URI
00214    error = strSafeCopy(connection->request.method, token, HTTP_SERVER_METHOD_MAX_LEN);
00215    //Any error to report?
00216    if(error)
00217       return ERROR_INVALID_REQUEST;
00218 
00219    //The Request-URI is following the method token
00220    token = strtok_r(NULL, " \r\n", &p);
00221    //Unable to retrieve the Request-URI?
00222    if(token == NULL)
00223       return ERROR_INVALID_REQUEST;
00224 
00225    //Check whether a query string is present
00226    s = strchr(token, '?');
00227 
00228    //Query string found?
00229    if(s != NULL)
00230    {
00231       //Split the string
00232       *s = '\0';
00233 
00234       //Save the Request-URI
00235       error = httpDecodePercentEncodedString(token,
00236          connection->request.uri, HTTP_SERVER_URI_MAX_LEN);
00237       //Any error to report?
00238       if(error)
00239          return ERROR_INVALID_REQUEST;
00240 
00241       //Check the length of the query string
00242       if(strlen(s + 1) > HTTP_SERVER_QUERY_STRING_MAX_LEN)
00243          return ERROR_INVALID_REQUEST;
00244 
00245       //Save the query string
00246       strcpy(connection->request.queryString, s + 1);
00247    }
00248    else
00249    {
00250       //Save the Request-URI
00251       error = httpDecodePercentEncodedString(token,
00252          connection->request.uri, HTTP_SERVER_URI_MAX_LEN);
00253       //Any error to report?
00254       if(error)
00255          return ERROR_INVALID_REQUEST;
00256 
00257       //No query string
00258       connection->request.queryString[0] = '\0';
00259    }
00260 
00261    //Redirect to the default home page if necessary
00262    if(!strcasecmp(connection->request.uri, "/"))
00263       strcpy(connection->request.uri, connection->settings->defaultDocument);
00264 
00265    //Clean the resulting path
00266    pathCanonicalize(connection->request.uri);
00267 
00268    //The protocol version is following the Request-URI
00269    token = strtok_r(NULL, " \r\n", &p);
00270 
00271    //HTTP version 0.9?
00272    if(token == NULL)
00273    {
00274       //Save version number
00275       connection->request.version = HTTP_VERSION_0_9;
00276       //Persistent connections are not supported
00277       connection->request.keepAlive = FALSE;
00278    }
00279    //HTTP version 1.0?
00280    else if(!strcasecmp(token, "HTTP/1.0"))
00281    {
00282       //Save version number
00283       connection->request.version = HTTP_VERSION_1_0;
00284       //By default connections are not persistent
00285       connection->request.keepAlive = FALSE;
00286    }
00287    //HTTP version 1.1?
00288    else if(!strcasecmp(token, "HTTP/1.1"))
00289    {
00290       //Save version number
00291       connection->request.version = HTTP_VERSION_1_1;
00292       //HTTP 1.1 makes persistent connections the default
00293       connection->request.keepAlive = TRUE;
00294    }
00295    //HTTP version not supported?
00296    else
00297    {
00298       //Report an error
00299       return ERROR_INVALID_REQUEST;
00300    }
00301 
00302    //Successful processing
00303    return NO_ERROR;
00304 }
00305 
00306 
00307 /**
00308  * @brief Read multiple-line header field
00309  * @param[in] connection Structure representing an HTTP connection
00310  * @param[out] buffer Buffer where to store the header field
00311  * @param[in] size Size of the buffer, in bytes
00312  * @param[in,out] firstChar Leading character of the header line
00313  * @return Error code
00314  **/
00315 
00316 error_t httpReadHeaderField(HttpConnection *connection,
00317    char_t *buffer, size_t size, char_t *firstChar)
00318 {
00319    error_t error;
00320    size_t n;
00321    size_t length;
00322 
00323    //This is the actual length of the header field
00324    length = 0;
00325 
00326    //The process of moving from a multiple-line representation of a header
00327    //field to its single line representation is called unfolding
00328    do
00329    {
00330       //Check the length of the header field
00331       if((length + 1) >= size)
00332       {
00333          //Report an error
00334          error = ERROR_INVALID_REQUEST;
00335          //Exit immediately
00336          break;
00337       }
00338 
00339       //NULL character found?
00340       if(*firstChar == '\0')
00341       {
00342          //Prepare to decode the first header field
00343          length = 0;
00344       }
00345       //LWSP character found?
00346       else if(*firstChar == ' ' || *firstChar == '\t')
00347       {
00348          //Unfolding is accomplished by regarding CRLF immediately
00349          //followed by a LWSP as equivalent to the LWSP character
00350          buffer[length] = *firstChar;
00351          //The current header field spans multiple lines
00352          length++;
00353       }
00354       //Any other character?
00355       else
00356       {
00357          //Restore the very first character of the header field
00358          buffer[0] = *firstChar;
00359          //Prepare to decode a new header field
00360          length = 1;
00361       }
00362 
00363       //Read data until a CLRF character is encountered
00364       error = httpReceive(connection, buffer + length,
00365          size - 1 - length, &n, SOCKET_FLAG_BREAK_CRLF);
00366       //Any error to report?
00367       if(error)
00368          break;
00369 
00370       //Update the length of the header field
00371       length += n;
00372       //Properly terminate the string with a NULL character
00373       buffer[length] = '\0';
00374 
00375       //An empty line indicates the end of the header fields
00376       if(!strcmp(buffer, "\r\n"))
00377          break;
00378 
00379       //Read the next character to detect if the CRLF is immediately
00380       //followed by a LWSP character
00381       error = httpReceive(connection, firstChar,
00382          sizeof(char_t), &n, SOCKET_FLAG_WAIT_ALL);
00383       //Any error to report?
00384       if(error)
00385          break;
00386 
00387       //LWSP character found?
00388       if(*firstChar == ' ' || *firstChar == '\t')
00389       {
00390          //CRLF immediately followed by LWSP as equivalent to the LWSP character
00391          if(length >= 2)
00392          {
00393             if(buffer[length - 2] == '\r' || buffer[length - 1] == '\n')
00394             {
00395                //Remove trailing CRLF sequence
00396                length -= 2;
00397                //Properly terminate the string with a NULL character
00398                buffer[length] = '\0';
00399             }
00400          }
00401       }
00402 
00403       //A header field may span multiple lines...
00404    } while(*firstChar == ' ' || *firstChar == '\t');
00405 
00406    //Return status code
00407    return error;
00408 }
00409 
00410 
00411 /**
00412  * @brief Parse HTTP header field
00413  * @param[in] connection Structure representing an HTTP connection
00414  * @param[in] name Name of the header field
00415  * @param[in] value Value of the header field
00416  * @return Error code
00417  **/
00418 
00419 void httpParseHeaderField(HttpConnection *connection,
00420    const char_t *name, char_t *value)
00421 {
00422    //Host header field?
00423    if(!strcasecmp(name, "Host"))
00424    {
00425       //Save host name
00426       strSafeCopy(connection->request.host, value,
00427          HTTP_SERVER_HOST_MAX_LEN);
00428    }
00429    //Connection header field?
00430    else if(!strcasecmp(name, "Connection"))
00431    {
00432       //Parse Connection header field
00433       httpParseConnectionField(connection, value);
00434    }
00435    //Transfer-Encoding header field?
00436    else if(!strcasecmp(name, "Transfer-Encoding"))
00437    {
00438       //Check whether chunked encoding is used
00439       if(!strcasecmp(value, "chunked"))
00440          connection->request.chunkedEncoding = TRUE;
00441    }
00442    //Content-Type field header?
00443    else if(!strcasecmp(name, "Content-Type"))
00444    {
00445       //Parse Content-Type header field
00446       httpParseContentTypeField(connection, value);
00447    }
00448    //Content-Length header field?
00449    else if(!strcasecmp(name, "Content-Length"))
00450    {
00451       //Get the length of the body data
00452       connection->request.contentLength = atoi(value);
00453    }
00454    //Authorization header field?
00455    else if(!strcasecmp(name, "Authorization"))
00456    {
00457       //Parse Authorization header field
00458       httpParseAuthorizationField(connection, value);
00459    }
00460 #if (HTTP_SERVER_WEB_SOCKET_SUPPORT == ENABLED)
00461    //Upgrade header field?
00462    else if(!strcasecmp(name, "Upgrade"))
00463    {
00464       //WebSocket support?
00465       if(!strcasecmp(value, "websocket"))
00466          connection->request.upgradeWebSocket = TRUE;
00467    }
00468    //Sec-WebSocket-Key header field?
00469    else if(!strcasecmp(name, "Sec-WebSocket-Key"))
00470    {
00471       //Save the contents of the Sec-WebSocket-Key header field
00472       strSafeCopy(connection->request.clientKey, value,
00473          WEB_SOCKET_CLIENT_KEY_SIZE + 1);
00474    }
00475 #endif
00476 }
00477 
00478 
00479 /**
00480  * @brief Parse Connection header field
00481  * @param[in] connection Structure representing an HTTP connection
00482  * @param[in] value Content-Type field value
00483  **/
00484 
00485 void httpParseConnectionField(HttpConnection *connection,
00486    char_t *value)
00487 {
00488    char_t *p;
00489    char_t *token;
00490 
00491    //Get the first value of the list
00492    token = strtok_r(value, ",", &p);
00493 
00494    //Parse the comma-separated list
00495    while(token != NULL)
00496    {
00497       //Trim whitespace characters
00498       value = strTrimWhitespace(token);
00499 
00500       //Check current value
00501       if(!strcasecmp(value, "keep-alive"))
00502       {
00503          //The connection is persistent
00504          connection->request.keepAlive = TRUE;
00505       }
00506       else if(!strcasecmp(value, "close"))
00507       {
00508          //The connection will be closed after completion of the response
00509          connection->request.keepAlive = FALSE;
00510       }
00511 #if (HTTP_SERVER_WEB_SOCKET_SUPPORT == ENABLED)
00512       else if(!strcasecmp(value, "upgrade"))
00513       {
00514          //Upgrade the connection
00515          connection->request.connectionUpgrade = TRUE;
00516       }
00517 #endif
00518 
00519       //Get next value
00520       token = strtok_r(NULL, ",", &p);
00521    }
00522 }
00523 
00524 
00525 /**
00526  * @brief Parse Content-Type header field
00527  * @param[in] connection Structure representing an HTTP connection
00528  * @param[in] value Content-Type field value
00529  **/
00530 
00531 void httpParseContentTypeField(HttpConnection *connection,
00532    char_t *value)
00533 {
00534 #if (HTTP_SERVER_MULTIPART_TYPE_SUPPORT == ENABLED)
00535    size_t n;
00536    char_t *p;
00537    char_t *token;
00538 
00539    //Retrieve type
00540    token = strtok_r(value, "/", &p);
00541    //Any parsing error?
00542    if(token == NULL)
00543       return;
00544 
00545    //The boundary parameter makes sense only for the multipart content-type
00546    if(!strcasecmp(token, "multipart"))
00547    {
00548       //Skip subtype
00549       token = strtok_r(NULL, ";", &p);
00550       //Any parsing error?
00551       if(token == NULL)
00552          return;
00553 
00554       //Retrieve parameter name
00555       token = strtok_r(NULL, "=", &p);
00556       //Any parsing error?
00557       if(token == NULL)
00558          return;
00559 
00560       //Trim whitespace characters
00561       token = strTrimWhitespace(token);
00562 
00563       //Check parameter name
00564       if(!strcasecmp(token, "boundary"))
00565       {
00566          //Retrieve parameter value
00567          token = strtok_r(NULL, ";", &p);
00568          //Any parsing error?
00569          if(token == NULL)
00570             return;
00571 
00572          //Trim whitespace characters
00573          token = strTrimWhitespace(token);
00574          //Get the length of the boundary string
00575          n = strlen(token);
00576 
00577          //Check the length of the boundary string
00578          if(n < HTTP_SERVER_BOUNDARY_MAX_LEN)
00579          {
00580             //Copy the boundary string
00581             strncpy(connection->request.boundary, token, n);
00582             //Properly terminate the string
00583             connection->request.boundary[n] = '\0';
00584 
00585             //Save the length of the boundary string
00586             connection->request.boundaryLength = n;
00587          }
00588       }
00589    }
00590 #endif
00591 }
00592 
00593 
00594 /**
00595  * @brief Read chunk-size field from the input stream
00596  * @param[in] connection Structure representing an HTTP connection
00597  **/
00598 
00599 error_t httpReadChunkSize(HttpConnection *connection)
00600 {
00601    error_t error;
00602    size_t n;
00603    char_t *end;
00604    char_t s[8];
00605 
00606    //First chunk to be received?
00607    if(connection->request.firstChunk)
00608    {
00609       //Clear the flag
00610       connection->request.firstChunk = FALSE;
00611    }
00612    else
00613    {
00614       //Read the CRLF that follows the previous chunk-data field
00615       error = httpReceive(connection, s, sizeof(s) - 1, &n, SOCKET_FLAG_BREAK_CRLF);
00616       //Any error to report?
00617       if(error)
00618          return error;
00619 
00620       //Properly terminate the string with a NULL character
00621       s[n] = '\0';
00622 
00623       //The chunk data must be terminated by CRLF
00624       if(strcmp(s, "\r\n"))
00625          return ERROR_WRONG_ENCODING;
00626    }
00627 
00628    //Read the chunk-size field
00629    error = httpReceive(connection, s, sizeof(s) - 1, &n, SOCKET_FLAG_BREAK_CRLF);
00630    //Any error to report?
00631    if(error)
00632       return error;
00633 
00634    //Properly terminate the string with a NULL character
00635    s[n] = '\0';
00636    //Remove extra whitespaces
00637    strRemoveTrailingSpace(s);
00638 
00639    //Retrieve the size of the chunk
00640    connection->request.byteCount = strtoul(s, &end, 16);
00641 
00642    //No valid conversion could be performed?
00643    if(end == s || *end != '\0')
00644       return ERROR_WRONG_ENCODING;
00645 
00646    //Any chunk whose size is zero terminates the data transfer
00647    if(!connection->request.byteCount)
00648    {
00649       //The end of the HTTP request body has been reached
00650       connection->request.lastChunk = TRUE;
00651 
00652       //Skip the trailer
00653       while(1)
00654       {
00655          //Read a complete line
00656          error = httpReceive(connection, s, sizeof(s) - 1, &n, SOCKET_FLAG_BREAK_CRLF);
00657          //Unable to read any data?
00658          if(error)
00659             return error;
00660 
00661          //Properly terminate the string with a NULL character
00662          s[n] = '\0';
00663 
00664          //The trailer is terminated by an empty line
00665          if(!strcmp(s, "\r\n"))
00666             break;
00667       }
00668    }
00669 
00670    //Successful processing
00671    return NO_ERROR;
00672 }
00673 
00674 
00675 /**
00676  * @brief Initialize response header
00677  * @param[in] connection Structure representing an HTTP connection
00678  **/
00679 
00680 void httpInitResponseHeader(HttpConnection *connection)
00681 {
00682    //Default HTTP header fields
00683    connection->response.version = connection->request.version;
00684    connection->response.statusCode = 200;
00685    connection->response.noCache = FALSE;
00686    connection->response.maxAge = 0;
00687    connection->response.location = NULL;
00688    connection->response.contentType = mimeGetType(connection->request.uri);
00689    connection->response.chunkedEncoding = TRUE;
00690 
00691 #if (HTTP_SERVER_PERSISTENT_CONN_SUPPORT == ENABLED)
00692    //Persistent connections are accepted
00693    connection->response.keepAlive = connection->request.keepAlive;
00694 #else
00695    //Connections are not persistent by default
00696    connection->response.keepAlive = FALSE;
00697 #endif
00698 }
00699 
00700 
00701 /**
00702  * @brief Format HTTP response header
00703  * @param[in] connection Structure representing an HTTP connection
00704  * @param[out] buffer Pointer to the buffer where to format the HTTP header
00705  * @return Error code
00706  **/
00707 
00708 error_t httpFormatResponseHeader(HttpConnection *connection, char_t *buffer)
00709 {
00710    uint_t i;
00711    char_t *p;
00712 
00713    //HTTP version 0.9?
00714    if(connection->response.version == HTTP_VERSION_0_9)
00715    {
00716       //Enforce default parameters
00717       connection->response.keepAlive = FALSE;
00718       connection->response.chunkedEncoding = FALSE;
00719       //The size of the response body is not limited
00720       connection->response.byteCount = UINT_MAX;
00721       //We are done since HTTP 0.9 does not support Full-Response format
00722       return NO_ERROR;
00723    }
00724 
00725    //When generating dynamic web pages with HTTP 1.0, the only way to
00726    //signal the end of the body is to close the connection
00727    if(connection->response.version == HTTP_VERSION_1_0 &&
00728       connection->response.chunkedEncoding)
00729    {
00730       //Make the connection non persistent
00731       connection->response.keepAlive = FALSE;
00732       connection->response.chunkedEncoding = FALSE;
00733       //The size of the response body is not limited
00734       connection->response.byteCount = UINT_MAX;
00735    }
00736    else
00737    {
00738       //Limit the size of the response body
00739       connection->response.byteCount = connection->response.contentLength;
00740    }
00741 
00742    //Point to the beginning of the buffer
00743    p = buffer;
00744 
00745    //The first line of a response message is the Status-Line, consisting
00746    //of the protocol version followed by a numeric status code and its
00747    //associated textual phrase
00748    p += sprintf(p, "HTTP/%u.%u %u ", MSB(connection->response.version),
00749       LSB(connection->response.version), connection->response.statusCode);
00750 
00751    //Retrieve the Reason-Phrase that corresponds to the Status-Code
00752    for(i = 0; i < arraysize(statusCodeList); i++)
00753    {
00754       //Check the status code
00755       if(statusCodeList[i].value == connection->response.statusCode)
00756       {
00757          //Append the textual phrase to the Status-Line
00758          p += sprintf(p, statusCodeList[i].message);
00759          //Break the loop and continue processing
00760          break;
00761       }
00762    }
00763 
00764    //Properly terminate the Status-Line
00765    p += sprintf(p, "\r\n");
00766    //The Server response-header field contains information about the
00767    //software used by the origin server to handle the request
00768    p += sprintf(p, "Server: Oryx Embedded HTTP Server\r\n");
00769 
00770    //Valid location?
00771    if(connection->response.location != NULL)
00772    {
00773       //Set Location field
00774       p += sprintf(p, "Location: %s\r\n", connection->response.location);
00775    }
00776 
00777    //Persistent connection?
00778    if(connection->response.keepAlive)
00779    {
00780       //Set Connection field
00781       p += sprintf(p, "Connection: keep-alive\r\n");
00782 
00783       //Set Keep-Alive field
00784       p += sprintf(p, "Keep-Alive: timeout=%u, max=%u\r\n",
00785          HTTP_SERVER_IDLE_TIMEOUT / 1000, HTTP_SERVER_MAX_REQUESTS);
00786    }
00787    else
00788    {
00789       //Set Connection field
00790       p += sprintf(p, "Connection: close\r\n");
00791    }
00792 
00793    //Specify the caching policy
00794    if(connection->response.noCache)
00795    {
00796       //Set Pragma field
00797       p += sprintf(p, "Pragma: no-cache\r\n");
00798       //Set Cache-Control field
00799       p += sprintf(p, "Cache-Control: no-store, no-cache, must-revalidate\r\n");
00800       p += sprintf(p, "Cache-Control: max-age=0, post-check=0, pre-check=0\r\n");
00801    }
00802    else if(connection->response.maxAge != 0)
00803    {
00804       //Set Cache-Control field
00805       p += sprintf(p, "Cache-Control: max-age=%u\r\n", connection->response.maxAge);
00806    }
00807 
00808 #if (HTTP_SERVER_BASIC_AUTH_SUPPORT == ENABLED || HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED)
00809    //Check whether authentication is required
00810    if(connection->response.auth.mode != HTTP_AUTH_MODE_NONE)
00811    {
00812       //Add WWW-Authenticate header field
00813       p += httpAddAuthenticateField(connection, p);
00814    }
00815 #endif
00816 
00817    //Valid content type?
00818    if(connection->response.contentType != NULL)
00819    {
00820       //Content type
00821       p += sprintf(p, "Content-Type: %s\r\n", connection->response.contentType);
00822    }
00823 
00824    //Use chunked encoding transfer?
00825    if(connection->response.chunkedEncoding)
00826    {
00827       //Set Transfer-Encoding field
00828       p += sprintf(p, "Transfer-Encoding: chunked\r\n");
00829    }
00830    //Persistent connection?
00831    else if(connection->response.keepAlive)
00832    {
00833       //Set Content-Length field
00834       p += sprintf(p, "Content-Length: %" PRIuSIZE "\r\n", connection->response.contentLength);
00835    }
00836 
00837    //Terminate the header with an empty line
00838    p += sprintf(p, "\r\n");
00839 
00840    //Successful processing
00841    return NO_ERROR;
00842 }
00843 
00844 
00845 /**
00846  * @brief Send data to the client
00847  * @param[in] connection Structure representing an HTTP connection
00848  * @param[in] data Pointer to a buffer containing the data to be transmitted
00849  * @param[in] length Number of bytes to be transmitted
00850  * @param[in] flags Set of flags that influences the behavior of this function
00851  **/
00852 
00853 error_t httpSend(HttpConnection *connection,
00854    const void *data, size_t length, uint_t flags)
00855 {
00856 #if (NET_RTOS_SUPPORT == ENABLED)
00857       error_t error;
00858 
00859 #if (HTTP_SERVER_TLS_SUPPORT == ENABLED)
00860    //Check whether a secure connection is being used
00861    if(connection->tlsContext != NULL)
00862    {
00863       //Use SSL/TLS to transmit data to the client
00864       error = tlsWrite(connection->tlsContext, data, length, NULL, flags);
00865    }
00866    else
00867 #endif
00868    {
00869       //Transmit data to the client
00870       error = socketSend(connection->socket, data, length, NULL, flags);
00871    }
00872 
00873    //Return status code
00874    return error;
00875 #else
00876    //Prevent buffer overflow
00877    if((connection->bufferLen + length) > HTTP_SERVER_BUFFER_SIZE)
00878       return ERROR_BUFFER_OVERFLOW;
00879 
00880    //Copy user data
00881    memcpy(connection->buffer + connection->bufferLen, data, length);
00882    //Adjust the length of the buffer
00883    connection->bufferLen += length;
00884 
00885    //Successful processing
00886    return NO_ERROR;
00887 #endif
00888 }
00889 
00890 
00891 /**
00892  * @brief Receive data from the client
00893  * @param[in] connection Structure representing an HTTP connection
00894  * @param[out] data Buffer into which received data will be placed
00895  * @param[in] size Maximum number of bytes that can be received
00896  * @param[out] received Actual number of bytes that have been received
00897  * @param[in] flags Set of flags that influences the behavior of this function
00898  * @return Error code
00899  **/
00900 
00901 error_t httpReceive(HttpConnection *connection,
00902    void *data, size_t size, size_t *received, uint_t flags)
00903 {
00904    error_t error;
00905 
00906 #if (HTTP_SERVER_TLS_SUPPORT == ENABLED)
00907    //Check whether a secure connection is being used
00908    if(connection->tlsContext != NULL)
00909    {
00910       //Use SSL/TLS to receive data from the client
00911       error = tlsRead(connection->tlsContext, data, size, received, flags);
00912    }
00913    else
00914 #endif
00915    {
00916       //Receive data from the client
00917       error = socketReceive(connection->socket, data, size, received, flags);
00918    }
00919 
00920    //Return status code
00921    return error;
00922 }
00923 
00924 
00925 /**
00926  * @brief Retrieve the full pathname to the specified resource
00927  * @param[in] connection Structure representing an HTTP connection
00928  * @param[in] relative String containing the relative path to the resource
00929  * @param[out] absolute Resulting string containing the absolute path
00930  * @param[in] maxLen Maximum acceptable path length
00931  **/
00932 
00933 void httpGetAbsolutePath(HttpConnection *connection,
00934    const char_t *relative, char_t *absolute, size_t maxLen)
00935 {
00936    //Copy the root directory
00937    strcpy(absolute, connection->settings->rootDirectory);
00938 
00939    //Append the specified path
00940    pathCombine(absolute, relative, maxLen);
00941 
00942    //Clean the resulting path
00943    pathCanonicalize(absolute);
00944 }
00945 
00946 
00947 /**
00948  * @brief Compare filename extension
00949  * @param[in] filename Filename whose extension is to be checked
00950  * @param[in] extension String defining the extension to be checked
00951  * @return TRUE is the filename matches the given extension, else FALSE
00952  **/
00953 
00954 bool_t httpCompExtension(const char_t *filename, const char_t *extension)
00955 {
00956    uint_t n;
00957    uint_t m;
00958 
00959    //Get the length of the specified filename
00960    n = strlen(filename);
00961    //Get the length of the extension
00962    m = strlen(extension);
00963 
00964    //Check the length of the filename
00965    if(n < m)
00966       return FALSE;
00967 
00968    //Compare extensions
00969    if(!strncasecmp(filename + n - m, extension, m))
00970       return TRUE;
00971    else
00972       return FALSE;
00973 }
00974 
00975 
00976 /**
00977  * @brief Decode a percent-encoded string
00978  * @param[in] input NULL-terminated string to be decoded
00979  * @param[out] output NULL-terminated string resulting from the decoding process
00980  * @param[in] outputSize Size of the output buffer in bytes
00981  * @return Error code
00982  **/
00983 
00984 error_t httpDecodePercentEncodedString(const char_t *input,
00985    char_t *output, size_t outputSize)
00986 {
00987    size_t i;
00988    char_t buffer[3];
00989 
00990    //Check parameters
00991    if(input == NULL || output == NULL)
00992       return ERROR_INVALID_PARAMETER;
00993 
00994    //Decode the percent-encoded string
00995    for(i = 0; *input != '\0' && i < outputSize; i++)
00996    {
00997       //Check current character
00998       if(*input == '+')
00999       {
01000          //Replace '+' characters with spaces
01001          output[i] = ' ';
01002          //Advance data pointer
01003          input++;
01004       }
01005       else if(input[0] == '%' && input[1] != '\0' && input[2] != '\0')
01006       {
01007          //Process percent-encoded characters
01008          buffer[0] = input[1];
01009          buffer[1] = input[2];
01010          buffer[2] = '\0';
01011          //String to integer conversion
01012          output[i] = (uint8_t) strtoul(buffer, NULL, 16);
01013          //Advance data pointer
01014          input += 3;
01015       }
01016       else
01017       {
01018          //Copy any other characters
01019          output[i] = *input;
01020          //Advance data pointer
01021          input++;
01022       }
01023    }
01024 
01025    //Check whether the output buffer runs out of space
01026    if(i >= outputSize)
01027       return ERROR_FAILURE;
01028 
01029    //Properly terminate the resulting string
01030    output[i] = '\0';
01031    //Successful processing
01032    return NO_ERROR;
01033 }
01034 
01035 
01036 /**
01037  * @brief Convert byte array to hex string
01038  * @param[in] input Point to the byte array
01039  * @param[in] inputLength Length of the byte array
01040  * @param[out] output NULL-terminated string resulting from the conversion
01041  * @return Error code
01042  **/
01043 
01044 void httpConvertArrayToHexString(const uint8_t *input,
01045    size_t inputLength, char_t *output)
01046 {
01047    size_t i;
01048 
01049    //Hex conversion table
01050    static const char_t hexDigit[] =
01051    {
01052       '0', '1', '2', '3', '4', '5', '6', '7',
01053       '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
01054    };
01055 
01056    //Process byte array
01057    for(i = 0; i < inputLength; i++)
01058    {
01059       //Convert upper nibble
01060       output[i * 2] = hexDigit[(input[i] >> 4) & 0x0F];
01061       //Then convert lower nibble
01062       output[i * 2 + 1] = hexDigit[input[i] & 0x0F];
01063    }
01064 
01065    //Properly terminate the string with a NULL character
01066    output[i * 2] = '\0';
01067 }
01068 
01069 #endif
01070