/*---------------------------------------------------------------------------*/
/* http_connection.cpp                                                       */
/* copyright (c) innovaphone 2015                                            */
/*                                                                           */
/*---------------------------------------------------------------------------*/

#include "platform/platform.h"

#include "common/os/iomux.h"

#include "common/interface/time.h"
#include "common/interface/socket.h"
#include "common/interface/webserver_plugin.h"
#include "common/interface/websocket_client.h"
#include "common/interface/random.h"

#include "common/ilib/hash.h"
#include "common/ilib/json.h"
#include "common/ilib/str.h"
#include "common/lib/httplib.h"
#include "common/lib/appservice.h"

#include "common/service/webserver_shared/inno_error_pages.h"
#include "common/service/webserver_shared/webserver_internal.h"
#include "common/service/webserver_shared/http_auth_digest.h"
#include "common/service/webserver_shared/http_digest_host.h"

#include "types.h"
#include "apparguments.h"
#include "webserver.h"
#include "http_datasource.h"
#include "http_data_consumer.h"
#include "http_method_handler.h"
#include "http_websocket_handler.h"
#include "http_get_handler.h"
#include "http_post_handler.h"
#include "http_passthrough_handler.h"
#include "http_webdav_handler.h"
#include "http_options_handler.h"
#include "http_connection.h"


#define HTTP_HEAD_REQUEST           false


/*---------------------------------------------------------------------------*/
/* class HTTPConnection                                                      */
/*---------------------------------------------------------------------------*/

HTTPConnection::HTTPConnection(Webserver * const webserver, class IInstanceLog * const log, dword connectionID, bool isTLS, bool redirectToTLS, bool localHost)
    : webserver(webserver),
      log(log)
{
    this->remoteSocket = nullptr;
    this->wsSocket = nullptr;
    this->websocketClient = nullptr;
    this->connectionID = connectionID;

    this->parser = new HTTPParser();
    this->curMethodHandler = nullptr;
    this->curDataSource = nullptr;
    this->curRegisteredPathObject = nullptr;
    this->connectionRegisteredToDataSource = false;

    this->resultHeader = new HTTPHeader();
    this->isClosing = false;
    this->datasourceLost = false;
    this->buffer = static_cast<char *>(malloc(CONNECTION_DEFAULT_RECV_SIZE));

    this->authObject = nullptr;
    
    this->postBoundary = nullptr;
    this->postBoundaryLen = 0;
    this->postDataType = WSP_RESPONSE_NO_TYPE;
    this->postDataBuffer = nullptr;
    this->postNextPartStart = nullptr;
    this->postTarget = nullptr;
    
    this->connectionFlags.dwordVal = 0;
    this->connectionFlags.connectionIsTLS = isTLS;
    this->connectionFlags.redirectToTLS = isTLS == true ? false : redirectToTLS; // TLS doesn't need to redirecct to TLS...
    // Lets set keepalive as default to true, so that a request to a resource that doesn't exist won't
    // close the connection. Setting it to true is not a problem, because it will be set according to
    // the HTTP header to the correct value.
    this->connectionFlags.connectionKeepAlive = true;
    this->connectionFlags.localHost = localHost;
    this->connectionFlags.sysClient = false;
    this->connectionFlags.eTag = false;
    this->connectionFlagsBase = this->connectionFlags;

    this->webserver->RegisterConnection(this);
}


HTTPConnection::~HTTPConnection()
{
    // Note: wsSocket must not be freed here. It only exists if a websocketClient object
    //       exists and that object will release wsSocket.
    if (this->curMethodHandler) {
        if (this->connectionRegisteredToDataSource) {
            this->curMethodHandler->DataSource()->RemoveConnectionUsingDataSource(this);
        }
        delete this->curMethodHandler;
    }
    
    delete remoteSocket;
    delete websocketClient;
    delete parser;
    delete resultHeader;
    delete authObject;
    
    free(buffer);
    free(postBoundary);
    free(postTarget);
}

int HTTPConnection::btree_compare(void * key)
{
    //return (dword)(((byte *)key) - ((byte *)0)) - this->connectionID;
    return *(dword *)key - this->connectionID;
}


int HTTPConnection::btree_compare(class btree * b)
{
    return this->btree_compare(&static_cast<HTTPConnection *>(b)->connectionID);
}


