
/*---------------------------------------------------------------------------*/
/* http_post_handler.cpp                                                     */
/* copyright (c) innovaphone 2016                                            */
/*                                                                           */
/*---------------------------------------------------------------------------*/

#include "platform/platform.h"

#include "common/os/iomux.h"
#include "common/interface/socket.h"
#include "common/interface/websocket_client.h"
#include "common/interface/webserver_plugin.h"

#include "common/service/webserver_shared/webserver_internal.h"

#include "common/lib/httplib.h"
#include "common/lib/appservice.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_post_handler.h"


HTTPPostHandler::HTTPPostHandler(UHTTPMethodHandler * const user, class HTTPRegisteredPath * reqPathObj, class HTTPDataSource * dataSource, ISocket * const remoteSocket, IInstanceLog * const log, connection_flags_t connFlags)
    : HTTPMethodHandler("HTTPPostHandler", user, reqPathObj, dataSource, remoteSocket, log, connFlags)
{
    memset(&curTransfer, 0, sizeof(curTransfer));
}


HTTPPostHandler::~HTTPPostHandler()
{
}


http_method_init_t HTTPPostHandler::OnHandlerInit(const HTTPParser * parser, byte * buffer, size_t size)
{
    dword gp = 0;

    ExtendedHeader extHeader(this->log, this->DataSource()->ProtocolVersion());
    extHeader.SetRequestPath(this->RequestPath(), this->RequestPathLen());
    extHeader.SetRegisteredPathForRequest(this->RegisteredPathForRequest(), this->RegisteredPathForRequestLen());
    extHeader.SetConnectionFlags(this->connFlags);
    extHeader.SetRemoteAddress(this->Socket());
    extHeader.SetHeaderFields(parser);

    if (DataSource()->ProtocolVersion() == WEBSERVER_PROTOCOL_VERSION_0) {
        gp = (this->RequestIsChunkEncoded() ? WS_RESPONSE_CHUNKED_V0 : dword(parser->GetContentLength()));
    }
    else {
        if (this->RequestIsChunkEncoded()) extHeader.SetChunkEncodedTransfer(true);
        else extHeader.SetDataSize(parser->GetContentLength());
    }

    extHeader.Encode(this->User()->GetConnectionID());
    DataSource()->SendMessage("HTTPPostHandler", User()->GetConnectionID(), WSM_POST_REQUEST, gp, extHeader.Buffer(), extHeader.BufferSize());

    return HTTP_INIT_DONE;
}


void HTTPPostHandler::OnPrepareHeader()
{
	const std::string response = this->GetStatusCodeStr(HTTPLib::HTTP_200);
    this->ResultHeader()->SetHTTPResult(response.c_str());

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


    if (this->SendingChunckEncodedEnabled()) {
        this->ResultHeader()->SetCustomField("Transfer-Encoding: chunked");
        this->EnableChunkEncodedSend();
    }
    else this->ResultHeader()->SetContentSize(this->curTransfer.contentLength);
}


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

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

    if (this->curTransfer.type < WSP_RESPONSE_NO_TYPE) { // || curTransfer.flags == WSP_FLAG_NO_DATA) {
        if (!(this->curTransfer.type == WSP_CANCEL_STREAM && this->curTransfer.started)) {
            // If the socket had not been closed, it will be reused. In that case, the method handler must be shutdown manually....
            if (!this->User()->HandleControlMessage(ResultHeader(), info != 0 ? info : this->curTransfer.type, &extHeader))
            {
                this->User()->HTTPHandlerShutdown(DataSource(), false);
            }
        }
        else this->User()->HTTPHandlerShutdown(DataSource(), true);
    }
}


