NetServices: Initial implementation of BHttpResult
Incomplete class, but will provide the basis to start working on the internals of the BHttpSession. Change-Id: I3ca14b7bd823fc1b4a5a32f5784592d214c4e9a7
This commit is contained in:
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
* Copyright 2022 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Niels Sascha Reedijk, [email protected]
|
||||
*
|
||||
* Corresponds to:
|
||||
* headers/private/netservices2/HttpResult.h hrev?????
|
||||
* src/kits/network/libnetservices2/HttpResult.cpp hrev?????
|
||||
*/
|
||||
|
||||
|
||||
#if __cplusplus >= 201703L
|
||||
|
||||
|
||||
/*!
|
||||
\file HttpResult.h
|
||||
\ingroup netservices
|
||||
\brief Provides classes and tools to handle HTTP responses.
|
||||
|
||||
\since Haiku R1
|
||||
*/
|
||||
|
||||
|
||||
namespace BPrivate {
|
||||
|
||||
namespace Network {
|
||||
|
||||
|
||||
/*!
|
||||
\struct BHttpStatus
|
||||
\ingroup netservices
|
||||
\brief Represents the HTTP status code and status text of an incoming response.
|
||||
|
||||
The HTTP Standard specifies that each response should include a
|
||||
<a href="https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2">three digit status
|
||||
code</a> and a phrase that describes the status code. When processing the response status, you
|
||||
should ignore the textual representation and only look at the status code.
|
||||
|
||||
Instances of this class provide a representation of the actual status information in the
|
||||
response. There is no additional validation done, other than that the structure of the
|
||||
status line is in compliance with the HTTP specification.
|
||||
|
||||
\since Haiku R1
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
\var int16 BHttpStatus::code
|
||||
\brief The three digit status code
|
||||
|
||||
This code represents the result of how the server is processing or has processed a request.
|
||||
Common codes are \c 200 indicating success, or \c 404 indicating that the requested resource is
|
||||
not found.
|
||||
|
||||
See <a href="https://datatracker.ietf.org/doc/html/rfc7231#page-48">RFC7231 and its
|
||||
references</a> for more information.
|
||||
|
||||
\since Haiku R1
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
\var BString BHttpStatus::text
|
||||
\brief A textual representation of the result status.
|
||||
|
||||
As defined in the
|
||||
<a href="https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2">specification</a>, the
|
||||
status text should only be taken as informative. Only the \ref BHttpStatus::code should be used
|
||||
for further processing.
|
||||
|
||||
\since Haiku R1
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
\class BHttpResult
|
||||
\ingroup netservices
|
||||
\brief Unique object to wait for and access a HTTP response data.
|
||||
|
||||
Once you have scheduled a HTTP request in a HTTP session, you will get an object of this type
|
||||
as a return value. This object allows you to track the progress of receiving the response, and
|
||||
to inspect the status, the headers and the data as the response is received from the server.
|
||||
|
||||
The object is a future type, meaning that eventually it will contain the data or an error.
|
||||
The \ref Status(), \ref Fields() and \ref Body() methods will yield the respective data. If it
|
||||
is not yet received, they will block until it is available. You can also use the non-blocking
|
||||
methods to check if data is available yet.
|
||||
|
||||
The result can either be a partial or completed HTTP Response, or an error. The partial aspect
|
||||
is represented by the fact that the status line, the fields and the body are loaded
|
||||
progressively and can be accessed as soon as they have been received. The meaning of a HTTP
|
||||
response is defined by the HTTP standards. For example, a GET request can return a response
|
||||
with a 200 status code, a set of headers and a body. But it can also return a 404 response,
|
||||
indicating that the resource was not found at the location. It is important to note that both
|
||||
responses are valid HTTP responses within the context of this API. This means that you can
|
||||
still use the access methods of this class to access data from the 404 response without raising
|
||||
an exception.
|
||||
|
||||
When there are errors during the request that lead to the situation where there is no valid
|
||||
response according to the HTTP specification, then this object goes into an error state. This
|
||||
means that the access methods of this object will throw an exception of the
|
||||
\ref BNetServiceError type.
|
||||
|
||||
A special property of this object is that it is unique. This means it cannot be copied, only
|
||||
moved. Objects that have moved from, are in an invalid state, and will always raise a
|
||||
\ref BRuntimeError exception when they are used.
|
||||
|
||||
\since Haiku R1
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
\name Constructors, assignment operators and destructor
|
||||
*/
|
||||
|
||||
|
||||
//! @{
|
||||
|
||||
|
||||
/*!
|
||||
\fn BHttpResult::BHttpResult(BHttpResult &&other) noexcept
|
||||
\brief Move constructor.
|
||||
|
||||
\param other The object to move from. The \a other object will be in an invalid state and will
|
||||
always throw exceptions when it is used.
|
||||
|
||||
\since Haiku R1
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
\fn BHttpResult::BHttpResult(const BHttpResult &other)=delete
|
||||
\brief Copy constructor is disabled.
|
||||
|
||||
These objects cannot be copied.
|
||||
|
||||
\since Haiku R1
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
\fn BHttpResult& BHttpResult::operator=(BHttpResult &&other) noexcept
|
||||
\brief Move operator.
|
||||
|
||||
\param other The object to move from. The \a other object will be in an invalid state and will
|
||||
always throw exceptions when it is used.
|
||||
|
||||
\since Haiku R1
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
\fn BHttpResult& BHttpResult::operator=(const BHttpResult &other)=delete
|
||||
\brief Copy assignment is disabled.
|
||||
|
||||
These objects cannot be copied.
|
||||
|
||||
\since Haiku R1
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
\fn BHttpResult::~BHttpResult()
|
||||
\brief Destructor
|
||||
|
||||
\since Haiku R1
|
||||
*/
|
||||
|
||||
|
||||
//! @}
|
||||
|
||||
|
||||
/*!
|
||||
\name Non-blocking status functions
|
||||
*/
|
||||
|
||||
|
||||
//! @{
|
||||
|
||||
|
||||
/*!
|
||||
\fn int32 BHttpResult::Identity() const
|
||||
\brief Unique identifier for the response.
|
||||
|
||||
The identifier can be used to cancel requests in a BHttpSession. It can also be uses to check
|
||||
incoming asynchronous event messages against the response.
|
||||
|
||||
\return A unique identifier that associates this response with an active or completed request.
|
||||
|
||||
\exception BRuntimeException This exception is raised when the object has been moved from and
|
||||
is thus no longer valid.
|
||||
|
||||
\since Haiku R1
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
\fn bool BHttpResult::HasStatus() const
|
||||
\brief Check if the status is available.
|
||||
|
||||
\retval true The status line of the response is available using the \ref Status() method.
|
||||
\retval false The line is not yet available. Any call to \ref Status() will block.
|
||||
|
||||
\exception BRuntimeException This exception is raised when the object has been moved from and
|
||||
is thus no longer valid.
|
||||
|
||||
\since Haiku R1
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
\fn bool BPrivate::Network::BHttpResult::HasHeaders() const
|
||||
\brief Check if the headers are available.
|
||||
|
||||
\retval true The headers of the response is available using the \ref Headers() method.
|
||||
\retval false They are not yet available. Any call to \ref Headers() will block.
|
||||
|
||||
\exception BRuntimeException This exception is raised when the object has been moved from and
|
||||
is thus no longer valid.
|
||||
|
||||
\since Haiku R1
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
\fn bool BPrivate::Network::BHttpResult::HasBody() const
|
||||
\brief Check if the body is available.
|
||||
|
||||
\retval true The body of the response is available using the \ref Body() method.
|
||||
\retval false The body is not yet available. Any call to \ref Body() will block.
|
||||
|
||||
\exception BRuntimeException This exception is raised when the object has been moved from and
|
||||
is thus no longer valid.
|
||||
|
||||
\since Haiku R1
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
\fn bool BPrivate::Network::BHttpResult::IsCompleted() const
|
||||
\brief Check if the request is completed.
|
||||
|
||||
A request is completed when the status, headers and body have been received, or an error was
|
||||
raised while receiving the data.
|
||||
|
||||
\exception BRuntimeException This exception is raised when the object has been moved from and
|
||||
is thus no longer valid.
|
||||
|
||||
\since Haiku R1
|
||||
*/
|
||||
|
||||
|
||||
//! @}
|
||||
|
||||
|
||||
/*!
|
||||
\name Blocking Data Access
|
||||
*/
|
||||
|
||||
|
||||
//! @{
|
||||
|
||||
|
||||
/*!
|
||||
\fn const BHttpStatus& BHttpResult::Status() const
|
||||
\brief Retrieve the status line of the HTTP response.
|
||||
|
||||
If the status line is not yet available, then this method call will block until it is. You can
|
||||
use the \ref HasStatus() method to do a non-blocking check if the status is available.
|
||||
|
||||
\returns A const reference to the \ref BHttpStatus object that describes the status of the
|
||||
response.
|
||||
|
||||
\exception BRuntimeException This exception is raised when the object has been moved from and
|
||||
is thus no longer valid.
|
||||
\exception BNetServicesError This exception is raised when there was an error that prevented
|
||||
completely retrieving and parsing the HTTP response.
|
||||
|
||||
\since Haiku R1
|
||||
*/
|
||||
|
||||
|
||||
//! @}
|
||||
|
||||
|
||||
} // namespace BPrivate
|
||||
|
||||
} // namespace Network
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2022 Haiku Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#ifndef _B_HTTP_RESULT_H_
|
||||
#define _B_HTTP_RESULT_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <String.h>
|
||||
|
||||
|
||||
namespace BPrivate {
|
||||
|
||||
namespace Network {
|
||||
|
||||
struct HttpResultPrivate;
|
||||
|
||||
|
||||
struct BHttpStatus
|
||||
{
|
||||
int16 code = 0;
|
||||
BString text;
|
||||
};
|
||||
|
||||
|
||||
class BHttpResult
|
||||
{
|
||||
public:
|
||||
// Constructors and destructor
|
||||
BHttpResult(const BHttpResult& other) = delete;
|
||||
BHttpResult(BHttpResult&& other) noexcept;
|
||||
~BHttpResult();
|
||||
|
||||
// Assignment operators
|
||||
BHttpResult& operator=(const BHttpResult& other) = delete;
|
||||
BHttpResult& operator=(BHttpResult&& other) noexcept;
|
||||
|
||||
// Blocking Access Functions
|
||||
const BHttpStatus& Status() const;
|
||||
// BHttpHeaders& Headers() const;
|
||||
// BHttpBody& Body() const;
|
||||
|
||||
// Check if data is available yet
|
||||
bool HasStatus() const;
|
||||
bool HasHeaders() const;
|
||||
bool HasBody() const;
|
||||
bool IsCompleted() const;
|
||||
|
||||
// Identity
|
||||
int32 Identity() const;
|
||||
|
||||
private:
|
||||
friend class BHttpSession;
|
||||
BHttpResult(std::shared_ptr<HttpResultPrivate> data);
|
||||
std::shared_ptr<HttpResultPrivate> fData;
|
||||
};
|
||||
|
||||
|
||||
} // namespace Network
|
||||
|
||||
} // namespace BPrivate
|
||||
|
||||
#endif // _B_HTTP_RESPONSE_H_
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2022 Haiku Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Niels Sascha Reedijk, [email protected]
|
||||
*/
|
||||
|
||||
|
||||
#include <ErrorsExt.h>
|
||||
#include <HttpResult.h>
|
||||
|
||||
#include "HttpResultPrivate.h"
|
||||
|
||||
using namespace BPrivate::Network;
|
||||
|
||||
|
||||
/*private*/
|
||||
BHttpResult::BHttpResult(std::shared_ptr<HttpResultPrivate> data)
|
||||
: fData(data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
BHttpResult::BHttpResult(BHttpResult&& other) noexcept = default;
|
||||
|
||||
|
||||
BHttpResult::~BHttpResult()
|
||||
{
|
||||
if (fData)
|
||||
fData->SetCancel();
|
||||
}
|
||||
|
||||
|
||||
BHttpResult&
|
||||
BHttpResult::operator=(BHttpResult&& other) noexcept = default;
|
||||
|
||||
|
||||
const BHttpStatus&
|
||||
BHttpResult::Status() const
|
||||
{
|
||||
if (!fData)
|
||||
throw BRuntimeError(__PRETTY_FUNCTION__, "The BHttpResult object is no longer valid");
|
||||
status_t status = B_OK;
|
||||
while (status == B_INTERRUPTED || status == B_OK) {
|
||||
auto dataStatus = fData->GetStatusAtomic();
|
||||
if (dataStatus == HttpResultPrivate::kError)
|
||||
std::rethrow_exception(*(fData->error));
|
||||
|
||||
if (dataStatus >= HttpResultPrivate::kStatusReady)
|
||||
return *(fData->status);
|
||||
|
||||
status = acquire_sem(fData->data_wait);
|
||||
}
|
||||
throw BRuntimeError(__PRETTY_FUNCTION__, "Unexpected error waiting for status!");
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
BHttpResult::HasStatus() const
|
||||
{
|
||||
if (!fData)
|
||||
throw BRuntimeError(__PRETTY_FUNCTION__, "The BHttpResult object is no longer valid");
|
||||
return fData->GetStatusAtomic() >= HttpResultPrivate::kStatusReady;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
BHttpResult::HasHeaders() const
|
||||
{
|
||||
if (!fData)
|
||||
throw BRuntimeError(__PRETTY_FUNCTION__, "The BHttpResult object is no longer valid");
|
||||
return fData->GetStatusAtomic() >= HttpResultPrivate::kHeadersReady;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
BHttpResult::HasBody() const
|
||||
{
|
||||
if (!fData)
|
||||
throw BRuntimeError(__PRETTY_FUNCTION__, "The BHttpResult object is no longer valid");
|
||||
return fData->GetStatusAtomic() >= HttpResultPrivate::kBodyReady;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
BHttpResult::IsCompleted() const
|
||||
{
|
||||
if (!fData)
|
||||
throw BRuntimeError(__PRETTY_FUNCTION__, "The BHttpResult object is no longer valid");
|
||||
return HasBody();
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
BHttpResult::Identity() const
|
||||
{
|
||||
if (!fData)
|
||||
throw BRuntimeError(__PRETTY_FUNCTION__, "The BHttpResult object is no longer valid");
|
||||
return fData->id;
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright 2022 Haiku Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Niels Sascha Reedijk, [email protected]
|
||||
*/
|
||||
|
||||
#ifndef _HTTP_RESULT_PRIVATE_H_
|
||||
#define _HTTP_RESULT_PRIVATE_H_
|
||||
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include <DataIO.h>
|
||||
#include <OS.h>
|
||||
|
||||
|
||||
namespace BPrivate {
|
||||
|
||||
namespace Network {
|
||||
|
||||
struct HttpResultPrivate {
|
||||
// Read-only properties (multi-thread safe)
|
||||
const int32 id;
|
||||
|
||||
// Locking and atomic variables
|
||||
sem_id data_wait;
|
||||
enum {
|
||||
kNoData = 0,
|
||||
kStatusReady,
|
||||
kHeadersReady,
|
||||
kBodyReady,
|
||||
kError
|
||||
};
|
||||
int32 requestStatus = kNoData;
|
||||
int32 canCancel = 0;
|
||||
|
||||
// Data
|
||||
std::optional<BHttpStatus> status;
|
||||
// std::optional<BHttpHeaders> headers;
|
||||
// std::optional<BHttpBody> body;
|
||||
std::optional<std::exception_ptr> error;
|
||||
|
||||
// Body storage
|
||||
std::unique_ptr<BDataIO> owned_body = nullptr;
|
||||
// std::shared_ptr<BMemoryRingIO> shared_body = nullptr;
|
||||
std::string body_text;
|
||||
|
||||
// Utility functions
|
||||
HttpResultPrivate(int32 identifier);
|
||||
int32 GetStatusAtomic();
|
||||
bool CanCancel();
|
||||
void SetCancel();
|
||||
void SetError(std::exception_ptr e);
|
||||
void SetStatus(BHttpStatus&& s);
|
||||
// void SetHeaders(BHttpHeaders&& h);
|
||||
// void SetBody();
|
||||
ssize_t WriteToBody(const void* buffer, ssize_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)
|
||||
{
|
||||
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::SetHeaders(BHttpHeaders&& h)
|
||||
//{
|
||||
// headers = std::move(h);
|
||||
// atomic_set(&requestStatus, kHeadersReady);
|
||||
// release_sem(data_wait);
|
||||
//}
|
||||
|
||||
|
||||
//inline void
|
||||
//HttpResultPrivate::SetBody()
|
||||
//{
|
||||
// body = BHttpBody{std::move(owned_body), std::move(body_text)};
|
||||
// atomic_set(&requestStatus, kBodyReady);
|
||||
// release_sem(data_wait);
|
||||
//}
|
||||
|
||||
|
||||
inline ssize_t
|
||||
HttpResultPrivate::WriteToBody(const void* buffer, ssize_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 (owned_body == nullptr) {
|
||||
body_text.append(static_cast<const char*>(buffer), size);
|
||||
return size;
|
||||
}
|
||||
return owned_body->Write(buffer, size);
|
||||
}
|
||||
|
||||
|
||||
} // namespace Network
|
||||
|
||||
} // namespace BPrivate
|
||||
|
||||
#endif // _HTTP_RESULT_PRIVATE_H_
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021 Haiku Inc. All rights reserved.
|
||||
* Copyright 2022 Haiku Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
|
||||
@@ -18,6 +18,7 @@ for architectureObject in [ MultiArchSubDirSetup ] {
|
||||
ErrorsExt.cpp
|
||||
HttpFields.cpp
|
||||
HttpRequest.cpp
|
||||
HttpResult.cpp
|
||||
HttpSession.cpp
|
||||
NetServicesMisc.cpp
|
||||
;
|
||||
|
||||
Reference in New Issue
Block a user