void HTTPConnection::SocketAcceptComplete(ISocket * const socket)
{
    remoteSocket = socket;
    remoteSocket->Recv(buffer, CONNECTION_DEFAULT_RECV_SIZE, true);
}


void HTTPConnection::SocketShutdown(ISocket * const socket, shutdownreason_t reason)
{
    //printf("+ HTTP-Session(%x): SocketShutdown (reason = ", connectionID);
    //switch (reason)
    //{
    //case SOCKET_SHUTDOWN_NORMAL: printf("SOCKET_SHUTDOWN_NORMAL"); break;
    //case SOCKET_SHUTDOWN_BY_PEER: printf("SOCKET_SHUTDOWN_BY_PEER"); break;
    //case SOCKET_LOST: printf("SOCKET_LOST"); break;
    //case SOCKET_ADDRESS_INVALID: printf("SOCKET_ADDRESS_INVALID"); break;
    //case SOCKET_OPEN_FAILED:  printf("SOCKET_OPEN_FAILED"); break;
    //case SOCKET_CONNECT_FAILED: printf("SOCKET_CONNECT_FAILED"); break;
    //case SOCKET_BIND_FAILED: printf("SOCKET_BIND_FAILED"); break;
    //case SOCKET_LISTEN_FAILED: printf("SOCKET_LISTEN_FAILED"); break;
    //case SOCKET_ACCEPT_FAILED: printf("SOCKET_ACCEPT_FAILED"); break;
    //case SOCKET_TLS_HANDSHAKE_FAILED: printf("SOCKET_TLS_HANDSHAKE_FAILED"); break;
    //}
    //printf(")\n");

    // Because the socket had been closed, the connection will close, too. This flag will be important
    // in the function that handles the HandlerShutdown (if called).
    this->isClosing = true;

    if (remoteSocket == nullptr) {
        log->Log(LOG_WEBSERVER, "HTTPConnection::SocketShutdown(0x%x, 0x%x): socket accept for connectionID %u failed\n", socket, this, connectionID);
        delete socket;
    }
    else {
        delete remoteSocket;
        remoteSocket = nullptr; // need that in HTTPMethodHandlerShutdown()...
    }

    if (curMethodHandler) {
        curMethodHandler->SocketShutdown(reason);
    }
    else {
        // ReleaseConnection() also will delete the instance.
        webserver->ReleaseConnection(this);
    }
}


void HTTPConnection::SocketSendResult(ISocket * const socket)
{
    if (curMethodHandler) curMethodHandler->SocketSendResult();
}


void HTTPConnection::Shutdown()
{
    if (!this->isClosing) {
        this->isClosing = true;

        if (this->curDataSource != nullptr) {
            this->curDataSource->RemoveConnectionUsingDataSource(this);
	        this->connectionRegisteredToDataSource = false;
            this->curDataSource = nullptr;
        }

        if (this->curMethodHandler != nullptr) this->curMethodHandler->ShutdownHandler();
        else if(this->remoteSocket != nullptr) remoteSocket->Shutdown();
        else webserver->ReleaseConnection(this);
    }
}


void HTTPConnection::ShutdownTimeout()
{
    debug->printf("HTTPConnection::ShutdownTimeout %d %d %d %s '%s'", (this->curMethodHandler != nullptr), (this->remoteSocket != nullptr), this->datasourceLost,
        this->curMethodHandler ? this->curMethodHandler->Name() : NULL, parser->GetRequestTarget());
}


void HTTPConnection::ShutDownWithError(const HTTPLib::http_result_t * httpError)
{
    resultHeader->SetHTTPResult(httpError);
    if (httpError->code == HTTPLib::HTTP_503.code) {
        resultHeader->SetCustomField("Retry-After: 1");
        resultHeader->SetCustomField("Cache-Control: no-cache, no-store");
    }
    //resultHeader->SetDate();
    remoteSocket->Send(resultHeader->GetHeaderData(), resultHeader->GetHeaderSize());
    this->Shutdown();
}


