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

//  HTTP Request Fields
//  ===================
//
//  (For a (maybe incomplete) list See: https://en.wikipedia.org/wiki/List_of_HTTP_header_fields)
// 
//  For now, the following fields are supported by the innoivahpone webserver:
//
//  *1 Accept-Encoding
//  *5 Authorization
//     Connection
//     Cookie
//     Content-Length
//     Content-Type
//     Host
//     Range
//  *1 Transfer-Encoding
//  *2 Upgrade 
//
//  Additional fields for websocket connection upgrades:
//
//  *3 Origin
//     Sec-WebSocket-Key
//     Sec-WebSocket-Version
//  *4 Sec-WebSocket-Protocol
//
// *  Only this fields are currently implemented. However, HTTPParser::GetHeaderField() can be used to handle
//    fields not directly supported by the lib.
// *1 Until now, only gzip is supported
// *2 Until now, only upgrade to websocket procotol is supported
// *3 Will internally handled like the Host field.
// *4 Until now, that values will be saved, but not really used
// *5 For now, the Webserver only supports digest authentication
//

#include <platform.h>

#include "common/ilib/str.h"

#include "../lib/httplib.h"

#define BUFFER_BLOCK_SIZE           512

#define PS_READY                        0
#define PS_READING_FIELDS               1
#define PS_READING_REQUEST              2
#define PS_READING_HTTP_VERSION_START   3
#define PS_READING_HTTP_VERSION         4
#define PS_READING_REQUEST_DONE         5
#define PS_DONE                         6
#define PS_ERROR                        0xF

// Some helper macros
#define ISSPACE(c)    (c == ' ' || c == '\t' || c == '\r' || c == '\f' || c == '\n' || c == '\v')
#define CURRENT_CONTENT_END     (buffer + contentSize - 2)

// From the RFC Docu:
// 
// Support at least 8000 bytes of request length (first line)
// request - target longer than any URI it wishes to parse MUST respond
// with a 414 (URI Too Long) status code
#define MAX_REQUEST_TARGET_LEN  8000
#define MAX_FIRST_LINE_LEN      (MAX_REQUEST_TARGET_LEN * 2)

// No whitespace is allowed between the header field - name and colon.In
// the past, differences in the handling of such whitespace have led to
// security vulnerabilities in request routing and response handling.A
// server MUST reject any received request message that contains
// whitespace between a header field - name and colon with a response code
// of 400 (Bad Request).A proxy MUST remove any such whitespace from a
// response message before forwarding the message downstream.

// 1xx - Information
const HTTPLib::http_result_t HTTPLib::HTTP_100 = { 100, "100 Continue", 12 };
const HTTPLib::http_result_t HTTPLib::HTTP_101 = { 101, "101 Switching Protocols", 23 };
const HTTPLib::http_result_t HTTPLib::HTTP_102 = { 102, "102 Payment Required", 20 };
const HTTPLib::http_result_t HTTPLib::HTTP_103 = { 103, "103 Forbidden", 13 };

// 2xx - Successful operations
const HTTPLib::http_result_t HTTPLib::HTTP_200 = { 200, "200 OK", 6 };
const HTTPLib::http_result_t HTTPLib::HTTP_201 = { 201, "201 Created", 11 };
const HTTPLib::http_result_t HTTPLib::HTTP_202 = { 202, "202 Accepted", 12 };
const HTTPLib::http_result_t HTTPLib::HTTP_203 = { 203, "203 Non-Authoritative Information", 34 };
const HTTPLib::http_result_t HTTPLib::HTTP_204 = { 204, "204 No Content", 14 };
const HTTPLib::http_result_t HTTPLib::HTTP_205 = { 205, "205 Reset Content", 17 };
const HTTPLib::http_result_t HTTPLib::HTTP_206 = { 206, "206 Partial Content", 19 };
const HTTPLib::http_result_t HTTPLib::HTTP_207 = { 207, "207 Multi-Status", 16 };
const HTTPLib::http_result_t HTTPLib::HTTP_208 = { 208, "208 Already Reported", 20 };
const HTTPLib::http_result_t HTTPLib::HTTP_226 = { 226, "226 IM Used", 11 };

// 3xx - Redirection
const HTTPLib::http_result_t HTTPLib::HTTP_300 = { 300, "300 Multiple Choices", 20 };
const HTTPLib::http_result_t HTTPLib::HTTP_301 = { 301, "301 Moved Permanently", 21 };
const HTTPLib::http_result_t HTTPLib::HTTP_302 = { 302, "302 Found", 9 };
const HTTPLib::http_result_t HTTPLib::HTTP_303 = { 303, "303 See Other", 13 };
const HTTPLib::http_result_t HTTPLib::HTTP_304 = { 304, "304 Not Modified", 16 };
const HTTPLib::http_result_t HTTPLib::HTTP_305 = { 305, "305 Use Proxy", 13 };
const HTTPLib::http_result_t HTTPLib::HTTP_307 = { 307, "307 Temporary Redirect", 22 };
const HTTPLib::http_result_t HTTPLib::HTTP_308 = { 308, "308 Permanent Redirect", 22 };

// 4xx - client errors
const HTTPLib::http_result_t HTTPLib::HTTP_400 = { 400, "400 Bad Request", 15 };
const HTTPLib::http_result_t HTTPLib::HTTP_401 = { 401, "401 Unauthorized", 16 };
const HTTPLib::http_result_t HTTPLib::HTTP_402 = { 402, "402 Payment Required", 20 };
const HTTPLib::http_result_t HTTPLib::HTTP_403 = { 403, "403 Forbidden", 13 };
const HTTPLib::http_result_t HTTPLib::HTTP_404 = { 404, "404 Not Found", 13 };
const HTTPLib::http_result_t HTTPLib::HTTP_405 = { 405, "405 Method Not Allowed", 22 };
const HTTPLib::http_result_t HTTPLib::HTTP_406 = { 406, "406 Not Acceptable", 18 };
const HTTPLib::http_result_t HTTPLib::HTTP_407 = { 407, "407 Proxy Authentication Required", 33 };
const HTTPLib::http_result_t HTTPLib::HTTP_408 = { 408, "408 Request Timeout", 19 };
const HTTPLib::http_result_t HTTPLib::HTTP_409 = { 409, "409 Conflict", 12 };
const HTTPLib::http_result_t HTTPLib::HTTP_410 = { 410, "410 Gone", 8 };
const HTTPLib::http_result_t HTTPLib::HTTP_411 = { 411, "411 Length Required", 19 };
const HTTPLib::http_result_t HTTPLib::HTTP_412 = { 412, "412 Precondition Failed", 23 };
const HTTPLib::http_result_t HTTPLib::HTTP_413 = { 413, "413 Payload Too Large", 21 };
const HTTPLib::http_result_t HTTPLib::HTTP_414 = { 414, "414 URI Too Long", 16 };
const HTTPLib::http_result_t HTTPLib::HTTP_415 = { 415, "415 Unsupported Media Type", 26 };
const HTTPLib::http_result_t HTTPLib::HTTP_416 = { 416, "416 Range Not Satisfiable", 25 };
const HTTPLib::http_result_t HTTPLib::HTTP_417 = { 417, "417 Expectation Failed", 22 };
const HTTPLib::http_result_t HTTPLib::HTTP_418 = { 418, "418 I'm a teapot", 16 };
const HTTPLib::http_result_t HTTPLib::HTTP_421 = { 421, "421 Misdirected Request", 23 };
const HTTPLib::http_result_t HTTPLib::HTTP_422 = { 422, "422 Unprocessable Entity", 24 };
const HTTPLib::http_result_t HTTPLib::HTTP_423 = { 423, "423 Locked", 10 };
const HTTPLib::http_result_t HTTPLib::HTTP_424 = { 424, "424 Failed Dependency", 21 };
const HTTPLib::http_result_t HTTPLib::HTTP_425 = { 425, "425 Too Early", 13 };
const HTTPLib::http_result_t HTTPLib::HTTP_426 = { 426, "426 Upgrade Required", 20 };
const HTTPLib::http_result_t HTTPLib::HTTP_428 = { 428, "428 Precondition Required", 25 };
const HTTPLib::http_result_t HTTPLib::HTTP_429 = { 429, "429 Too Many Requests", 21 };
const HTTPLib::http_result_t HTTPLib::HTTP_431 = { 431, "431 Request Header Fields Too Large", 35 };
const HTTPLib::http_result_t HTTPLib::HTTP_451 = { 451, "451 Unavailable For Legal Reasons", 33 };

