Webserver+3d print

Dependents:   Nucleo

Committer:
Sergunb
Date:
Sat Feb 04 18:15:49 2017 +0000
Revision:
0:8918a71cdbe9
nothing else

Who changed what in which revision?

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