bool HTTPConnection::CheckConnectionParameters()
{
    if (parser->GetHTTPResultCode()->code != HTTPLib::HTTP_200.code) {
        this->ShutDownWithError(parser->GetHTTPResultCode());
        return false;
    }

    // Supported HTTP Version?
    if (parser->GetVersion() != HTTP_VERSION_1_0 && parser->GetVersion() != HTTP_VERSION_1_1) {
        resultHeader->SetHTTPVersion(1, 1);
        this->log->Log(LOG_WEBSERVER, "#ERROR HTTPConnection::CheckConnectionParameters(0x%x): HTTPConnection - Client requested unsupported HTTP version (%u.%u)",
            this,
            parser->GetMajorVersion(), parser->GetMinorVersion());

        if (!this->HandleControlMessage(resultHeader, WSR_INT_HTTP_VERSION_UNSUPPORTED)) {
            HTTPHandlerShutdown(nullptr, false);
        }
        return false;
    }
    resultHeader->SetHTTPVersion(parser->GetMajorVersion(), parser->GetMinorVersion());

    return true;
}


void HTTPConnection::RedirectRequestToDefaultPath()
{
    const char redirectHTML[] = "<html><head><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">"
        "<title>302 Moved</title></head><body>"
        "<h1>302 Moved</h1>"
        "Please accesss to the page by clicking "
        "<a href=\"%s\">here</a>.</body></html>";

    char buffer[8000 + sizeof(redirectHTML) + 9]; // Max URI length = 8000
    size_t msgLen = 0;

    resultHeader->SetHTTPResult(&HTTPLib::HTTP_302);
    _snprintf(buffer, sizeof(buffer), "Location: %s", webserver->GetDefaultPath());
    resultHeader->SetCustomField(buffer);

    msgLen = _snprintf(buffer, sizeof(buffer), redirectHTML, webserver->GetDefaultPath());
    if (msgLen >= sizeof(buffer)) msgLen = sizeof(buffer) - 1;
    resultHeader->SetContentSize(msgLen);

    remoteSocket->Send(resultHeader->GetHeaderData(), resultHeader->GetHeaderSize());
    remoteSocket->Send(buffer, msgLen);
    remoteSocket->Shutdown();
}


void HTTPConnection::RedirectRequestToTLS()
{
    const char redirectHTML[] = "<html><head><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">"
        "<title>302 Moved</title></head><body>"
        "<h1>302 Moved</h1>"
        "TLS access required. Please accesss to the page using HTTPS or click "
        "<a href=\"https://%s%s\">here</a>.</body></html>";

    char buffer[8000 + sizeof(redirectHTML) + 9]; // Max URI length = 8000
    size_t msgLen = 0;

    resultHeader->SetHTTPResult(&HTTPLib::HTTP_302);

    if (parser->GetHost() == nullptr) {
        // For HTTP 1.0 the Hostname field could be missing. An automatically redirect is
        // not possible...
        const char addHTML[] = ".</body></html>";
        const char * p = strstr(redirectHTML, "HTTPS");
        p += 5;
        msgLen = size_t(p - redirectHTML);
        memcpy(buffer, redirectHTML, msgLen);
        memcpy(&buffer[msgLen], addHTML, sizeof(addHTML) - 1);
        msgLen += sizeof(addHTML) - 1; // ignore the ending zero
    }
    else {
        _snprintf(buffer, sizeof(buffer), "Location: https://%s%s", parser->GetHost(), parser->GetRequestTarget());
        resultHeader->SetCustomField(buffer);
        msgLen = _snprintf(buffer, sizeof(buffer), redirectHTML, parser->GetHost(), parser->GetRequestTarget());
        if (msgLen >= sizeof(buffer)) msgLen = sizeof(buffer) - 1;
        resultHeader->SetContentSize(msgLen);
    }

    remoteSocket->Send(resultHeader->GetHeaderData(), resultHeader->GetHeaderSize());
    remoteSocket->Send(buffer, msgLen);
    remoteSocket->Shutdown();
}


bool HTTPConnection::RootRequest(const char * target)
{
    if (*target == '/') target++;
    if (*target != '\0' && *target != '*') return false;

    if (parser->GetRequestMethod() == HTTPLib::HTTP_OPTIONS) {
        const char optionsResp[] = "HTTP/1.1 200 OK" CLRF
                                   "Content-Length: 0" CLRF
                                   "Content-Type: httpd/unix-directory" CLRF
                                   "Allow: GET,POST,PUT,PROPFIND,MOVE,COPY,MKCOL,DELETE,OPTIONS" CLRF CLRF;
        remoteSocket->Send(optionsResp, sizeof(optionsResp) - 1);
        remoteSocket->Shutdown();
    }
    else {
        if (webserver->GetDefaultPath()) {
            this->RedirectRequestToDefaultPath();
        }
        else {
            HTTPHeader tmpHeader;
            tmpHeader.SetHTTPVersion(parser->GetMajorVersion(), parser->GetMinorVersion());
            if (!this->HandleControlMessage(&tmpHeader, WSP_CANCEL_BAD_REQUEST)) {
                HTTPHandlerShutdown(nullptr, false);
            }
        }
    }
    return true;
}