// 5xx - server errors
const HTTPLib::http_result_t HTTPLib::HTTP_500 = { 500, "500 Internal Server Error", 25 };
const HTTPLib::http_result_t HTTPLib::HTTP_501 = { 501, "501 Not Implemented", 19 };
const HTTPLib::http_result_t HTTPLib::HTTP_502 = { 502, "502 Bad Gateway", 15 };
const HTTPLib::http_result_t HTTPLib::HTTP_503 = { 503, "503 Service Unavailable", 23 };
const HTTPLib::http_result_t HTTPLib::HTTP_504 = { 504, "504 Gateway Timeout", 19 };
const HTTPLib::http_result_t HTTPLib::HTTP_505 = { 505, "505 HTTP Version Not Supported", 30 };
const HTTPLib::http_result_t HTTPLib::HTTP_506 = { 506, "506 Variant Also Negotiates", 27 };
const HTTPLib::http_result_t HTTPLib::HTTP_507 = { 507, "507 Insufficient Storage", 24 };
const HTTPLib::http_result_t HTTPLib::HTTP_508 = { 508, "508 Loop Detected", 17 };
const HTTPLib::http_result_t HTTPLib::HTTP_509 = { 509, "509 Bandwidth Limit Exceeded", 28 };
const HTTPLib::http_result_t HTTPLib::HTTP_510 = { 510, "510 Not Extended", 16 };
const HTTPLib::http_result_t HTTPLib::HTTP_511 = { 511, "511 Network Authentication Required", 35 };


/*---------------------------------------------------------------------------*/
/* struct http__result_t                                                     */
/*---------------------------------------------------------------------------*/

HTTPLib::http_result_t::http_result_t(int code, const char * description, size_t descLen, bool staticBuffer)
    : isStaticBuffer(staticBuffer),
      code(code),
      descLen(descLen)
{
    if (staticBuffer) this->description = description;
    else {
        this->description = new char[this->descLen + 1];
        memcpy(const_cast<char *>(this->description), description, this->descLen + 1);
    }
}

HTTPLib::http_result_t::http_result_t(http_result_t && result)
{
    this->Move(result);
}

HTTPLib::http_result_t::http_result_t(const http_result_t & result)
{
    this->Copy(result);
}

HTTPLib::http_result_t::~http_result_t()
{
    if (!this->isStaticBuffer) delete[] this->description;
}


void HTTPLib::http_result_t::Copy(const http_result_t & result)
{
    if (!this->isStaticBuffer) delete[] this->description;
    this->code = result.code;
    this->descLen = result.descLen;
    this->isStaticBuffer = result.isStaticBuffer;
    if (result.isStaticBuffer) this->description = result.description;
    else {
        this->description = new char[this->descLen + 1];
        memcpy(const_cast<char *>(this->description), result.description, result.descLen + 1);
    }
}

void HTTPLib::http_result_t::Move(http_result_t & result)
{
    if (!this->isStaticBuffer) delete[] this->description;
    this->code = result.code;
    this->descLen = result.descLen;
    this->isStaticBuffer = result.isStaticBuffer;
    this->description = result.description;
    
    result.code = 0;
    result.descLen = 0;
    result.isStaticBuffer = false;
    result.description = nullptr;
}


/*---------------------------------------------------------------------------*/
/* class HTTPHeaderLine                                                      */
/*---------------------------------------------------------------------------*/

class HTTPHeaderLine {
public:
    HTTPHeaderLine() : line(nullptr), next(nullptr) {}
    ~HTTPHeaderLine() { free(line); }
    char * line;
    HTTPHeaderLine * next;
};


/*---------------------------------------------------------------------------*/
/* class HTTPHeader                                                          */
/*---------------------------------------------------------------------------*/

HTTPHeader::HTTPHeader()
    : buffer(nullptr),
    bufferSize(0),
    contentSize(0)
{
    httpVersion.major = httpVersion.minor = 1;
    bufferSize = 1024;
    buffer = (char *)malloc(bufferSize);
}


HTTPHeader::~HTTPHeader()
{
    free(buffer);
}


void HTTPHeader::GuaranteeSizeInBuffer(size_t sizeNeeded)
{
    size_t sNeeded = contentSize + sizeNeeded;
    if (bufferSize >= sNeeded) return;

    size_t newSize = sNeeded / BUFFER_BLOCK_SIZE * BUFFER_BLOCK_SIZE;
    if (newSize < sNeeded) newSize += BUFFER_BLOCK_SIZE;

    buffer = (char *)realloc(buffer, newSize);
}


void HTTPHeader::SetHTTPVersion(int major, int minor)
{
    httpVersion.major = major;
    httpVersion.minor = minor;
    sprintf(buffer, "HTTP/%u.%u ", major, minor);
}


void HTTPHeader::SetHTTPResult(const HTTPLib::http_result_t * result)
{
	this->SetHTTPResult(result->description);
}


void HTTPHeader::SetHTTPResult(const char * resultStr)
{
	ASSERT(resultStr != nullptr && *resultStr != '\0', "HTTPHEader::SetHTTPResult(const char *): empty string not allowed.");
	contentSize = 9; // Start after the default "HTTP/x.x "...
	size_t len = strlen(resultStr);
	char * bufPos = this->buffer + contentSize;
	contentSize += len + 4;
	this->GuaranteeSizeInBuffer(contentSize);
	sprintf(bufPos, "%.*s" CLRF CLRF, static_cast<int>(len), resultStr);
}


void HTTPHeader::SetContentSize(ulong64 size)
{
    // NOTE: Each Set Function need to remove the last CLRF part, add
    //       that needs to be added and add the final CLRF part again,
    //       so that the last empty line to close the header always
    //       will be part of the data inside the buffer.
    size_t sizeNeed = 37; // "Content-Length: " + 19 (max of ulong64 (1844674407370955161)) + 2 (CLRF)
    this->GuaranteeSizeInBuffer(sizeNeed);

    // Ok, time for a little hack: when the maximum of size will be used, the last '\n' is
    // missing because of the final '\0'. Because of this we will write the missing char at
    // that position.
    contentSize += sprintf(CURRENT_CONTENT_END, "Content-Length: %llu" CLRF CLRF, (unsigned long long)size);
    contentSize -= 2; // remove 2 bytes (because we removed from also in CURRENT_CONTENT_END)
    buffer[contentSize - 1] = '\n';
}


