Webserver+3d print
cyclone_tcp/http/http_server.c
- Committer:
- Sergunb
- Date:
- 2017-02-04
- Revision:
- 0:8918a71cdbe9
File content as of revision 0:8918a71cdbe9:
/** * @file http_server.c * @brief HTTP server (HyperText Transfer Protocol) * * @section License * * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved. * * This file is part of CycloneTCP Open. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * @section Description * * Using the HyperText Transfer Protocol, the HTTP server delivers web pages * to browsers as well as other data files to web-based applications. Refers * to the following RFCs for complete details: * - RFC 1945: Hypertext Transfer Protocol - HTTP/1.0 * - RFC 2616: Hypertext Transfer Protocol - HTTP/1.1 * - RFC 2617: HTTP Authentication: Basic and Digest Access Authentication * - RFC 2818: HTTP Over TLS * * @author Oryx Embedded SARL (www.oryx-embedded.com) * @version 1.7.6 **/ //Switch to the appropriate trace level #define TRACE_LEVEL HTTP_TRACE_LEVEL //Dependencies #include <stdlib.h> #include "core/net.h" #include "http/http_server.h" #include "http/http_server_auth.h" #include "http/http_server_misc.h" #include "http/mime.h" #include "http/ssi.h" #include "debug.h" //Check TCP/IP stack configuration #if (HTTP_SERVER_SUPPORT == ENABLED) /** * @brief Initialize settings with default values * @param[out] settings Structure that contains HTTP server settings **/ void httpServerGetDefaultSettings(HttpServerSettings *settings) { //The HTTP server is not bound to any interface settings->interface = NULL; //Listen to port 80 settings->port = HTTP_PORT; //Maximum length of the pending connection queue settings->backlog = HTTP_SERVER_BACKLOG; //Client connections settings->maxConnections = 0; settings->connections = NULL; //Specify the server's root directory strcpy(settings->rootDirectory, "/"); //Set default home page strcpy(settings->defaultDocument, "index.htm"); #if (HTTP_SERVER_TLS_SUPPORT == ENABLED) //SSL/TLS initialization callback function settings->tlsInitCallback = NULL; #endif #if (HTTP_SERVER_BASIC_AUTH_SUPPORT == ENABLED || HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED) //Random data generation callback function settings->randCallback = NULL; //HTTP authentication callback function settings->authCallback = NULL; #endif //CGI callback function settings->cgiCallback = NULL; //HTTP request callback function settings->requestCallback = NULL; //URI not found callback function settings->uriNotFoundCallback = NULL; } /** * @brief HTTP server initialization * @param[in] context Pointer to the HTTP server context * @param[in] settings HTTP server specific settings * @return Error code **/ error_t httpServerInit(HttpServerContext *context, const HttpServerSettings *settings) { error_t error; uint_t i; HttpConnection *connection; //Debug message TRACE_INFO("Initializing HTTP server...\r\n"); //Ensure the parameters are valid if(context == NULL || settings == NULL) return ERROR_INVALID_PARAMETER; //Check user settings if(settings->maxConnections == 0 || settings->connections == NULL) return ERROR_INVALID_PARAMETER; //Clear the HTTP server context memset(context, 0, sizeof(HttpServerContext)); //Save user settings context->settings = *settings; //Client connections context->connections = settings->connections; //Create a semaphore to limit the number of simultaneous connections if(!osCreateSemaphore(&context->semaphore, context->settings.maxConnections)) return ERROR_OUT_OF_RESOURCES; //Loop through client connections for(i = 0; i < context->settings.maxConnections; i++) { //Point to the structure representing the client connection connection = &context->connections[i]; //Initialize the structure memset(connection, 0, sizeof(HttpConnection)); //Create an event object to manage connection lifetime if(!osCreateEvent(&connection->startEvent)) return ERROR_OUT_OF_RESOURCES; } #if (HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED) //Create a mutex to prevent simultaneous access to the nonce cache if(!osCreateMutex(&context->nonceCacheMutex)) return ERROR_OUT_OF_RESOURCES; #endif //Open a TCP socket context->socket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP); //Failed to open socket? if(!context->socket) return ERROR_OPEN_FAILED; //Set timeout for blocking functions error = socketSetTimeout(context->socket, INFINITE_DELAY); //Any error to report? if(error) return error; //Associate the socket with the relevant interface error = socketBindToInterface(context->socket, settings->interface); //Unable to bind the socket to the desired interface? if(error) return error; //Bind newly created socket to port 80 error = socketBind(context->socket, &IP_ADDR_ANY, settings->port); //Failed to bind socket to port 80? if(error) return error; //Place socket in listening state error = socketListen(context->socket, settings->backlog); //Any failure to report? if(error) return error; //Successful initialization return NO_ERROR; } /** * @brief Start HTTP server * @param[in] context Pointer to the HTTP server context * @return Error code **/ error_t httpServerStart(HttpServerContext *context) { uint_t i; //Debug message TRACE_INFO("Starting HTTP server...\r\n"); //Make sure the HTTP server context is valid if(context == NULL) return ERROR_INVALID_PARAMETER; //Loop through client connections for(i = 0; i < context->settings.maxConnections; i++) { //Create a task to service a given HTTP client connection context->connections[i].taskHandle = osCreateTask("HTTP Connection", httpConnectionTask, &context->connections[i], HTTP_SERVER_STACK_SIZE, HTTP_SERVER_PRIORITY); //Unable to create the task? if(context->connections[i].taskHandle == OS_INVALID_HANDLE) return ERROR_OUT_OF_RESOURCES; } //Create the HTTP server listener task context->taskHandle = osCreateTask("HTTP Listener", httpListenerTask, context, HTTP_SERVER_STACK_SIZE, HTTP_SERVER_PRIORITY); //Unable to create the task? if(context->taskHandle == OS_INVALID_HANDLE) return ERROR_OUT_OF_RESOURCES; //The HTTP server has successfully started return NO_ERROR; } /** * @brief HTTP server listener task * @param[in] param Pointer to the HTTP server context **/ void httpListenerTask(void *param) { uint_t i; uint_t counter; uint16_t clientPort; IpAddr clientIpAddr; HttpServerContext *context; HttpConnection *connection; Socket *socket; //Retrieve the HTTP server context context = (HttpServerContext *) param; //Process incoming connections to the server for(counter = 1; ; counter++) { //Debug message TRACE_INFO("Ready to accept a new connection...\r\n"); //Limit the number of simultaneous connections to the HTTP server osWaitForSemaphore(&context->semaphore, INFINITE_DELAY); //Loop through available client connections for(i = 0; i < context->settings.maxConnections; i++) { //Point to the current connection connection = &context->connections[i]; //Ready to service the client request? if(!connection->running) { //Accept an incoming connection socket = socketAccept(context->socket, &clientIpAddr, &clientPort); //Make sure the socket handle is valid if(socket != NULL) { //Debug message TRACE_INFO("Connection #%u established with client %s port %" PRIu16 "...\r\n", counter, ipAddrToString(&clientIpAddr, NULL), clientPort); //Reference to the HTTP server settings connection->settings = &context->settings; //Reference to the HTTP server context connection->serverContext = context; //Reference to the new socket connection->socket = socket; //Set timeout for blocking functions socketSetTimeout(connection->socket, HTTP_SERVER_TIMEOUT); //The client connection task is now running... connection->running = TRUE; //Service the current connection request osSetEvent(&connection->startEvent); //We are done break; } } } } } /** * @brief Task that services requests from an active connection * @param[in] param Structure representing an HTTP connection with a client **/ 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 = httpReadRequestHeader(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 httpInitResponseHeader(connection); //Invoke user-defined callback, if any if(connection->settings->requestCallback != NULL) { error = connection->settings->requestCallback(connection, connection->request.uri); } else { //Keep processing... error = ERROR_NOT_FOUND; } //Check status code if(error == ERROR_NOT_FOUND) { #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 { //Set the maximum age for static resources connection->response.maxAge = HTTP_SERVER_MAX_AGE; //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 httpInitResponseHeader(connection); //Invoke user-defined callback, if any if(connection->settings->uriNotFoundCallback != NULL) { error = connection->settings->uriNotFoundCallback(connection, connection->request.uri); } } } //Check status code if(error) { //Default HTTP header fields httpInitResponseHeader(connection); //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 //Valid socket handle? if(connection->socket != NULL) { //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); } } /** * @brief Send HTTP response header * @param[in] connection Structure representing an HTTP connection * @return Error code **/ error_t httpWriteHeader(HttpConnection *connection) { error_t error; //Format HTTP response header error = httpFormatResponseHeader(connection, connection->buffer); //Check status code if(!error) { //Debug message TRACE_DEBUG("HTTP response header:\r\n%s", connection->buffer); //Send HTTP response header to the client error = httpSend(connection, connection->buffer, strlen(connection->buffer), HTTP_FLAG_DELAY); } //Return status code return error; } /** * @brief Read data from client request * @param[in] connection Structure representing an HTTP connection * @param[out] data Buffer where to store the incoming data * @param[in] size Maximum number of bytes that can be received * @param[out] received Number of bytes that have been received * @param[in] flags Set of flags that influences the behavior of this function * @return Error code **/ error_t httpReadStream(HttpConnection *connection, void *data, size_t size, size_t *received, uint_t flags) { error_t error; size_t n; //No data has been read yet *received = 0; //Chunked encoding transfer is used? if(connection->request.chunkedEncoding) { //Point to the output buffer char_t *p = data; //Read as much data as possible while(*received < size) { //End of HTTP request body? if(connection->request.lastChunk) return ERROR_END_OF_STREAM; //Acquire a new chunk when the current chunk //has been completely consumed if(connection->request.byteCount == 0) { //The size of each chunk is sent right before the chunk itself error = httpReadChunkSize(connection); //Failed to decode the chunk-size field? if(error) return error; //Any chunk whose size is zero terminates the data transfer if(!connection->request.byteCount) { //The user must be satisfied with data already on hand return (*received > 0) ? NO_ERROR : ERROR_END_OF_STREAM; } } //Limit the number of bytes to read at a time n = MIN(size - *received, connection->request.byteCount); //Read data error = httpReceive(connection, p, n, &n, flags); //Any error to report? if(error) return error; //Total number of data that have been read *received += n; //Remaining data still available in the current chunk connection->request.byteCount -= n; //The HTTP_FLAG_BREAK_CHAR flag causes the function to stop reading //data as soon as the specified break character is encountered if(flags & HTTP_FLAG_BREAK_CRLF) { //Check whether a break character has been received if(p[n - 1] == LSB(flags)) break; } //The HTTP_FLAG_WAIT_ALL flag causes the function to return //only when the requested number of bytes have been read else if(!(flags & HTTP_FLAG_WAIT_ALL)) { break; } //Advance data pointer p += n; } } //Default encoding? else { //Return immediately if the end of the request body has been reached if(!connection->request.byteCount) return ERROR_END_OF_STREAM; //Limit the number of bytes to read n = MIN(size, connection->request.byteCount); //Read data error = httpReceive(connection, data, n, received, flags); //Any error to report if(error) return error; //Decrement the count of remaining bytes to read connection->request.byteCount -= *received; } //Successful read operation return NO_ERROR; } /** * @brief Write data to the client * @param[in] connection Structure representing an HTTP connection * @param[in] data Buffer containing the data to be transmitted * @param[in] length Number of bytes to be transmitted * @return Error code **/ error_t httpWriteStream(HttpConnection *connection, const void *data, size_t length) { error_t error; uint_t n; //Use chunked encoding transfer? if(connection->response.chunkedEncoding) { //Any data to send? if(length > 0) { char_t s[8]; //The chunk-size field is a string of hex digits //indicating the size of the chunk n = sprintf(s, "%X\r\n", length); //Send the chunk-size field error = httpSend(connection, s, n, HTTP_FLAG_DELAY); //Failed to send data? if(error) return error; //Send the chunk-data error = httpSend(connection, data, length, HTTP_FLAG_DELAY); //Failed to send data? if(error) return error; //Terminate the chunk-data by CRLF error = httpSend(connection, "\r\n", 2, HTTP_FLAG_DELAY); } else { //Any chunk whose size is zero may terminate the data //transfer and must be discarded error = NO_ERROR; } } //Default encoding? else { //The length of the body shall not exceed the value //specified in the Content-Length field length = MIN(length, connection->response.byteCount); //Send user data error = httpSend(connection, data, length, HTTP_FLAG_DELAY); //Decrement the count of remaining bytes to be transferred connection->response.byteCount -= length; } //Return status code return error; } /** * @brief Close output stream * @param[in] connection Structure representing an HTTP connection * @return Error code **/ error_t httpCloseStream(HttpConnection *connection) { error_t error; //Use chunked encoding transfer? if(connection->response.chunkedEncoding) { //The chunked encoding is ended by any chunk whose size is zero error = httpSend(connection, "0\r\n\r\n", 5, HTTP_FLAG_NO_DELAY); } else { //Flush the send buffer error = httpSend(connection, "", 0, HTTP_FLAG_NO_DELAY); } //Return status code return error; } /** * @brief Send HTTP response * @param[in] connection Structure representing an HTTP connection * @param[in] uri NULL-terminated string containing the file to be sent in response * @return Error code **/ error_t httpSendResponse(HttpConnection *connection, const char_t *uri) { #if (HTTP_SERVER_FS_SUPPORT == ENABLED) error_t error; uint32_t length; size_t n; FsFile *file; //Retrieve the full pathname httpGetAbsolutePath(connection, uri, connection->buffer, HTTP_SERVER_BUFFER_SIZE); //Retrieve the size of the specified file error = fsGetFileSize(connection->buffer, &length); //The specified URI cannot be found? if(error) return ERROR_NOT_FOUND; //Open the file for reading file = fsOpenFile(connection->buffer, FS_FILE_MODE_READ); //Failed to open the file? if(file == NULL) return ERROR_NOT_FOUND; #else error_t error; size_t length; uint8_t *data; //Retrieve the full pathname httpGetAbsolutePath(connection, uri, connection->buffer, HTTP_SERVER_BUFFER_SIZE); //Get the resource data associated with the URI error = resGetData(connection->buffer, &data, &length); //The specified URI cannot be found? if(error) return error; #endif //Format HTTP response header connection->response.statusCode = 200; connection->response.contentType = mimeGetType(uri); connection->response.chunkedEncoding = FALSE; connection->response.contentLength = length; //Send the header to the client error = httpWriteHeader(connection); //Any error to report? if(error) { #if (HTTP_SERVER_FS_SUPPORT == ENABLED) //Close the file fsCloseFile(file); #endif //Return status code return error; } #if (HTTP_SERVER_FS_SUPPORT == ENABLED) //Send response body while(length > 0) { //Limit the number of bytes to read at a time n = MIN(length, HTTP_SERVER_BUFFER_SIZE); //Read data from the specified file error = fsReadFile(file, connection->buffer, n, &n); //End of input stream? if(error) break; //Send data to the client error = httpWriteStream(connection, connection->buffer, n); //Any error to report? if(error) break; //Decrement the count of remaining bytes to be transferred length -= n; } //Close the file fsCloseFile(file); //Successful file transfer? if(error == NO_ERROR || error == ERROR_END_OF_FILE) { if(length == 0) { //Properly close the output stream error = httpCloseStream(connection); } } #else //Send response body error = httpWriteStream(connection, data, length); //Any error to report? if(error) return error; //Properly close output stream error = httpCloseStream(connection); #endif //Return status code return error; } /** * @brief Send error response to the client * @param[in] connection Structure representing an HTTP connection * @param[in] statusCode HTTP status code * @param[in] message User message * @return Error code **/ error_t httpSendErrorResponse(HttpConnection *connection, uint_t statusCode, const char_t *message) { error_t error; size_t length; //HTML response template static const char_t template[] = "<!doctype html>\r\n" "<html>\r\n" "<head><title>Error %03d</title></head>\r\n" "<body>\r\n" "<h2>Error %03d</h2>\r\n" "<p>%s</p>\r\n" "</body>\r\n" "</html>\r\n"; //Compute the length of the response length = strlen(template) + strlen(message) - 4; //Format HTTP response header connection->response.statusCode = statusCode; connection->response.contentType = mimeGetType(".htm"); connection->response.chunkedEncoding = FALSE; connection->response.contentLength = length; //Send the header to the client error = httpWriteHeader(connection); //Any error to report? if(error) return error; //Format HTML response sprintf(connection->buffer, template, statusCode, statusCode, message); //Send response body error = httpWriteStream(connection, connection->buffer, length); //Any error to report? if(error) return error; //Properly close output stream error = httpCloseStream(connection); //Return status code return error; } /** * @brief Send redirect response to the client * @param[in] connection Structure representing an HTTP connection * @param[in] statusCode HTTP status code (301 for permanent redirects) * @param[in] uri NULL-terminated string containing the redirect URI * @return Error code **/ error_t httpSendRedirectResponse(HttpConnection *connection, uint_t statusCode, const char_t *uri) { error_t error; size_t length; //HTML response template static const char_t template[] = "<!doctype html>\r\n" "<html>\r\n" "<head><title>Moved</title></head>\r\n" "<body>\r\n" "<h2>Moved</h2>\r\n" "<p>This page has moved to <a href=\"%s\">%s</a>.</p>" "</body>\r\n" "</html>\r\n"; //Compute the length of the response length = strlen(template) + 2 * strlen(uri) - 4; //Format HTTP response header connection->response.statusCode = statusCode; connection->response.location = uri; connection->response.contentType = mimeGetType(".htm"); connection->response.chunkedEncoding = FALSE; connection->response.contentLength = length; //Send the header to the client error = httpWriteHeader(connection); //Any error to report? if(error) return error; //Format HTML response sprintf(connection->buffer, template, uri, uri); //Send response body error = httpWriteStream(connection, connection->buffer, length); //Any error to report? if(error) return error; //Properly close output stream error = httpCloseStream(connection); //Return status code return error; } /** * @brief Check whether the client's handshake is valid * @param[in] connection Structure representing an HTTP connection * @return TRUE if the WebSocket handshake is valid, else FALSE **/ bool_t httpCheckWebSocketHandshake(HttpConnection *connection) { #if (HTTP_SERVER_WEB_SOCKET_SUPPORT == ENABLED) error_t error; size_t n; //The request must contain an Upgrade header field whose value //must include the "websocket" keyword if(!connection->request.upgradeWebSocket) return FALSE; //The request must contain a Connection header field whose value //must include the "Upgrade" token if(!connection->request.connectionUpgrade) return FALSE; //Retrieve the length of the client's key n = strlen(connection->request.clientKey); //The request must include a header field with the name Sec-WebSocket-Key if(n == 0) return FALSE; //The value of the Sec-WebSocket-Key header field must be a 16-byte //value that has been Base64-encoded error = base64Decode(connection->request.clientKey, n, connection->buffer, &n); //Decoding failed? if(error) return FALSE; //Check the length of the resulting value if(n != 16) return FALSE; //The client's handshake is valid return TRUE; #else //WebSocket are not supported return FALSE; #endif } /** * @brief Upgrade an existing HTTP connection to a WebSocket * @param[in] connection Structure representing an HTTP connection * @return Handle referencing the new WebSocket **/ WebSocket *httpUpgradeToWebSocket(HttpConnection *connection) { WebSocket *webSocket; #if (HTTP_SERVER_WEB_SOCKET_SUPPORT == ENABLED) #if (HTTP_SERVER_TLS_SUPPORT == ENABLED) //Check whether a secure connection is being used if(connection->tlsContext != NULL) { //Upgrade the secure connection to a WebSocket webSocket = webSocketUpgradeSecureSocket(connection->socket, connection->tlsContext); } else #endif { //Upgrade the connection to a WebSocket webSocket = webSocketUpgradeSocket(connection->socket); } //Succesful upgrade? if(webSocket != NULL) { error_t error; //Copy client's key error = webSocketSetClientKey(webSocket, connection->request.clientKey); //Check status code if(!error) { #if (HTTP_SERVER_TLS_SUPPORT == ENABLED) //Detach the SSL/TLS context from the HTTP connection connection->tlsContext = NULL; #endif //Detach the socket from the HTTP connection connection->socket = NULL; } else { //Clean up side effects webSocketClose(webSocket); webSocket = NULL; } } #else //WebSockets are not supported webSocket = NULL; #endif //Return a handle to the freshly created WebSocket return webSocket; } #endif