HTTPMethodHandler * HTTPConnection::CreateMethodHandlerForRequest(byte * buffer, size_t len)
{
    if (parser->GetRequestMethod() == HTTPLib::HTTP_SYSCLIENT) {
        HandleSysclientAuthentication(buffer, len);
        return nullptr;
    }

    HTTPMethodHandler * result = nullptr;
    if (this->curRegisteredPathObject->IsPassThrough())
        result = new HTTPPassthroughHandler(this, this->curRegisteredPathObject, this->curDataSource, remoteSocket, this->connectionFlags.connectionIsTLS, this->log, this->connectionFlags);
    else {
        bool isWebsocketUpg = parser->GetUpgrade() == HTTPLib::HTTP_UPGRADE_WEBSOCKET;
        if ((isWebsocketUpg && !this->curRegisteredPathObject->SupportsWebsocket()) || (!isWebsocketUpg && !this->curRegisteredPathObject->IsHTTPListening())) {
            if (!this->HandleControlMessage(resultHeader, WSP_CANCEL_NOT_FOUND)) {
                HTTPHandlerShutdown(nullptr, false);
            }
        }
        else {
            switch (parser->GetRequestMethod())
            {
            case HTTPLib::HTTP_GET:
                if (isWebsocketUpg) {
                    result = new HTTPWebsocketHandler(this, this->curRegisteredPathObject, this->curDataSource, remoteSocket, this->log, this->connectionFlags);
                }
                else result = new HTTPGetHandler(this, this->curRegisteredPathObject, this->curDataSource, remoteSocket, this->log, this->connectionFlags);
                break;

            case HTTPLib::HTTP_POST:
                result = new HTTPPostHandler(this, this->curRegisteredPathObject, this->curDataSource, remoteSocket, this->log, this->connectionFlags);
                break;

            case HTTPLib::HTTP_OPTIONS:
                result = new HTTPOptionsHandler(this, this->curRegisteredPathObject, this->curDataSource, remoteSocket, this->log, this->connectionFlags);
                break;

            case HTTPLib::HTTP_PUT:
                result = new HTTPWebdavHandler(this, this->curRegisteredPathObject, this->curDataSource, remoteSocket, this->log, WSM_PUT_REQUEST, this->connectionFlags);
                break;

            case HTTPLib::HTTP_PROPFIND:
                result = new HTTPWebdavHandler(this, this->curRegisteredPathObject, this->curDataSource, remoteSocket, this->log, WSM_PROPFIND_REQUEST, this->connectionFlags);
                break;

            case HTTPLib::HTTP_MOVE:
                result = new HTTPWebdavHandler(this, this->curRegisteredPathObject, this->curDataSource, remoteSocket, this->log, WSM_MOVE_REQUEST, this->connectionFlags);
                break;

            case HTTPLib::HTTP_COPY:
                result = new HTTPWebdavHandler(this, this->curRegisteredPathObject, this->curDataSource, remoteSocket, this->log, WSM_COPY_REQUEST, this->connectionFlags);
                break;

            case HTTPLib::HTTP_MKCOL:
                result = new HTTPWebdavHandler(this, this->curRegisteredPathObject, this->curDataSource, remoteSocket, this->log, WSM_MKCOL_REQUEST, this->connectionFlags);
                break;

            case HTTPLib::HTTP_DELETE:
                result = new HTTPWebdavHandler(this, this->curRegisteredPathObject, this->curDataSource, remoteSocket, this->log, WSM_DELETE_REQUEST, this->connectionFlags);
                break;

            case HTTPLib::HTTP_LOCK:
                result = new HTTPWebdavHandler(this, this->curRegisteredPathObject, this->curDataSource, remoteSocket, this->log, WSM_LOCK_REQUEST, this->connectionFlags);
                break;

            case HTTPLib::HTTP_UNLOCK:
                result = new HTTPWebdavHandler(this, this->curRegisteredPathObject, this->curDataSource, remoteSocket, this->log, WSM_UNLOCK_REQUEST, this->connectionFlags);
                break;

            case HTTPLib::HTTP_PROPPATCH:
                result = new HTTPWebdavHandler(this, this->curRegisteredPathObject, this->curDataSource, remoteSocket, this->log, WSM_PROPPATCH_REQUEST, this->connectionFlags);
                break;

            default:
                this->log->Log(LOG_WEBSERVER, "HTTPConnection::GetMethodHandlerForRequest(0x%x, 0x%x): got unsupported request method %s",
                    this->curDataSource,
                    this,
                    HTTPLib::GetHTTPRequestMethodName(parser->GetRequestMethod()));
                if (!this->HandleControlMessage(resultHeader, WSP_CANCEL_UNAVAILABLE)) {
                    HTTPHandlerShutdown(nullptr, false);
                }
            }
        }
    }

    this->curDataSource = nullptr;
    this->curRegisteredPathObject = nullptr;
    return result;
}