void HTTPHeader::SetContentRange(ulong64 start, ulong64 end, ulong64 completeSize)
{
    this->SetCustomField("Accept-Ranges: bytes");
    
    size_t sizeNeed = 21 + 2 + (3 * 19) + 2; // "Content-Range: bytes " + '/' + '-' + 3 * 19 (max of ulong64) + 2 (CLRF)
    this->GuaranteeSizeInBuffer(sizeNeed);

    if (completeSize == 0xFFFFFFFFFFFFFFFF) contentSize += sprintf(CURRENT_CONTENT_END, "Content-Range: bytes %llu-%llu/*" CLRF CLRF, (unsigned long long)start, (unsigned long long)end);
    else contentSize += sprintf(CURRENT_CONTENT_END, "Content-Range: bytes %llu-%llu/%llu" CLRF CLRF, (unsigned long long)start, (unsigned long long)end, (unsigned long long)completeSize);
    contentSize -= 2; // remove 2 bytes (because we removed fronm also in CURRENT_CONTENT_END)

    // Also set the content size. There is a special case: if start and end are the same (which leads to
    // a data length of 0), one byte will be send from the app.
    this->SetContentSize(end - start + 1);
}



void HTTPHeader::SetRequest(HTTPLib::http_request_method_t req, const char * resource)
{
    contentSize = strlen(resource);
    const char * reqMeth = HTTPLib::GetHTTPRequestMethodName(req);
    contentSize += strlen(reqMeth) + 14; // Request + space + resource + space = HTTP/x.x + CL RF CL RF
    this->GuaranteeSizeInBuffer(contentSize);
    _sprintf(buffer, "%s %s HTTP/%u.%u" CLRF CLRF, reqMeth, resource, httpVersion.major, httpVersion.minor);
}


void HTTPHeader::SetContentType(const char * contentType)
{
    size_t cts = strlen(contentType);
    size_t sizeNeed = cts + 16;
    this->GuaranteeSizeInBuffer(sizeNeed);
    sprintf(CURRENT_CONTENT_END, "Content-Type: %s" CLRF CLRF, contentType);
    contentSize += sizeNeed;
}


void HTTPHeader::SetEncoding(HTTPLib::http_encoding_t enc)
{
    static const char enc_gzip[] = "Content-Encoding: gzip";
    static size_t enc_gzip_len = strlen(enc_gzip);
    size_t sizeNeed = enc_gzip_len + 2;
    if (enc == HTTPLib::HTTP_ENCODING_GZIP) {
        this->GuaranteeSizeInBuffer(sizeNeed);
        sprintf(CURRENT_CONTENT_END, "%s" CLRF CLRF, enc_gzip);
        contentSize += sizeNeed;
    }
}


void HTTPHeader::SetETag(const char * etag)
{
    size_t sizeNeed = 10 + strlen(etag); // Len of (etag: "") + 2 (CLRF) + len of (etag)
    this->GuaranteeSizeInBuffer(sizeNeed);
    sprintf(CURRENT_CONTENT_END, "Etag: \"%s\"" CLRF CLRF, etag);
    contentSize += sizeNeed;
}


void HTTPHeader::SetConnection(HTTPLib::http_connection_t connection)
{
    if (connection != HTTPLib::HTTP_CONNECTION_UPGRADE) {
        debug->printf("WARNING: HTTPResultHeader::SetConnection(): Ignoring unsupported connection type value %u", connection);
        return;
    }

    static const char conn_upg[] = "Connection: Upgrade";
    static size_t conn_upg_len = strlen(conn_upg);
    size_t sizeNeed = conn_upg_len + 2;
    this->GuaranteeSizeInBuffer(sizeNeed);
    sprintf(CURRENT_CONTENT_END, "%s" CLRF CLRF, conn_upg);
    contentSize += sizeNeed;
}


void HTTPHeader::SetUpgrade(HTTPLib::http_upgrade_t upgrade)
{
    static const char upg_ws[] = "Upgrade: websocket";
    static size_t upg_ws_len = strlen(upg_ws);
    size_t sizeNeed = upg_ws_len + 2;
    if (upgrade == HTTPLib::HTTP_UPGRADE_WEBSOCKET) {
        this->GuaranteeSizeInBuffer(sizeNeed);
        sprintf(CURRENT_CONTENT_END, "%s" CLRF CLRF, upg_ws);
        contentSize += sizeNeed;
    }
}


void HTTPHeader::SetWebsocketAcceptKey(const char * key)
{
    size_t sizeNeed = 24 + strlen(key); // Len of (Sec-WebSocket-Accept: ) + 2 + len of (key)
    this->GuaranteeSizeInBuffer(sizeNeed);
    sprintf(CURRENT_CONTENT_END, "Sec-WebSocket-Accept: %s" CLRF CLRF, key);
    contentSize += sizeNeed;
}


void HTTPHeader::SetCustomField(const char * field)
{
    size_t sizeNeed = strlen(field) + 2;
    this->GuaranteeSizeInBuffer(sizeNeed);
    sprintf(CURRENT_CONTENT_END, "%s" CLRF CLRF, field);
    contentSize += sizeNeed;
}


void HTTPHeader::SetCustomField(const char * field, const char * value)
{
    size_t sizeNeeded = strlen(field) + strlen(value) + 4; // +4 = ": " + CLRF
    this->GuaranteeSizeInBuffer(sizeNeeded);
    sprintf(CURRENT_CONTENT_END, "%s: %s" CLRF CLRF, field, value);
    contentSize += sizeNeeded;
}


void HTTPHeader::SetDate()
{
    size_t sizeNeed = 37; // 6 ("Date: ") + 29 (Time String) + 2 (CLRF)
    this->GuaranteeSizeInBuffer(sizeNeed);

    time_t cur;
    time(&cur);
    // Add + 2 for the second CLRF. There will be sufficient Space inside the buffer,
    // bevause we removed the last CLRF part (see CURRENT_CONTENT_END)
    strftime(CURRENT_CONTENT_END, sizeNeed + 2, "Date: %a, %m %b %Y %T %Z" CLRF CLRF, localtime(&cur));
    contentSize += sizeNeed;

    // A little hack. strftime() will add a closing zero. Because of that the last part of
    // \r\n will (the '\n' part) will not be printed to the buffer. Instead of this, we will
    // find a '\0'. But adding a 1 to the needed size could lead in an unused buffer resize
    // and we will not need that 0. Because of this simply override it.
    buffer[contentSize - 1] = '\n';
}


/*---------------------------------------------------------------------------*/
/* class HTTPFieldList  (used internally by HTTPParser)                      */
/*---------------------------------------------------------------------------*/

class HTTPFieldList : public HTTPHeaderField {
protected:
    const char * fieldName;
    size_t fieldValuesSize;
    HTTPFieldList * next;

    HTTPFieldList(const char * field, const char * value, HTTPFieldList * next)
        : HTTPHeaderField(),
        fieldName(field),
        fieldValuesSize(5),
        next(next)
    {
        values = (const char **)malloc(fieldValuesSize * sizeof(char *));
        values[0] = value;
        valueCount = 1;
    }

    ~HTTPFieldList()
    {
        free(values);
    }

