Webserver+3d print
Embed:
(wiki syntax)
Show/hide line numbers
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
Generated on Tue Jul 12 2022 17:10:13 by