void HTTPConnection::HandleSysclientAuthentication(byte * buffer, size_t len)
{
    // a SYSCLIENT connection is just allowed by localhost connections and the target must start with a slash
    if (!this->connectionFlags.localHost || *parser->GetRequestTarget() != '/') { 
        if (!this->HandleControlMessage(resultHeader, WSP_CANCEL_ACCESS_DENIED)) {
            HTTPHandlerShutdown(nullptr, false);
        }
        return;
    }
    char * target = _strdup(parser->GetRequestTarget() + 1);    // +1 => don't copy the starting /
    str::from_url(target);                                      // url decode the input JSON
    class json_io json(target);
    if (json.decode()) {
        word base = json.get_object(JSON_ID_ROOT, NULL);
        const char * digest = json.get_string(base, "digest");
        const char * nonce = json.get_string(base, "nonce");
        if (digest && nonce && webserver->GetPassword()) {
            class hash hash;
            byte digestLocal[HASH_SIZE_SHA256];
            char digestLocalStr[HASH_SIZE_SHA256 * 2 + 1];
            hash.init(HASH_SHA256);
            hash.update(nonce, strlen(nonce));
            hash.update(webserver->GetPassword(), strlen(webserver->GetPassword()));
            hash.final(digestLocal);
            str::from_hexmem(digestLocal, HASH_SIZE_SHA256, digestLocalStr);
            if (!strcmp(digestLocalStr, digest)) {
                // Once a connection is a sysclient connection, it will be for the rest of the connections lifetime
                // (similar to a websocket connection). So we need to adjust the flag to reset the connectionFlags
                // too.
                this->connectionFlagsBase.sysClient = this->connectionFlags.sysClient = true;
            }
        }
    }
    free(target);
    if (!connectionFlags.sysClient) {
        if (!this->HandleControlMessage(resultHeader, WSP_CANCEL_ACCESS_DENIED)) {
            HTTPHandlerShutdown(nullptr, false);
        }
    }
    else {
        if (len > 0) this->SocketRecvResult(this->remoteSocket, buffer, len);
        else remoteSocket->Recv(this->buffer, CONNECTION_DEFAULT_RECV_SIZE, true);
    }
}


bool HTTPConnection::HTTPParserValidateRequest(class HTTPParser * httpParser)
{
    // Try to find someone that is responsible for that request. If that path goes to a passthrough registered datasource or 
    // if no datasource for that request can be found, return false.
    if (!this->CheckConnectionParameters()) return false;

    if (httpParser->GetRequestMethod() == HTTPLib::HTTP_SYSCLIENT) return true;

    // If the request is OPTIONS and the target * or /*, the Webserver need to send a response, telling
    // what requests are principally supported by the webserver.
    parser->NormalizeRequestTarget();
    const char * requestTarget = parser->GetRequestTarget();
    if (this->RootRequest(requestTarget)) return false;

    HTTPRegisteredPath *  registeredPathObject;
    HTTPDataSource * dataSource = webserver->GetDataSourceForPath(requestTarget, registeredPathObject);
    if (dataSource == nullptr) {
        this->curDataSource = new HTTPDataConsumer(this->webserver);
        this->curRegisteredPathObject = this->curDataSource->GetPathObject(nullptr, nullptr); // HTTPDataConsumer only has one registered path object,
    }
    else if (registeredPathObject == nullptr) {
        this->curDataSource = new HTTPDataConsumer(this->webserver);
        this->curRegisteredPathObject = this->curDataSource->GetPathObject(nullptr, nullptr); // HTTPDataConsumer only has one registered path object,
    }
    else {
        // Set the curDataSource and curRegisteredPathObject now (and not earlier), because they will be checked if their
        // are nullptr, what should be the case if the function had been left earlier.
        this->curRegisteredPathObject = registeredPathObject;
        this->curDataSource = dataSource;
    }

    // Bufix: The connection must regisistered to the datasource here. If not, it can lead to a crash if the header
    // has not been read completly and the datasource will be closed and released before receiving the rest of the
    // header.
    this->curDataSource->AddConnectionUsingDataSource(this);
    this->connectionRegisteredToDataSource = true;

    return !this->curRegisteredPathObject->IsPassThrough();
}