    void CopyFieldNameToBuffer(byte *&buffer, size_t bufferSize, size_t &bytesCopied)
    {
        size_t len = strlen(this->fieldName) + 1;
        bytesCopied += len;
        if (bytesCopied >= bufferSize) {
            debug->printf("HTTPLib::HTTPParser::HTTPFieldList::CopyFieldsToBuffer(): copy field name - bytesCopied = %llu, but max buffer size = %llu", bytesCopied, bufferSize);
            ASSERT(false, "HTTPLib::HTTPParser::HTTPFieldList::CopyFieldsToBuffer(): copy field name - Buffer Overflow");
        }
        memcpy(buffer, this->fieldName, len);
        buffer += len;
    }

    void CopyFiledValuesToBuffer(byte *&buffer, size_t bufferSize, size_t &bytesCopied)
    {
        size_t i = 0;
        size_t lastItem = this->valueCount - 1;
        while (true) {
            size_t len = strlen(this->values[i]) + 1;
            bytesCopied += len;
            if (bytesCopied > bufferSize) {
                debug->printf("HTTPLib::HTTPParser::HTTPFieldList::CopyFiledValuesToBuffer(): copy field value[%llu] - bytesCopied = %llu, but max buffer size = %llu", i, bytesCopied, bufferSize);
                ASSERT(false, "HTTPLib::HTTPParser::HTTPFieldList::CopyFiledValuesToBuffer(): copy field value - Buffer Overflow");
            }
            memcpy(buffer, this->values[i], len);
            buffer += len;

            if (i == lastItem) return;

            // If there is an additional field value to copy, we need to remove the closing zero and add the seperator. Make sure, that there is place enough left.
            ++bytesCopied;
            if (bytesCopied > bufferSize) {
                debug->printf("HTTPLib::HTTPParser::HTTPFieldList::CopyFiledValuesToBuffer(): copy field value[%llu] (add seperator) - bytesCopied = %llu, but max buffer size = %llu", i, bytesCopied, bufferSize);
                ASSERT(false, "HTTPLib::HTTPParser::HTTPFieldList::CopyFiledValuesToBuffer(): copy field value (add seperator) - Buffer Overflow");
            }

            *(buffer - 1) = ',';
            *buffer++ = ' ';
            ++i;
        }
    }

public:
    HTTPFieldList * AddField(const char * field, const char * value, bool & fieldExist)
    {
        HTTPFieldList * result = this->GetField(field);
        if (result) {
            if (result->valueCount == result->fieldValuesSize) {
                result->fieldValuesSize += 5;
                result->values = (const char **)realloc(result->values, result->fieldValuesSize * sizeof(char *));
            }
            result->values[result->valueCount] = value;
            result->valueCount++;
            fieldExist = true;
            // When the value had been added to an existing field, return this to not screw up the filed list.
            // Because the returned value must always be the top of the list.
            return this;
        }

        fieldExist = false;
        return new HTTPFieldList(field, value, this);
    }

    HTTPFieldList * GetField(const char * fieldName)
    {
        HTTPFieldList * tmp = this;
        while (tmp) {
            if (strcasecmp(tmp->fieldName, fieldName) == 0) return tmp;
            tmp = tmp->next;
        }

        return nullptr;
    }

    HTTPFieldList * Clear()
    {
        HTTPFieldList * cur = this;
        while (cur) {
            HTTPFieldList * n = cur->next;
            delete cur;
            cur = n;
        }
        return nullptr;
    }

    void CopyFieldsToBuffer(byte * buffer, size_t bufferSize)
    {
        HTTPFieldList * cur = this;
        size_t bytesCopied = 0;
        while (cur != nullptr) {
            cur->CopyFieldNameToBuffer(buffer, bufferSize, bytesCopied);
            cur->CopyFiledValuesToBuffer(buffer, bufferSize, bytesCopied);
            cur = cur->next;
        }
    }
};


/*---------------------------------------------------------------------------*/
/* class HTTPParser                                                          */
/*---------------------------------------------------------------------------*/

HTTPParser::HTTPParser()
    : httpResultCode(),
      httpResultCodePtr(&HTTPLib::HTTP_200)
{
    this->headerLineStart = nullptr;
    this->headerLineEnd = nullptr;
    this->curLineTmpBuf = nullptr;
    this->curLineTmpBufSize = 0;
    this->parserState = PS_READY;
    this->parseResult = HTTP_PARSE_DONE;
    this->reqMethod = HTTPLib::HTTP_NONE;
    this->httpVersion = 0;
    this->fieldList = nullptr;
    this->firstLineBufferSize = 0;
    this->acceptEncoding = HTTPLib::HTTP_ENCODING_NONE;
    this->contentLength = 0;
    this->connectionType = HTTPLib::HTTP_CONNECTION_NONE;
    this->host = nullptr;
    this->upgradeType = HTTPLib::HTTP_UPGRADE_NONE;
    this->rangeListStart = nullptr;
    this->rangeListEnd = nullptr;
    this->rangeCount = 0;
    this->encoding = HTTPLib::HTTP_ENCODING_NONE;
    this->websocketKey = nullptr;
    this->websocketVersion = 0;
    this->websocketProtocol = nullptr;
    this->origin = nullptr;
    this->etag = nullptr;
    this->firstLineBuffer = nullptr;
    this->firstLineBufferSize = 0;
    this->requestTarget = nullptr;
    this->mustFreeRequestTarget = false;
    this->requestLineLoaded = false;
}


HTTPParser::~HTTPParser()
{
    fieldList->Clear();
    free(curLineTmpBuf);
    free(this->firstLineBuffer);
    if (this->mustFreeRequestTarget) free(this->requestTarget);

    while (rangeListStart) {
        HTTPRange * tmp = rangeListStart;
        rangeListStart = rangeListStart->next;
        delete tmp;
    }

    while (headerLineStart) {
        HTTPHeaderLine * next = headerLineStart->next;
        delete headerLineStart;
        headerLineStart = next;
    }
}


void HTTPParser::Reset()
{
    parserState = PS_READY;
    httpResultCode = HTTPLib::HTTP_200;
    reqMethod = HTTPLib::HTTP_NONE;
    requestLineLoaded = false;
    free(firstLineBuffer);
    firstLineBuffer = nullptr;
    firstLineBufferSize = 0;
    if (mustFreeRequestTarget) free(requestTarget);
    requestTarget = nullptr;
    mustFreeRequestTarget = false;
    requestParameters = nullptr;
    acceptEncoding = HTTPLib::HTTP_ENCODING_NONE;
    contentLength = 0;
    connectionType = HTTPLib::HTTP_CONNECTION_NONE;
    cookie = nullptr;
    etag = nullptr;
    contentType = nullptr;
    host = nullptr;
    upgradeType = HTTPLib::HTTP_UPGRADE_NONE;
    encoding = HTTPLib::HTTP_ENCODING_NONE;
    websocketKey = nullptr;
    websocketVersion = 0;
    websocketProtocol = nullptr;
    origin = nullptr;
    httpVersion = 0;
    fieldList = fieldList->Clear();
    fieldListBufferSize = 0;

    while (rangeListStart) {
        HTTPRange * tmp = rangeListStart;
        rangeListStart = rangeListStart->next;
        delete tmp;
    }
    rangeCount = 0;

    while (headerLineStart) {
        HTTPHeaderLine * next = headerLineStart->next;
        delete headerLineStart;
        headerLineStart = next;
    }
    headerLineEnd = nullptr;
}


