/*---------------------------------------------------------------------------*/
/* http_get_handler.cpp                                                      */
/* copyright (c) innovaphone 2016 - 2019                                     */
/*                                                                           */
/*---------------------------------------------------------------------------*/

#include "platform/platform.h"

#include "common/ilib/hash.h"
#include "common/ilib/base64.h"

#include "common/os/iomux.h"
#include "common/lib/httplib.h"

#include "common/interface/time.h"
#include "common/interface/random.h"
#include "common/interface/socket.h"
#include "common/interface/websocket_client.h"
#include "common/interface/webserver_plugin.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_method_handler.h"
#include "http_connection.h"
#include "http_datasource.h"
#include "http_get_handler.h"


HTTPGetHandler::HTTPGetHandler(UHTTPMethodHandler * const user, HTTPRegisteredPath * reqPathObj, class HTTPDataSource * dataSource, ISocket * const remoteSocket, IInstanceLog * const log, connection_flags_t connFlags)
    : HTTPMethodHandler("HTTPGetHandler", user, reqPathObj, dataSource, remoteSocket, log, connFlags)
{
    this->fragmentedState = FT_NONE;
    this->curTransfer = {};
}


HTTPGetHandler::~HTTPGetHandler()
{
    free(curTransfer.etag);
}


http_method_init_t HTTPGetHandler::OnHandlerInit(const HTTPParser * parser, byte * buffer, size_t size)
{
    // The GET request will be send with the following format inside the data buffer:
    //
    //      - Request path incl. terminating 0
    //      - etag (if there is any)
    //      - Number of ranges (if there are any).
    //      - Connection Flags

    const char * etag = parser->GetETag();
    this->connFlags.eTag = etag != nullptr;

    ExtendedHeader extHeader(this->log, this->DataSource()->ProtocolVersion());
    extHeader.SetRequestPath(this->RequestPath(), this->RequestPathLen());
    extHeader.SetRegisteredPathForRequest(this->RegisteredPathForRequest(), this->RegisteredPathForRequestLen());
    extHeader.SetETag(etag);
    extHeader.SetConnectionFlags(this->connFlags);
    extHeader.SetRequestRange(parser->GetRange(), parser->GetRangeCount());
    extHeader.SetHeaderFields(parser);
    extHeader.SetRemoteAddress(this->Socket());
    extHeader.Encode(this->User()->GetConnectionID());

    this->DataSource()->SendMessage("HTTPGetHandler", User()->GetConnectionID(), WSM_GET_REQUEST, 0, extHeader.Buffer(), extHeader.BufferSize());

    return HTTP_INIT_DONE;
}


void HTTPGetHandler::OnSendMetaData(dword info, ExtendedHeader & extHeader)
{
    transfer_info_t transferInfo = extHeader.TransferInfo();
    this->curTransfer.flags = transferInfo.flags;
    this->curTransfer.type = transferInfo.resourceType;
    this->curTransfer.completeSize = transferInfo.dataSize;

    if (extHeader.ChunkEncodedTransfer()) this->EnableChunkEncodedSend();

    if (extHeader.ETagLen() > 0) {
        this->curTransfer.etag = static_cast<char *>(malloc(extHeader.ETagLen() + 1));
        memcpy(this->curTransfer.etag, extHeader.ETag(), extHeader.ETagLen() + 1);
    }

    if (extHeader.RangeCount() > 0) {
        byte * buf = extHeader.RangesBuffer();
        this->curTransfer.rangeType = *buf++;
        if (this->DataSource()->ProtocolVersion() == WEBSERVER_PROTOCOL_VERSION_0) {
            this->curTransfer.rangeStart = READ_DWORD_NET(buf);
            this->curTransfer.rangeEnd = READ_DWORD_NET(buf + sizeof(dword));
        }
        else {
            this->curTransfer.rangeStart = READ_QUAD_NET(buf);
            this->curTransfer.rangeEnd = READ_QUAD_NET(buf + sizeof(ulong64));
        }
        this->curTransfer.flags = wsr_flags_t(this->curTransfer.flags | WSP_FLAG_SEND_RANGE);
    }

    if (this->curTransfer.type < WSP_RESPONSE_NO_TYPE) { // || curTransfer.flags == WSP_FLAG_NO_DATA) {
        this->HandleCancel(info != 0 ? info : this->curTransfer.type, extHeader);
    }
}


void HTTPGetHandler::OnPrepareHeader()
{
    if (curTransfer.flags & WSP_FLAG_SEND_RANGE) {
        this->ResultHeader()->SetHTTPResult(&HTTPLib::HTTP_206);
        this->ResultHeader()->SetContentRange(curTransfer.rangeStart, curTransfer.rangeEnd, curTransfer.completeSize);
    }
    else {
	    const std::string response = this->GetStatusCodeStr(HTTPLib::HTTP_200);
	    this->ResultHeader()->SetHTTPResult(response.c_str());
        if (this->SendingChunckEncodedEnabled()) {
            this->ResultHeader()->SetCustomField("Transfer-Encoding: chunked");
        }
        else {
            this->ResultHeader()->SetContentSize(curTransfer.completeSize);
        }
    }

    if (curTransfer.type != WSP_RESPONSE_NO_TYPE) this->SetResultMIMEType(wsr_type_t(curTransfer.type));
    if (curTransfer.flags & WSP_FLAG_ENCODING_GZIP) this->ResultHeader()->SetEncoding(HTTPLib::HTTP_ENCODING_GZIP);

    // Caching will be activated if an etag had been given...
    if (curTransfer.etag != NULL) {
        this->ResultHeader()->SetCustomField(CACHE_FILE_MAX_AGE_FIELD);
        this->ResultHeader()->SetETag(curTransfer.etag);
    }
}