void HTTPConnection::SocketRecvResult(ISocket * const socket, void * buf, size_t len)
{
    if (curMethodHandler && (curMethodHandler->InitState() == HTTP_INIT_DONE || curMethodHandler->IsConsumingData())) {
        curMethodHandler->SocketRecvResult(static_cast<byte *>(buf), len);
        return;
    }

    // If there is no method handler 
    if (this->datasourceLost) return;

    //this->log->Log(LOG_WEBSERVER, "HTTPConnection::SocketRecvResult(0x%x, 0x%x): Incomming Request - Connection ID = %u", socket, this, connectionID);
    //this->log->HexDump(LOG_WEBSERVER, buf, len);

    size_t dataStart = parser->ParseData((char *)buf, len, this);
    //this->log->HexDump(LOG_WEBSERVER, buf, dataStart); // Debug the header...

    switch (parser->GetParseResult())
    {
    case HTTPParser::HTTP_PARSE_NEED_DATA:
        socket->Recv(buffer, CONNECTION_DEFAULT_RECV_SIZE, true);
        break;

    case HTTPParser::HTTP_PARSE_ERROR:
        this->Shutdown();
        break;


    // If the parsing had been canceld but there exists a methodHandler, the request is for a passthorugh
    // registered path. Because of this, HTTP_PARSE_CANCEL and HTTP_PARSE_DONE can be handled in the same way.
    case HTTPParser::HTTP_PARSE_CANCEL:
        // The callback-function of the parser to validate the request, will return an HTTP_PARSE_CANCEL while
        // curRegisteredPathObject is not nullptr, if a request for passthrough path had been detected. In that
        // case, interpred that state as HTTP_PARSE_DONE (no break below). For everything else, return...
        if (this->curRegisteredPathObject == nullptr) return;
        // No break here!!!

    case HTTPParser::HTTP_PARSE_DONE:
    {
        if (this->connectionFlags.redirectToTLS) this->RedirectRequestToTLS();
        else {
            // Check for Keep-Alive flag - it depends on the value given to the connection field and the HTTP version used.
            // For HTTP 1.0, the default is to close the connection if keepalive is not given. For HTTP 1.1, the default is
            // to not close the connection, until the connection field had been set to close.
            if (parser->GetVersion() == HTTP_VERSION_1_0) {
                this->connectionFlags.connectionKeepAlive = parser->IsConnectionType(HTTPLib::HTTP_CONNECTION_KEEP_ALIVE);
            }
            else {
                this->connectionFlags.connectionKeepAlive = !parser->IsConnectionType(HTTPLib::HTTP_CONNECTION_CLOSE);
            }
            this->connectionFlags.eTag = (parser->GetETag() != nullptr);

            // There are two reasons for CreateMethidHandlerForRequest() returning a nullpts: the request is a SysClient message
            // or an error occures. On both cases, CreateMethodHandlerForRequest() will it correctly (redirecting to handle the
            // SysClientMessaage or Sending an error response and shutting down the connection). So no further things must be
            // done here, if nullptr will be returned.
            HTTPMethodHandler * handler = this->CreateMethodHandlerForRequest(static_cast<byte *>(buf)+dataStart, len - dataStart);
            if (handler == nullptr) this->connectionFlags = this->connectionFlagsBase;
            else {
                this->curMethodHandler = handler;
                curMethodHandler->Init(parser, static_cast<byte *>(buf)+dataStart, len - dataStart);
            }
        }
    } break;
    }
}


void HTTPConnection::SocketRecvCanceled(ISocket * const socket, void * buf)
{
}