HTTPHeaderLine * HTTPParser::CreateNewLine()
{
    HTTPHeaderLine * result = new HTTPHeaderLine;
    if (headerLineStart == nullptr) {
        headerLineStart = headerLineEnd = result;
    }
    else {
        headerLineEnd->next = result;
        headerLineEnd = result;
    }

    return result;
}


bool HTTPParser::ParseResponseLine()
{
    char * line = this->firstLineBuffer;
    if (strncmp(line, "HTTP/", 5) != 0) {
        parserState = PS_ERROR;
        return false;
    }
    line += 5;

    // For know we're ignoring the HTTP version...
    httpVersion = ((*line - 48) << 8) + (*(line + 2) - 48);

    parserState = PS_DONE;
    line += 4;
    int resCode = atoi(line);
    if (resCode <= 0) {
        debug->printf("Unknown HTTP result code (%i) - result line: '%s'\n", resCode, line);
        parserState = PS_ERROR;
        return false;
    }
    
    switch (resCode) {
    case 100: httpResultCodePtr = &HTTPLib::HTTP_100; break;
    case 101: httpResultCodePtr = &HTTPLib::HTTP_101; break;
    case 102: httpResultCodePtr = &HTTPLib::HTTP_102; break;
    case 103: httpResultCodePtr = &HTTPLib::HTTP_103; break;
    case 200: httpResultCodePtr = &HTTPLib::HTTP_200; break;
    case 201: httpResultCodePtr = &HTTPLib::HTTP_201; break;
    case 202: httpResultCodePtr = &HTTPLib::HTTP_202; break;
    case 203: httpResultCodePtr = &HTTPLib::HTTP_203; break;
    case 204: httpResultCodePtr = &HTTPLib::HTTP_204; break;
    case 205: httpResultCodePtr = &HTTPLib::HTTP_205; break;
    case 206: httpResultCodePtr = &HTTPLib::HTTP_206; break;
    case 207: httpResultCodePtr = &HTTPLib::HTTP_207; break;
    case 208: httpResultCodePtr = &HTTPLib::HTTP_208; break;
    case 226: httpResultCodePtr = &HTTPLib::HTTP_226; break;
    case 300: httpResultCodePtr = &HTTPLib::HTTP_300; break;
    case 301: httpResultCodePtr = &HTTPLib::HTTP_301; break;
    case 302: httpResultCodePtr = &HTTPLib::HTTP_302; break;
    case 303: httpResultCodePtr = &HTTPLib::HTTP_303; break;
    case 304: httpResultCodePtr = &HTTPLib::HTTP_304; break;
    case 305: httpResultCodePtr = &HTTPLib::HTTP_305; break;
    case 307: httpResultCodePtr = &HTTPLib::HTTP_307; break;
    case 308: httpResultCodePtr = &HTTPLib::HTTP_308; break;
    case 400: httpResultCodePtr = &HTTPLib::HTTP_400; break;
    case 401: httpResultCodePtr = &HTTPLib::HTTP_401; break;
    case 402: httpResultCodePtr = &HTTPLib::HTTP_402; break;
    case 403: httpResultCodePtr = &HTTPLib::HTTP_403; break;
    case 404: httpResultCodePtr = &HTTPLib::HTTP_404; break;
    case 405: httpResultCodePtr = &HTTPLib::HTTP_405; break;
    case 406: httpResultCodePtr = &HTTPLib::HTTP_406; break;
    case 407: httpResultCodePtr = &HTTPLib::HTTP_407; break;
    case 408: httpResultCodePtr = &HTTPLib::HTTP_408; break;
    case 409: httpResultCodePtr = &HTTPLib::HTTP_409; break;
    case 410: httpResultCodePtr = &HTTPLib::HTTP_410; break;
    case 411: httpResultCodePtr = &HTTPLib::HTTP_411; break;
    case 412: httpResultCodePtr = &HTTPLib::HTTP_412; break;
    case 413: httpResultCodePtr = &HTTPLib::HTTP_413; break;
    case 414: httpResultCodePtr = &HTTPLib::HTTP_414; break;
    case 415: httpResultCodePtr = &HTTPLib::HTTP_415; break;
    case 416: httpResultCodePtr = &HTTPLib::HTTP_416; break;
    case 417: httpResultCodePtr = &HTTPLib::HTTP_417; break;
    case 418: httpResultCodePtr = &HTTPLib::HTTP_418; break;
    case 421: httpResultCodePtr = &HTTPLib::HTTP_421; break;
    case 422: httpResultCodePtr = &HTTPLib::HTTP_422; break;
    case 423: httpResultCodePtr = &HTTPLib::HTTP_423; break;
    case 424: httpResultCodePtr = &HTTPLib::HTTP_424; break;
    case 425: httpResultCodePtr = &HTTPLib::HTTP_425; break;
    case 426: httpResultCodePtr = &HTTPLib::HTTP_426; break;
    case 428: httpResultCodePtr = &HTTPLib::HTTP_428; break;
    case 429: httpResultCodePtr = &HTTPLib::HTTP_429; break;
    case 431: httpResultCodePtr = &HTTPLib::HTTP_431; break;
    case 451: httpResultCodePtr = &HTTPLib::HTTP_451; break;
    case 500: httpResultCodePtr = &HTTPLib::HTTP_500; break;
    case 501: httpResultCodePtr = &HTTPLib::HTTP_501; break;
    case 502: httpResultCodePtr = &HTTPLib::HTTP_502; break;
    case 503: httpResultCodePtr = &HTTPLib::HTTP_503; break;
    case 504: httpResultCodePtr = &HTTPLib::HTTP_504; break;
    case 505: httpResultCodePtr = &HTTPLib::HTTP_505; break;
    case 506: httpResultCodePtr = &HTTPLib::HTTP_506; break;
    case 507: httpResultCodePtr = &HTTPLib::HTTP_507; break;
    case 508: httpResultCodePtr = &HTTPLib::HTTP_508; break;
    case 509: httpResultCodePtr = &HTTPLib::HTTP_509; break;
    case 510: httpResultCodePtr = &HTTPLib::HTTP_510; break;
    case 511: httpResultCodePtr = &HTTPLib::HTTP_511; break;    
    default:
        httpResultCode = HTTPLib::http_result_t(resCode, line, strlen(line), false);
        httpResultCodePtr = &httpResultCode;
    }

    if (httpResultCodePtr != &httpResultCode) httpResultCode = HTTPLib::http_result_t();
    
    return true;
}


