void httpConnectionTask(void *param) { error_t error; uint_t counter; HttpConnection *connection; //Point to the structure representing the HTTP connection connection = (HttpConnection *) param; //Endless loop while(1) { //Wait for an incoming connection attempt osWaitForEvent(&connection->startEvent, INFINITE_DELAY); //Initialize status code error = NO_ERROR; #if (HTTP_SERVER_TLS_SUPPORT == ENABLED) //Use SSL/TLS to secure the connection? if(connection->settings->useTls) { //Debug message TRACE_INFO("Initializing SSL/TLS session...\r\n"); //Start of exception handling block do { //Allocate SSL/TLS context connection->tlsContext = tlsInit(); //Initialization failed? if(connection->tlsContext == NULL) { //Report an error error = ERROR_OUT_OF_MEMORY; //Exit immediately break; } //Select server operation mode error = tlsSetConnectionEnd(connection->tlsContext, TLS_CONNECTION_END_SERVER); //Any error to report? if(error) break; //Bind TLS to the relevant socket error = tlsSetSocket(connection->tlsContext, connection->socket); //Any error to report? if(error) break; //Invoke user-defined callback, if any if(connection->settings->tlsInitCallback != NULL) { //Perform SSL/TLS related initialization error = connection->settings->tlsInitCallback(connection, connection->tlsContext); //Any error to report? if(error) break; } //Establish a secure session error = tlsConnect(connection->tlsContext); //Any error to report? if(error) break; //End of exception handling block } while(0); } else { //Do not use SSL/TLS connection->tlsContext = NULL; } #endif //Check status code if(!error) { //Process incoming requests for(counter = 0; counter < HTTP_SERVER_MAX_REQUESTS; counter++) { //Debug message TRACE_INFO("Waiting for request...\r\n"); //Clear request header memset(&connection->request, 0, sizeof(HttpRequest)); //Clear response header memset(&connection->response, 0, sizeof(HttpResponse)); //Read the HTTP request header and parse its contents error = httpReadHeader(connection); //Any error to report? if(error) { //Debug message TRACE_INFO("No HTTP request received or parsing error...\r\n"); break; } #if (HTTP_SERVER_BASIC_AUTH_SUPPORT == ENABLED || HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED) //No Authorization header found? if(!connection->request.auth.found) { //Invoke user-defined callback, if any if(connection->settings->authCallback != NULL) { //Check whether the access to the specified URI is authorized connection->status = connection->settings->authCallback(connection, connection->request.auth.user, connection->request.uri); } else { //Access to the specified URI is allowed connection->status = HTTP_ACCESS_ALLOWED; } } //Check access status if(connection->status == HTTP_ACCESS_ALLOWED) { //Access to the specified URI is allowed error = NO_ERROR; } else if(connection->status == HTTP_ACCESS_BASIC_AUTH_REQUIRED) { //Basic access authentication is required connection->response.auth.mode = HTTP_AUTH_MODE_BASIC; //Report an error error = ERROR_AUTH_REQUIRED; } else if(connection->status == HTTP_ACCESS_DIGEST_AUTH_REQUIRED) { //Digest access authentication is required connection->response.auth.mode = HTTP_AUTH_MODE_DIGEST; //Report an error error = ERROR_AUTH_REQUIRED; } else { //Access to the specified URI is denied error = ERROR_NOT_FOUND; } #endif //Debug message TRACE_INFO("Sending HTTP response to the client...\r\n"); //Check status code if(!error) { //Default HTTP header fields connection->response.version = connection->request.version; connection->response.noCache = FALSE; #if (HTTP_SERVER_PERSISTENT_CONN_SUPPORT == ENABLED) //Persistent connections are accepted connection->response.keepAlive = connection->request.keepAlive; #else //Connections are not persistent by default connection->response.keepAlive = FALSE; #endif #if (HTTP_SERVER_SSI_SUPPORT == ENABLED) //Use server-side scripting to dynamically generate HTML code? if(httpCompExtension(connection->request.uri, ".stm") || httpCompExtension(connection->request.uri, ".shtm") || httpCompExtension(connection->request.uri, ".shtml")) { //SSI processing (Server Side Includes) error = ssiExecuteScript(connection, connection->request.uri, 0); } else #endif { //Send the contents of the requested page error = httpSendResponse(connection, connection->request.uri); } //The requested resource is not available? if(error == ERROR_NOT_FOUND) { //Default HTTP header fields connection->response.version = connection->request.version; connection->response.statusCode = 200; connection->response.keepAlive = connection->request.keepAlive; connection->response.noCache = FALSE; connection->response.location = NULL; connection->response.contentType = mimeGetType(connection->request.uri); connection->response.chunkedEncoding = TRUE; //Invoke user-defined callback, if any if(connection->settings->uriNotFoundCallback != NULL) { error = connection->settings->uriNotFoundCallback(connection, connection->request.uri); } } } //Bad request? if(error == ERROR_INVALID_REQUEST) { //Send an error 400 and close the connection immediately httpSendErrorResponse(connection, 400, "The request is badly formed"); } //Authorization required? else if(error == ERROR_AUTH_REQUIRED) { //Send an error 401 and keep the connection alive error = httpSendErrorResponse(connection, 401, "Authorization required"); } //Page not found? else if(error == ERROR_NOT_FOUND) { //Send an error 404 and keep the connection alive error = httpSendErrorResponse(connection, 404, "The requested page could not be found"); } //Internal error? if(error) { //Close the connection immediately break; } //Check whether the connection is persistent or not if(!connection->request.keepAlive || !connection->response.keepAlive) { //Close the connection immediately break; } } } #if (HTTP_SERVER_TLS_SUPPORT == ENABLED) //Valid SSL/TLS context? if(connection->tlsContext != NULL) { //Debug message TRACE_INFO("Closing SSL/TLS session...\r\n"); //Gracefully close SSL/TLS session tlsShutdown(connection->tlsContext); //Release context tlsFree(connection->tlsContext); } #endif //Debug message TRACE_INFO("Graceful shutdown...\r\n"); //Graceful shutdown socketShutdown(connection->socket, SOCKET_SD_BOTH); //Debug message TRACE_INFO("Closing socket...\r\n"); //Close socket socketClose(connection->socket); //Ready to serve the next connection request... connection->running = FALSE; //Release semaphore osReleaseSemaphore(&connection->serverContext->semaphore); } }
error_t ssiProcessIncludeCommand(HttpConnection *connection, const char_t *tag, size_t length, const char_t *uri, uint_t level) { error_t error; char_t *separator; char_t *attribute; char_t *value; char_t *path; char_t *p; //Discard invalid SSI directives if(length < 7 || length >= HTTP_SERVER_BUFFER_SIZE) return ERROR_INVALID_TAG; //Skip the SSI include command (7 bytes) memcpy(connection->buffer, tag + 7, length - 7); //Ensure the resulting string is NULL-terminated connection->buffer[length - 7] = '\0'; //Check whether a separator is present separator = strchr(connection->buffer, '='); //Separator not found? if(!separator) return ERROR_INVALID_TAG; //Split the tag *separator = '\0'; //Get attribute name and value attribute = strTrimWhitespace(connection->buffer); value = strTrimWhitespace(separator + 1); //Remove leading simple or double quote if(value[0] == '\'' || value[0] == '\"') value++; //Get the length of the attribute value length = strlen(value); //Remove trailing simple or double quote if(length > 0) { if(value[length - 1] == '\'' || value[length - 1] == '\"') value[length - 1] = '\0'; } //Check the length of the filename if(strlen(value) > HTTP_SERVER_URI_MAX_LEN) return ERROR_INVALID_TAG; //The file parameter defines the included file as relative to the document path if(!strcasecmp(attribute, "file")) { //Allocate a buffer to hold the path to the file to be included path = osAllocMem(strlen(uri) + strlen(value) + 1); //Failed to allocate memory? if(!path) return ERROR_OUT_OF_MEMORY; //Copy the path identifying the script file being processed strcpy(path, uri); //Search for the last slash character p = strrchr(path, '/'); //Remove the filename from the path if applicable if(p) strcpy(p + 1, value); else strcpy(path, value); } //The virtual parameter defines the included file as relative to the document root else if(!strcasecmp(attribute, "virtual")) { //Copy the absolute path path = strDuplicate(value); //Failed to duplicate the string? if(!path) return ERROR_OUT_OF_MEMORY; } //Unknown parameter... else { //Report an error return ERROR_INVALID_TAG; } //Use server-side scripting to dynamically generate HTML code? if(httpCompExtension(value, ".stm") || httpCompExtension(value, ".shtm") || httpCompExtension(value, ".shtml")) { //SSI processing (Server Side Includes) error = ssiExecuteScript(connection, path, level + 1); } else { #if (HTTP_SERVER_FS_SUPPORT == ENABLED) FsFile *file; //Retrieve the full pathname httpGetAbsolutePath(connection, path, connection->buffer, HTTP_SERVER_BUFFER_SIZE); //Open the file for reading file = fsOpenFile(connection->buffer, FS_FILE_MODE_READ); //Successful operation? if(file) { //Send the contents of the requested file while(1) { //Read data from the specified file error = fsReadFile(file, connection->buffer, HTTP_SERVER_BUFFER_SIZE, &length); //End of input stream? if(error) break; //Send data to the client error = httpWriteStream(connection, connection->buffer, length); //Any error to report? if(error) break; } //Close the file fsCloseFile(file); //Successful file transfer? if(error == ERROR_END_OF_STREAM) error = NO_ERROR; } else { //The specified URI cannot be found error = ERROR_NOT_FOUND; } #else uint8_t *data; //Retrieve the full pathname httpGetAbsolutePath(connection, path, connection->buffer, HTTP_SERVER_BUFFER_SIZE); //Get the resource data associated with the file error = resGetData(connection->buffer, &data, &length); //Send the contents of the requested file if(!error) error = httpWriteStream(connection, data, length); #endif } //Cannot found the specified resource? if(error == ERROR_NOT_FOUND) error = ERROR_INVALID_TAG; //Release previously allocated memory osFreeMem(path); //return status code return error; }