void HTTPConnection::DataSourceClosed()
{
    this->datasourceLost = true;

    // This message will only be called, if the connection had been assigned to a HTTPDataSource instance.
    // In that case, either this->curDataSource or this->curMethodHandler has a valid object assigned, but
    // not both of them (because when creating a method handler, the dataSource instance will assigned to
    // him while this->curDataSource will be set to nullptr).
    if (this->curMethodHandler) {
        this->curMethodHandler->InvalidateDatasource();
        this->curMethodHandler->ShutdownHandler();
    }
    else {
        if (this->remoteSocket != nullptr) {
            if (this->curDataSource != nullptr) {
                // Shutdown with error won't call remoteSocket shotdown, if curDataSource is not nullptr.
                // But that isn't a problem, because it will be closed after that.
                this->ShutDownWithError(&HTTPLib::HTTP_503);
                this->curDataSource = nullptr;
            }

            // Close the socket because the state of the connection won't be clear.
            this->remoteSocket->Shutdown();
        }
        else this->webserver->ReleaseConnection(this);
    }
}


void HTTPConnection::HTTPHandlerShutdown(HTTPDataSource * const dataSource, bool closeConnection, const HTTPLib::http_result_t* resultMsg)
{
    // Restore it for further connections. The current flags had been copied to the method handler and aren't used here anymore.
    bool connectionKeepAlive =  this->connectionFlags.connectionKeepAlive;
    this->connectionFlags = this->connectionFlagsBase;

    byte * mbuf = nullptr;
    if (this->curMethodHandler && strcmp(this->curMethodHandler->Name(), "HTTPPassthroughHandler") == 0) {
        HTTPPassthroughHandler * pt = static_cast<HTTPPassthroughHandler *>(this->curMethodHandler);
        if (pt->RecvCalled()) mbuf = pt->GetRecvBufferOwnership();
    }

    delete this->curMethodHandler;
    this->curMethodHandler = nullptr;

    if (this->connectionRegisteredToDataSource) {
        if (dataSource != nullptr) { // can happen if the datasource connection got lost
            dataSource->SendMessage("HTTPConnection", this->connectionID, WSM_SHUTDOWN_COMPLETE);
            dataSource->RemoveConnectionUsingDataSource(this);
        }
        this->connectionRegisteredToDataSource = false;
    }


    if (resultMsg != nullptr && this->remoteSocket != nullptr) {
        resultHeader->SetHTTPResult(resultMsg);
        if (resultMsg->code == HTTPLib::HTTP_503.code) {
            resultHeader->SetCustomField("Retry-After: 1");
            resultHeader->SetCustomField("Cache-Control: no-cache, no-store");
        }
        remoteSocket->Send(resultHeader->GetHeaderData(), resultHeader->GetHeaderSize());
    }

    if (this->isClosing) {
        free(mbuf);
        if (remoteSocket != nullptr) remoteSocket->Shutdown();
        else webserver->ReleaseConnection(this);
    }
    else {
        if ((closeConnection || !connectionKeepAlive) && remoteSocket != nullptr) {
            remoteSocket->Shutdown();
            free(mbuf);
        }
        else if (remoteSocket) { // Will be nullptr after shutdown of the socket...
            if (mbuf != nullptr) {
                free(this->buffer);
                this->buffer = reinterpret_cast<char *>(mbuf);
                // No need to call recv(), because already called in Passthrough Handler
            }
            else
            {
                if (buffer == nullptr) buffer = static_cast<char *>(malloc(CONNECTION_DEFAULT_RECV_SIZE));
                remoteSocket->Recv(buffer, CONNECTION_DEFAULT_RECV_SIZE, true);
            }
        }
        else {
            free(mbuf);
            webserver->ReleaseConnection(this);
        }
    }
}


