root/src/kits/network/libnetservices2/HttpResultPrivate.h
/*
 * Copyright 2022 Haiku Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Niels Sascha Reedijk, niels.reedijk@gmail.com
 */

#ifndef _HTTP_RESULT_PRIVATE_H_
#define _HTTP_RESULT_PRIVATE_H_


#include <memory>
#include <optional>
#include <string>

#include <DataIO.h>
#include <ExclusiveBorrow.h>
#include <OS.h>
#include <String.h>


namespace BPrivate {

namespace Network {

struct HttpResultPrivate {
        // Read-only properties (multi-thread safe)
        const   int32                           id;

        // Locking and atomic variables
        enum { kNoData = 0, kStatusReady, kHeadersReady, kBodyReady, kError };
                        int32                           requestStatus = kNoData;
                        int32                           canCancel = 0;
                        sem_id                          data_wait;

        // Data
                        std::optional<BHttpStatus> status;
                        std::optional<BHttpFields> fields;
                        std::optional<BHttpBody> body;
                        std::optional<std::exception_ptr> error;

        // Interim body storage (used while the request is running)
                        BString                         bodyString;
                        BBorrow<BDataIO>        bodyTarget;

        // Utility functions
                                                                HttpResultPrivate(int32 identifier);
                        int32                           GetStatusAtomic();
                        bool                            CanCancel();
                        void                            SetCancel();
                        void                            SetError(std::exception_ptr e);
                        void                            SetStatus(BHttpStatus&& s);
                        void                            SetFields(BHttpFields&& f);
                        void                            SetBody();
                        size_t                          WriteToBody(const void* buffer, size_t size);
};


inline HttpResultPrivate::HttpResultPrivate(int32 identifier)
        :
        id(identifier)
{
        std::string name = "httpresult:" + std::to_string(identifier);
        data_wait = create_sem(1, name.c_str());
        if (data_wait < B_OK)
                throw BRuntimeError(__PRETTY_FUNCTION__, "Cannot create internal sem for httpresult");
}


inline int32
HttpResultPrivate::GetStatusAtomic()
{
        return atomic_get(&requestStatus);
}


inline bool
HttpResultPrivate::CanCancel()
{
        return atomic_get(&canCancel) == 1;
}


inline void
HttpResultPrivate::SetCancel()
{
        atomic_set(&canCancel, 1);
}


inline void
HttpResultPrivate::SetError(std::exception_ptr e)
{
        // Release any held body target borrow
        bodyTarget.Return();

        error = e;
        atomic_set(&requestStatus, kError);
        release_sem(data_wait);
}


inline void
HttpResultPrivate::SetStatus(BHttpStatus&& s)
{
        status = std::move(s);
        atomic_set(&requestStatus, kStatusReady);
        release_sem(data_wait);
}


inline void
HttpResultPrivate::SetFields(BHttpFields&& f)
{
        fields = std::move(f);
        atomic_set(&requestStatus, kHeadersReady);
        release_sem(data_wait);
}


inline void
HttpResultPrivate::SetBody()
{
        if (bodyTarget.HasValue()) {
                body = BHttpBody{};
                bodyTarget.Return();
        } else
                body = BHttpBody{std::move(bodyString)};

        atomic_set(&requestStatus, kBodyReady);
        release_sem(data_wait);
}


inline size_t
HttpResultPrivate::WriteToBody(const void* buffer, size_t size)
{
        // TODO: when the support for a shared BMemoryRingIO is here, choose
        // between one or the other depending on which one is available.
        if (bodyTarget.HasValue()) {
                auto result = bodyTarget->Write(buffer, size);
                if (result < 0)
                        throw BSystemError("BDataIO::Write()", result);
                return result;
        } else {
                bodyString.Append(reinterpret_cast<const char*>(buffer), size);
                return size;
        }
}


} // namespace Network

} // namespace BPrivate

#endif // _HTTP_RESULT_PRIVATE_H_