bool HTTPParser::ParseRequestLine(UHTTPParser * user)
{
    char * line = this->firstLineBuffer;

    // Get everything to the first space...
    if (line == nullptr || *line == '\0') {
        parserState = PS_ERROR;
        parseResult = HTTP_PARSE_ERROR;
        httpResultCode = HTTPLib::HTTP_400;
        return false;
    }

    char * endPos = strchr(line, ' ');
    if (endPos == nullptr) {
        parserState = PS_ERROR;
        parseResult = HTTP_PARSE_ERROR;
        httpResultCode = HTTPLib::HTTP_400;
        return false;
    }

    *endPos = '\0';

    if (strcmp(line, HTTP_GET_KEY) == 0) reqMethod = HTTPLib::HTTP_GET;
    else if (strcmp(line, HTTP_HEAD_KEY) == 0) reqMethod = HTTPLib::HTTP_HEAD;
    else if (strcmp(line, HTTP_POST_KEY) == 0) reqMethod = HTTPLib::HTTP_POST;
    else if (strcmp(line, HTTP_PUT_KEY) == 0) reqMethod = HTTPLib::HTTP_PUT;
    else if (strcmp(line, HTTP_DELETE_KEY) == 0) reqMethod = HTTPLib::HTTP_DELETE;
    else if (strcmp(line, HTTP_CONNECT_KEY) == 0) reqMethod = HTTPLib::HTTP_CONNECT;
    else if (strcmp(line, HTTP_OPTIONS_KEY) == 0) reqMethod = HTTPLib::HTTP_OPTIONS;
    else if (strcmp(line, HTTP_TRACE_KEY) == 0) reqMethod = HTTPLib::HTTP_TRACE;
    else if (strcmp(line, HTTP_PROPFIND_KEY) == 0) reqMethod = HTTPLib::HTTP_PROPFIND;
    else if (strcmp(line, HTTP_MKCOL_KEY) == 0) reqMethod = HTTPLib::HTTP_MKCOL;
    else if (strcmp(line, HTTP_COPY_KEY) == 0) reqMethod = HTTPLib::HTTP_COPY;
    else if (strcmp(line, HTTP_MOVE_KEY) == 0) reqMethod = HTTPLib::HTTP_MOVE;
    else if (strcmp(line, HTTP_LOCK_KEY) == 0) reqMethod = HTTPLib::HTTP_LOCK;
    else if (strcmp(line, HTTP_UNLOCK_KEY) == 0) reqMethod = HTTPLib::HTTP_UNLOCK;
    else if (strcmp(line, HTTP_PROPPATCH_KEY) == 0) reqMethod = HTTPLib::HTTP_PROPPATCH;
    else if (strcmp(line, HTTP_SYSCLIENT_KEY) == 0) reqMethod = HTTPLib::HTTP_SYSCLIENT;
    else {
        // Restore the char at endPos to have a correct error message.
        *endPos = ' '; // Restore original string.
        debug->printf("ERROR: HTTPParser::ReadRequestLine(): unknown HTTP method %s", this->firstLineBuffer);
        parserState = PS_ERROR;
        parseResult = HTTP_PARSE_ERROR;
        httpResultCode = HTTPLib::HTTP_400;
        return false;
    }

    line += endPos - line + 1;

    // For the first call, we need to check, if the URI is valid...
    // According to the HTTP specification RFC 7230, Request Target
    // (https://tools.ietf.org/html/rfc7230#section-5.3), the request target will
    // start with '/', 'h', 'w'. However, it could be * as well to get generell informations
    // (e. g. with an OPTIONS request...)
    if (*line != '/' && *line != 'h' && *line != 'w' && *line != '*') {
        *endPos = ' '; // Restore original string.
        debug->printf("ERROR: HTTPParser::ReadRequestLine(): invalid request target in \"%s\"", this->firstLineBuffer);
        parserState = PS_ERROR;
        parseResult = HTTP_PARSE_ERROR;
        httpResultCode = HTTPLib::HTTP_400;
        return false;
    }
    this->requestTarget = line;

    // Now simply find the necessary data...
    char * eoURI = strchr(line, ' ');
    if (eoURI == nullptr) {
        debug->printf("ERROR: HTTPParser::ReadRequestLine(): Missing HTTP version part of request line.");
        this->httpResultCode = HTTPLib::HTTP_400;
        this->parserState = PS_ERROR;
        this->parseResult = HTTP_PARSE_ERROR;
        return false;
    }

    *eoURI = '\0';
    char * paramPos = strchr(line, '?');
    size_t uriLen;
    if (paramPos != nullptr) {
        //*paramPos = '\0';
        this->requestParameters = paramPos + 1;
        uriLen = paramPos - this->requestTarget;
    }
    else uriLen = eoURI - this->requestTarget;

    if (uriLen >= MAX_REQUEST_TARGET_LEN) {
        debug->printf("ERROR: HTTPParser::ReadRequestLine(): request target too long (max %u)", MAX_REQUEST_TARGET_LEN);
        parserState = PS_ERROR;
        parseResult = HTTP_PARSE_ERROR;
        httpResultCode = HTTPLib::HTTP_414;
        return false;
    }

    line = eoURI + 1; // Reuse line, now points to somewhere inside the requestTarget buffer

    // Next: HTTPx.x...
    if (*line != 'H' || *(++line) != 'T' || *(++line) != 'T' || *(++line) != 'P') {
        // Restore original string.
        *endPos = ' ';
        *eoURI = ' ';

        debug->printf("ERROR: HTTPParser::ReadRequestLine(): expected \"HTTPx.x\", but found \"%s\"", (line - 3));
        parserState = PS_ERROR;
        parseResult = HTTP_PARSE_ERROR;
        httpResultCode = HTTPLib::HTTP_400;
        return false;
    }
    line++;

    // Check for the "/" after HTTP
    if (*line != '/') {
        // Restore original string.
        *endPos = ' ';
        *eoURI = ' ';

        debug->printf("ERROR: HTTPParser::ReadRequestLine(): HTTP version number failure. Missing '/'\n");
        parserState = PS_ERROR;
        parseResult = HTTP_PARSE_ERROR;
        httpResultCode = HTTPLib::HTTP_400;
        return false;
    }
    line++;

    // Make sure to have exactly 3 bytes left to handle...
    if (*line == '\0' || *(line + 1) == '\0' || *(line + 3) != '\0') {
        debug->printf("ERROR: HTTPParser::ReadRequestLine(): expected HTTP version number not found");
        parserState = PS_ERROR;
        parseResult = HTTP_PARSE_ERROR;
        httpResultCode = HTTPLib::HTTP_400;
        return false;
    }

    // Finally - Check version number...
    if (*(line + 1) != '.') {
        debug->printf("ERROR: HTTPParser::ReadRequestLine(): expected \"HTTP/x.x\", but found \"%s\"", (line - 3));
        parserState = PS_ERROR;
        parseResult = HTTP_PARSE_ERROR;
        httpResultCode = HTTPLib::HTTP_400;
        return false;
    }

    // ASCII Code of 0 = 48.
    int httpMajorVersion = *line - 48;
    int httpMinorVersion = *(line + 2) - 48;
    if (httpMajorVersion < 1 || httpMajorVersion > 9 || httpMinorVersion < 0 || httpMinorVersion > 9) {
        debug->printf("ERROR: HTTPParser::ReadRequestLine(): HTTP version number missmatch (%u.%u)",
            httpMajorVersion, httpMinorVersion);
        parserState = PS_ERROR;
        parseResult = HTTP_PARSE_ERROR;
        httpResultCode = HTTPLib::HTTP_400;
        return false;
    }
    httpVersion = (httpMajorVersion << 8) + httpMinorVersion;

    // Last check - have we reached the end of the string?
    line += 3;
    if (*line != '\0') {
        debug->printf("ERROR: HTTPParser::ReadRequestLine(): unexpected data at end of line (%s)", line);
        parserState = PS_ERROR;
        parseResult = HTTP_PARSE_ERROR;
        httpResultCode = HTTPLib::HTTP_400;
        return false;
    }

    if (user != nullptr && !user->HTTPParserValidateRequest(this)) {
        parserState = PS_ERROR;
        parseResult = HTTP_PARSE_CANCEL;
    }
    else parserState = PS_READING_FIELDS;

    return true;
}