bool HTTPConnection::HandleControlMessage(HTTPHeader * resultHeader, dword message, ExtendedHeader * extHeader)
{
    const char redirectHTML[] = "<html><head><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">"
        "<title>307 Moved</title></head><body>"
        "<h1>307 Moved</h1>"
        "That page you have requested had been moved temporarily. Please update your bookmarks and click "
        "<a href=\"%s\">here</a> to proceed.</body></html>";
    char redirBuffer[sizeof(redirectHTML) + 8000]; // 8000 = max size of URI an app should support. Look at RCFI docu.
    byte * buf = nullptr;
    size_t len = 0;
    bool closeSocket = false;

    switch (message)
    {
    case WSP_CANCEL_NOT_FOUND:
        resultHeader->SetHTTPResult(&HTTPLib::HTTP_404);
        resultHeader->SetContentType("text/html; charset=utf-8");
        buf = (byte *)INNO_404;
        len = INNO_404_LEN;
        break;

    case WSP_CANCEL_BAD_REQUEST:
        resultHeader->SetHTTPResult(&HTTPLib::HTTP_400);
        resultHeader->SetContentType("text/html; charset=utf-8");
        buf = (byte *)INNO_400;
        len = INNO_400_LEN;
        break;

    case WSP_CANCEL_UNAVAILABLE:
        resultHeader->SetHTTPResult(&HTTPLib::HTTP_501);
        break;

    case WSR_INT_REDIRECT:
        resultHeader->SetHTTPResult(&HTTPLib::HTTP_307);
        _snprintf(redirBuffer, sizeof(redirBuffer), "Location: %s", extHeader->RedirectPath());
        resultHeader->SetCustomField(redirBuffer);
        if (extHeader->FieldListBufferSize() > 0) {
            char * curPos = extHeader->FieldListBuffer();
            char * endPos = curPos + extHeader->FieldListBufferSize();
            while (curPos < endPos) {
                char * valPtr = curPos + strlen(curPos) + 1;
                resultHeader->SetCustomField(curPos, valPtr);
                curPos = valPtr + strlen(valPtr) + 1;
            }
        }
        len = _snprintf(redirBuffer, sizeof(redirBuffer), redirectHTML, extHeader->RedirectPath());
        if (len >= sizeof(redirBuffer)) len = sizeof(redirBuffer) - 1;
        buf = reinterpret_cast<byte *>(redirBuffer);
        break;

    case WSR_INT_UNCHANGED:
        resultHeader->SetHTTPResult(&HTTPLib::HTTP_304);
        resultHeader->SetCustomField(CACHE_FILE_MAX_AGE_FIELD);
        resultHeader->SetETag(extHeader->ETag());
        break;

    case WSP_CANCEL_MISSING_LENGTH:
        resultHeader->SetHTTPResult(&HTTPLib::HTTP_411);
        break;

    case WSP_CANCEL_STREAM:
        remoteSocket->Shutdown();
        return true;

    case WSP_CANCEL_INTERNAL_ERROR:
        resultHeader->SetHTTPResult(&HTTPLib::HTTP_500);
        buf = (byte *)INNO_500;
        len = INNO_500_LEN;
        break;

    case WSR_INT_HTTP_VERSION_UNSUPPORTED:
        resultHeader->SetHTTPResult(&HTTPLib::HTTP_505);
        break;

    case WSP_CANCEL_ACCESS_DENIED:
        resultHeader->SetHTTPResult(&HTTPLib::HTTP_401);
        if (buf == nullptr) {
            resultHeader->SetContentType("text/html; charset=utf-8");
            buf = (byte *)INNO_401;
            len = INNO_401_LEN;
        }
        break;

    case WSR_INT_UNDEFINED: // Well, that should not happen...
        this->log->Log(LOG_WEBSERVER, "#ERROR HTTPConnection::HandleControlMessage(0x%x): Connection %u received message type WSR_INT_UNDEFINED - sending HTTP 500.", this, connectionID);
        resultHeader->SetHTTPResult(&HTTPLib::HTTP_500);
        buf = (byte *)INNO_500;
        len = INNO_500_LEN;
        closeSocket = true;
        break;
    }

    resultHeader->SetContentSize(len);

    remoteSocket->Send(resultHeader->GetHeaderData(), resultHeader->GetHeaderSize());
    if (buf) remoteSocket->Send(buf, len);

    if (closeSocket) remoteSocket->Shutdown();
    return closeSocket;
}


HTTPDigestHostManager * HTTPConnection::GetDigestHostManager()
{
    return webserver->GetDigestHostManager();
}


HTTPAuthDigest * HTTPConnection::GetAuthenticationObject()
{
    return authObject;
}


void HTTPConnection::SetAuthenticationObject(HTTPAuthDigest * authObj)
{
    if (authObject != authObj) {
        delete authObject;
        authObject = authObj;
    }
}


void HTTPConnection::RegisterWebsocketForPing(class UWebserverWebsocket * websocket)
{
    this->webserver->RegisterWebsocketForPing(websocket);
}


void HTTPConnection::UnregisterWebsocketForPing(class UWebserverWebsocket * websocket)
{
    this->webserver->UnregisterWebsocketForPing(websocket);
}
