#include "output.h"

namespace {
using namespace NSrvKernel;

}  // namespace

namespace NSrvKernel {

TError THttpOutput::DoSendHead(TResponse&& response, const bool forceClose, TInstant deadline) {
    Y_ASSERT(Encoder_.Finished());

    THeaders& headers = response.Headers();
    const ui32 responseStatus = response.ResponseLine().StatusCode;
    const auto contentLength = response.Props().ContentLength;
    const bool chunkedTransfer = response.Props().ChunkedTransfer;
    const auto responseVersion = response.Props().Version;
    const auto connectionUpgraded = response.Props().UpgradeRequested;
    const bool explicitConnectionHeader = response.Props().AddExplicitConnectionHeader;

    NeedForceClose_ = forceClose || !RequestKeepAlive_;

    // To skip all the treatment on 100-continue.
    if (responseStatus == HTTP_CONTINUE) {
        Encoder_.InitNullContent();
    // Either request is HTTP/1.0 or response is HTTP/1.0.
    } else if (RequestMinorVersion_ == 0 || responseVersion == 0) {
        // HEAD request's method.
        if (HeadRequest_) {
            Encoder_.InitHeadResponse(headers, contentLength);
        } else if (contentLength) {
            Encoder_.InitContentLength(headers, *contentLength);
        } else if (NullResponseBody(responseStatus)) {
            Encoder_.InitNullContent();
        } else if (RequestMinorVersion_ == 1 && !RequestIsHttp2_) {
            // sending http/1.1 response to 1.1 client so that
            // keep-alive and chunked works as expected
            response.ResponseLine().MinorVersion = 1;
            Encoder_.InitChunkedTransfer(headers);
        } else {
            NeedForceClose_ = true;
            Encoder_.InitPlainContent();
        }

        NPrivate::TreatConnectionV0(!NeedForceClose_, explicitConnectionHeader, headers);
    // Both request and response is HTTP/1.1 (or HTTP/2 for request)
    } else if (RequestMinorVersion_ == 1) {
        // Websockets took place.
        if (responseStatus == HTTP_SWITCHING_PROTOCOLS) {
            Encoder_.InitPlainContent();
            NeedForceClose_ = true;
        // HEAD method in request.
        } else if (HeadRequest_) {
            Encoder_.InitHeadResponse(headers, contentLength);
        } else if (chunkedTransfer) {
            if (RequestIsHttp2_) {
                Encoder_.InitPlainContent();
            } else {
                Encoder_.InitChunkedTransfer(headers);
            }
        } else if (contentLength) {
            Encoder_.InitContentLength(headers, *contentLength);
        } else if (NullResponseBody(responseStatus)) {
            Encoder_.InitNullContent();
        } else {
            if (RequestIsHttp2_) {
                Encoder_.InitPlainContent();
            } else {
                Encoder_.InitChunkedTransfer(headers);
            }
        }

        NPrivate::TreatConnectionV1(connectionUpgraded, !NeedForceClose_, explicitConnectionHeader, headers);
    } else {
        return Y_MAKE_ERROR(THttpError{505});
    }

    return Encoder_.SendMessage(response, nullptr, Nothing(), deadline);
}

TError THttpOutput::DoSendTrailers(THeaders&&, TInstant) {
    return Y_MAKE_ERROR(yexception{} << "trailers are not supported");
}

}  // namespace NSrvKernel