void HTTPParser::ParseFieldLines(HTTPHeaderLine * curLine)
{
    parserState = PS_DONE; // Lets be optimistic ;-)
    while (curLine) {

        char * tmp = strchr(curLine->line, ':');
        // With the latest RFC, spaces between colon and the field name are allowed. So remove them, if necessary.
        if (tmp == nullptr || tmp == curLine->line) {
            debug->printf("ERROR: HTTPParser::ReadFieldLine(): malformed field line (%s)", curLine->line);
            parserState = PS_ERROR;
            httpResultCode = HTTPLib::HTTP_400;
            return;
        }

        char * endPtr = tmp - 1;
        while (endPtr != curLine->line && ISSPACE(*endPtr)) --endPtr;

        // Write a closing zero to the end of the word. For this, the endPtr must be incremented by one, because it has been
        // decremented above and will point to the last char.
        *++endPtr = '\0';

        ++tmp;
        while (ISSPACE(*tmp)) ++tmp;
        this->SetFieldValue(curLine->line, tmp);

        bool fieldExist;
        fieldList = fieldList->AddField(curLine->line, tmp, fieldExist);

        // Calculate the buffer size needed, if a copy should be created. If the field already exists, the length filed name
        // will not be added again (because for creating a copy of the fields later, each name will only be added once).
        // However, if the field exists, the length of the value need to be adjusted by 1. This is, because the values
        // (if there is one than more because a field exists a couple of times) will be concatenated. This means, instead
        // of the closing 0, ', ' will be added (the byte for the closing zero for that line already had been added before).
        // Note that the length of the name is calculated in an easy way: endPtr - curLine->line + 1. +1 to add the closing
        // zero.
        size_t fnLen = size_t(endPtr - curLine->line) + 1;
        size_t vLen = strlen(tmp) + 1;

        this->fieldListBufferSize += vLen;
        this->fieldListBufferSize += (fieldExist ? 1 : fnLen);

        curLine = curLine->next;
    }
}


void HTTPParser::SetFieldValue(const char * field, const char * value)
{
    // Because we need a case insensitive compare and strstr, field and value will be
    // converted to lower case. To not change the originals, a copy of the items will
    // be made. However, the value itself only will be lovercased if necessary.
    char * lcValue = nullptr;
    char * lcField = _strdup(field);
    str::caselwr(lcField);

    switch (*lcField) {
    case 'a':
        if (strcmp(lcField, "accept-encoding") == 0) {
            lcValue = _strdup(value);
            str::caselwr(lcValue);
            if (strstr(lcValue, "gzip") != nullptr) acceptEncoding = HTTPLib::HTTP_ENCODING_GZIP;
        }
        break;

    case 'c':
        if (strcmp(lcField, "connection") == 0) {
            lcValue = _strdup(value);
            str::caselwr(lcValue);
            if (strstr(lcValue, "keep-alive") != 0) connectionType |= HTTPLib::HTTP_CONNECTION_KEEP_ALIVE;
            if (strstr(lcValue, "close") != 0) connectionType |= HTTPLib::HTTP_CONNECTION_CLOSE;
            if (strstr(lcValue, "upgrade") != 0) connectionType |= HTTPLib::HTTP_CONNECTION_UPGRADE;
            // default (HTTP_CONN_CLOSE) already set;
        }
        else if (strcmp(lcField, "content-length") == 0) {
            contentLength = strtoull(value, nullptr, 10);
        }
        else if (strcmp(lcField, "content-type") == 0) {
            contentType = value;
        }
        else if (strcmp(lcField, "cookie") == 0) {
            cookie = value;
        }
        break;

    case 'h':
        if (strcmp(lcField, "host") == 0) host = value;
        break;

    case 'i':
        if (strcmp(lcField, "if-none-match") == 0) {
            if (*value == '"') value++;
            size_t l = strlen(value);
            if (value[l - 1] == '"') const_cast<char *>(value)[l - 1] = '\0';
            etag = value;
        }
        break;

    case 'o':
        if (strcmp(lcField, "origin") == 0) origin = value;
        break;

    case 'r':
        if (strcmp(lcField, "range") == 0) {
            char * start = const_cast<char *>(value);
            if (strncmp(start, "bytes=", 6) == 0) {
                start += 6;
                char * p = strchr((char *)start, '-');
                while (p) {
                    if (*p == ',') p++;
                    if (*p == '\0') {
                        parserState = PS_ERROR;
                        return;
                    }

                    rangeCount++;
                    if (!rangeListStart) {
                        rangeListStart = rangeListEnd = new HTTPRange;
                    }
                    else {
                        rangeListEnd->next = new HTTPRange;
                        rangeListEnd = rangeListEnd->next;
                    }

                    if (p == start) {
                        rangeListEnd->rangeType = HTTPRange::RANGE_LAST_BYTES;
                        rangeListEnd->start = strtoull(p + 1, nullptr, 10);
                        rangeListEnd->end = 0;
                    }
                    else if (*(p + 1) == '\0') {
                        rangeListEnd->rangeType = HTTPRange::RANGE_START_ONLY;
                        rangeListEnd->start = strtoull(start, nullptr, 10);
                        rangeListEnd->end = 0;
                    }
                    else {
                        *p = '\0';
                        rangeListEnd->rangeType = HTTPRange::RANGE_START_END;
                        rangeListEnd->start = strtoull(start, nullptr, 10);
                        rangeListEnd->end = strtoull(p + 1, nullptr, 10);
                        *p = '-'; // Set again to allow the parsed header be rebuild as byte stream
                    }
                    p = strchr(p, ',');
                }
            }
        }
        break;

    case 's':
        if (strcmp(lcField, "sec-websocket-key") == 0) websocketKey = value;
        else if (strcmp(lcField, "sec-websocket-version") == 0) websocketVersion = atoi(value);
        else if (strcmp(lcField, "sec-websocket-protocol") == 0) websocketProtocol = value;
        break;

    case 't':
        if (strcmp(lcField, "transfer-encoding") == 0) {
            lcValue = _strdup(value);
            str::caselwr(lcValue);
            if (strstr(lcValue, "gzip") != nullptr) encoding = HTTPLib::HTTP_ENCODING_GZIP;
        }
        break;

    case 'u':
        if (strcmp(lcField, "upgrade") == 0) {
            lcValue = _strdup(value);
            str::caselwr(lcValue);
            if (strstr(lcValue, "websocket") != nullptr) upgradeType = HTTPLib::HTTP_UPGRADE_WEBSOCKET;
        }
        break;
    }

    free(lcValue);
    free(lcField);
}