void HTTPPostHandler::SetResultMIMEType(wsr_type_t type)
{
    switch (type)
    {
    case WSP_RESPONSE_HTML:
        ResultHeader()->SetContentType("text/html; charset=utf-8");
        break;

    case WSP_RESPONSE_CSS:
        ResultHeader()->SetContentType("text/css");
        break;

    case WSP_RESPONSE_JAVASCRIPT:
    case WSP_RESPONSE_MJS:
        ResultHeader()->SetContentType("text/javascript");
        break;

    case WSP_RESPONSE_JSON:
        ResultHeader()->SetContentType("application/json");
        break;

    case WSP_RESPONSE_XML:
        ResultHeader()->SetContentType("text/xml; charset=utf-8");
        break;

    case WSP_RESPONSE_JAVA:
        ResultHeader()->SetContentType("java/*");
        break;

    case WSP_RESPONSE_TEXT:
        ResultHeader()->SetContentType("text/plain; charset=utf-8");
        break;

    case WSP_RESPONSE_ICON:
        ResultHeader()->SetContentType("image/x-icon");
        break;

    case WSP_RESPONSE_PNG:
        ResultHeader()->SetContentType("image/png");
        break;

    case WSP_RESPONSE_GIF:
        ResultHeader()->SetContentType("image/gif");
        break;

    case WSP_RESPONSE_BMP:
        ResultHeader()->SetContentType("image/bmp");
        break;

    case WSP_RESPONSE_WAV:
        ResultHeader()->SetContentType("audio/wav");
        break;

    case WSP_RESPONSE_OGG:
        ResultHeader()->SetContentType("audio/ogg");
        break;

    case WSP_RESPONSE_MP3:
        ResultHeader()->SetContentType("audio/mpeg");
        break;

    case WSP_RESPONSE_BINARY:
        ResultHeader()->SetContentType("application/octet-stream");
        break;

    case WSP_RESPONSE_SVG:
        ResultHeader()->SetContentType("image/svg+xml");
        break;

    case WSP_RESPONSE_PDF:
        ResultHeader()->SetContentType("application/pdf");
        break;

    case WSP_RESPONSE_TTF:
    case WSP_RESPONSE_TFLITE:
        ResultHeader()->SetContentType("application/octet-stream");
        break;

    case WSP_RESPONSE_EOT:
        ResultHeader()->SetContentType("application/vnd.ms-fontobject");
        break;

    case WSP_RESPONSE_WOFF:
        ResultHeader()->SetContentType("application/font-woff");
        break;

    case WSP_RESPONSE_G711:
        ResultHeader()->SetContentType("audio/G711-0");
        break;

    case WSP_RESPONSE_G722:
        ResultHeader()->SetContentType("audio/G722");
        break;

    case WSP_RESPONSE_G729:
        ResultHeader()->SetContentType("audio/G729");
        break;

    case WSP_RESPONSE_JPEG:
        ResultHeader()->SetContentType("image/jpeg");
        break;

    case WSP_RESPONSE_MP4:
        ResultHeader()->SetContentType("video/mp4");
        break;

    case WSP_RESPONSE_WEBM:
        ResultHeader()->SetContentType("video/webm");
        break;

    case WSP_RESPONSE_PEM:
        ResultHeader()->SetContentType("application/x-pem-file");
        break;

    case WSP_RESPONSE_MOBILECONFIG:
        ResultHeader()->SetContentType("application/x-apple-aspen-config");
        break;

    case WSP_RESPONSE_CACHE_MANIFEST:
        ResultHeader()->SetContentType("text/cache-manifest");
        break;

    case WSP_RESPONSE_CSV:
        ResultHeader()->SetContentType("text/csv");
        break;

    case WSP_RESPONSE_APPLICATION_XML:
        ResultHeader()->SetContentType("application/xml; charset=utf-8");
        break;

    case WSP_RESPONSE_WASM:
        ResultHeader()->SetContentType("application/wasm");
        break;

    default:
        //this->log->Log(LOG_WEBSERVER, "#WARNING HTTPPostHandler::SetResultMIMEType(0x%x): Connection %u (path registered: %s, requested: %s) received unkown resource type %u - using plain text",
        //    this,
        //    User()->GetConnectionID(),
        //    this->RegisteredPathForRequest(),
        //    this->RequestPath(),
        //    type);
        ResultHeader()->SetContentType("text/plain; charset=utf-8");
        break;
    }
}