size_t HTTPParser::AddHeaderLines(char * data, size_t len)
{
    parseResult = HTTP_PARSE_NEED_DATA;
    if (len == 0) return 0;

    char * curStart = data;
    char * eol = nullptr;

    while (true) {
        size_t dataProcessed = curStart - data;
        if (dataProcessed == len) return len;

        size_t rest = len - dataProcessed;
        eol = static_cast<char *>(memchr(curStart, '\n', rest));

        if (!eol) {
            size_t oldSize = curLineTmpBufSize;
            curLineTmpBufSize += rest;
            curLineTmpBuf = (char *)realloc(curLineTmpBuf, curLineTmpBufSize);
            char * dst = curLineTmpBuf + oldSize;
            memcpy(dst, curStart, rest);
            return len;
        }

        char * curLine;
        size_t curLineLen;
        if (curLineTmpBuf) {
            size_t restSize = (eol - curStart);
            if (restSize > 0) {
                curLineTmpBuf = (char *)realloc(curLineTmpBuf, restSize + curLineTmpBufSize);
                memcpy(curLineTmpBuf + curLineTmpBufSize, curStart, restSize);
            }

            curLine = curLineTmpBuf;
            curLineLen = restSize + curLineTmpBufSize;
            curLineTmpBuf = nullptr;
            curLineTmpBufSize = 0;
        }
        else {
            size_t llen = eol - curStart;
            if (llen == 0) {
                debug->printf("HTTPParser found \\n at the begin of a line");
                parseResult = HTTP_PARSE_ERROR;
                parserState = PS_ERROR;
                return 0;
            }

            curLine = (char *)malloc(llen);
            curLineLen = llen;
            memcpy(curLine, curStart, llen);
        }

        if (curLine[curLineLen - 1] != '\r') {
            debug->printf("HTTPParser missing \\r at end of a line");
            parseResult = HTTP_PARSE_ERROR;
            parserState = PS_ERROR;
            free(curLine);
            return 0;
        }

        curLine[curLineLen - 1] = '\0';
        if (*curLine == '\0') {
            // Empty line found = end of header...
            free(curLine);
            curStart = eol + 1;
            parseResult = HTTP_PARSE_DONE;
            return (curStart > data + len ? len : curStart - data);
        }
        HTTPHeaderLine * hl = this->CreateNewLine();
        hl->line = curLine;
        curStart = eol + 1;
    }
}


size_t HTTPParser::ParseData(char * data, size_t len, UHTTPParser * user)
{
    size_t result = 0;
    if (parserState == PS_READY || parserState == PS_ERROR || parserState == PS_DONE) {
        this->Reset();
    }

    if (!this->requestLineLoaded) {
        this->parseResult = HTTP_PARSE_NEED_DATA;

        char * eol = static_cast<char *>(memchr(data, '\n', len));
        size_t dataToCopy = (eol == nullptr ? len : eol - data);
        size_t newBufSize = this->firstLineBufferSize + dataToCopy;

        if (newBufSize > MAX_FIRST_LINE_LEN) {
            debug->printf("ERROR: HTTPParser::ReadRequestLine(): line to long (%u bytes)", newBufSize);
            this->parserState = PS_ERROR;
            this->parseResult = HTTP_PARSE_ERROR;
            this->httpResultCode = HTTPLib::HTTP_400;
            return 0;
        }

        this->parserState = PS_READING_REQUEST;
        this->firstLineBuffer = static_cast<char *>(realloc(this->firstLineBuffer, newBufSize));
        memcpy(this->firstLineBuffer + this->firstLineBufferSize, data, dataToCopy);
        this->firstLineBufferSize = newBufSize;
        if (eol == nullptr) return dataToCopy;

        if (this->firstLineBufferSize == 0 || this->firstLineBuffer[this->firstLineBufferSize - 1] != '\r') {
            debug->printf("ERROR: HTTPParser::ReadRequestLine(): Found \\n but missing \\r before that.");
            this->parserState = PS_ERROR;
            this->parseResult = HTTP_PARSE_ERROR;
            this->httpResultCode = HTTPLib::HTTP_400;
            return 0;
        }

        this->firstLineBuffer[this->firstLineBufferSize - 1] = '\0'; // also remove the \r at the end of the \n...
        this->requestLineLoaded = true;

        // Because C++ guarantee short-circuiting for standard types, we can call the two functions
        // in this order.
        if (!this->ParseResponseLine() && !this->ParseRequestLine(user)) return dataToCopy + 1; // +1 to also threat the \n as handled

        result = dataToCopy + 1; // + 1 to also ignore the already found \n
        if (this->parseResult == HTTP_PARSE_CANCEL) return result;
    }

    this->parserState = PS_READING_FIELDS;

    if (result == len) return result;

    data += result;
    len -= result;

    result += this->AddHeaderLines(data, len);
    if (parseResult == HTTP_PARSE_NEED_DATA) return result;

    if (parserState == PS_ERROR) {
        parseResult = HTTP_PARSE_ERROR;
        return 0;
    }

    HTTPHeaderLine * curLine = headerLineStart;
    this->ParseFieldLines(curLine);
    if (parserState == PS_ERROR) {
        parseResult = HTTP_PARSE_ERROR;
        return 0;
    }

    parseResult = HTTP_PARSE_DONE;
    return result;
}


const HTTPHeaderField * HTTPParser::GetHeaderField(const char * fieldName) const
{
    return fieldList->GetField(fieldName);
}


void HTTPParser::GetFieldListCopy(byte * & buffer, size_t & bufferSize) const
{
    if (!fieldList) {
        buffer = nullptr;
        bufferSize = 0;
        return;
    }

    buffer = static_cast<byte *>(malloc(this->fieldListBufferSize));
    bufferSize = this->fieldListBufferSize;
    this->fieldList->CopyFieldsToBuffer(buffer, bufferSize);
}


bool HTTPParser::IsChunkedTransfer()
{
    const HTTPHeaderField * headerField = this->GetHeaderField("Transfer-Encoding");
    if (headerField) {
        for (size_t i = 0; i < headerField->GetValueCount(); i++) {
            if (strcasecmp(headerField->GetValue(i), "chunked") == 0) {
                return true;
            }
        }
    }
    return false;
}


void HTTPParser::NormalizeRequestTarget()
{
    if (this->mustFreeRequestTarget ||   // Normalize already called.
        this->requestTarget == nullptr ||
        *this->requestTarget == '\0' ||
        *this->requestTarget == '*' ||
        strstr(this->requestTarget, "./") == 0 ||             // make sure, there is something to normalize
        str::n_casecmp(this->requestTarget, "www.", 4) == 0 ||    // If a full address is given, no normalization will be done.
        str::n_casecmp(this->requestTarget, "http:", 5) == 0 ||
        str::n_casecmp(this->requestTarget, "https:", 6) == 0)
    {
        return;
    }

    std::string pathStr;
    std::string result;

    if (this->requestParameters != nullptr) pathStr.assign(this->requestTarget, this->requestParameters - this->requestTarget - 1);
    else pathStr = this->requestTarget;

    bool endsWithSlash = pathStr.back() == '/';

    std::vector<std::string> pathParts;
    std::string cur;
    size_t pos = 0;

    while (pos != std::string::npos) {
        size_t npos = pathStr.find('/', pos);
        if (npos == std::string::npos) {
            cur = pathStr.substr(pos);
            pos = npos;
        }
        else {
            cur = pathStr.substr(pos, npos - pos);
            pos = npos == pathStr.size() ? std::string::npos : npos + 1;
        }

        if (!cur.empty() && cur != ".") {
            if (cur == "..") {
                if (!pathParts.empty()) pathParts.pop_back();
            }
            else pathParts.push_back(cur);
        }
    }

    if (pathParts.empty()) result = "/";
    else {
        for (std::string & s : pathParts) result += "/" + s;
        if (endsWithSlash) result += '/';
    }

    if (this->requestParameters != nullptr) {
        result += "?";
        result += this->requestParameters;
    }

    this->requestTarget = static_cast<char *>(malloc(result.size() + 1));
    if (this->requestTarget != nullptr) memcpy(this->requestTarget, result.c_str(), result.size() + 1); // + 1 to copy the terminating zero, too.
    else ASSERT(false, "void HTTPParser::NormalizeRequestTarget(): malloc failed!");
    this->mustFreeRequestTarget = true;
}
