NetServices: Serialize the header of a BHttpRequest to a string
Change-Id: Ib1e22536a0b39dc6e9461e7993ea6784f1ea0e2f
This commit is contained in:
@@ -476,6 +476,23 @@ namespace Network {
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
\fn void BHttpFields::AddFields(std::initializer_list< Field > fields)
|
||||
\brief Add a list of fields.
|
||||
|
||||
This enables you to add a list of \ref BHttpFields::Field objects. Like \ref AddField(), the
|
||||
fields are added in the the original order, though if there are duplicate keys within the
|
||||
\a fields list, or there are existing keys in the object, they will be grouped together in
|
||||
sequence.
|
||||
|
||||
\exception std::bad_alloc Error in case memory cannot be allocated.
|
||||
\exception BHttpFields::InvalidInput This error indicates that some of the names or values in
|
||||
the list do not adhere to the HTTP specification.
|
||||
|
||||
\since Haiku R1
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
\fn void BHttpFields::RemoveField(const std::string_view &name) noexcept
|
||||
\brief Remove all fields with the \a name.
|
||||
|
||||
@@ -473,6 +473,54 @@ namespace Network {
|
||||
//! @}
|
||||
|
||||
|
||||
/*!
|
||||
\name Serialization
|
||||
*/
|
||||
|
||||
|
||||
//! @{
|
||||
|
||||
|
||||
/*!
|
||||
\fn ssize_t BHttpRequest::SerializeHeaderTo(BDataIO *target) const
|
||||
\brief Serialize the HTTP Header of this request to the \a target.
|
||||
|
||||
The HTTP header consists of the request line, and the fields, serialized as text according to
|
||||
the HTTP specification.
|
||||
|
||||
\param target The \ref BDataIO object to write the header to
|
||||
|
||||
\return The total number of byte size of the header.
|
||||
|
||||
\exception BSystemError In case there is an error when calling the \ref BDataIO::Write() call
|
||||
of the provided \a target. Note that writing the string is \em not transactional, and that
|
||||
the error can occur while a part of the header has already been written.
|
||||
\exception std::bad_alloc In case it is not possible to allocate internal buffers.
|
||||
|
||||
\since Haiku R1
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
\fn BString BHttpRequest::HeaderToString() const
|
||||
\brief Serialize the HTTP Header of this request to a string.
|
||||
|
||||
The HTTP header consists of the request line, and the fields, serialized as text according to
|
||||
the HTTP specification.
|
||||
|
||||
This method can be used to debug requests.
|
||||
|
||||
\return A new string that represents the HTTP request.
|
||||
|
||||
\exception std::bad_alloc In case it is not possible to allocate memory for the output string.
|
||||
|
||||
\since Haiku R1
|
||||
*/
|
||||
|
||||
|
||||
//! @}
|
||||
|
||||
|
||||
} // namespace Network
|
||||
|
||||
} // namespace BPrivate
|
||||
|
||||
@@ -219,7 +219,7 @@ namespace Network {
|
||||
to initialize it to \c __PRETTY_FUNCTION__ by default.
|
||||
\param type The error type that describes what the issue was that prevented the completion of
|
||||
the request.
|
||||
\param error Optional underlying system error. See the \ref BNetworkRequestError::ErrorType
|
||||
\param errorCode Optional underlying system error. See the \ref BNetworkRequestError::ErrorType
|
||||
documentation on which error types expect a system error.
|
||||
|
||||
\since Haiku R1
|
||||
|
||||
@@ -103,6 +103,7 @@ public:
|
||||
|
||||
// Modifiers
|
||||
void AddField(const std::string_view& name, const std::string_view& value);
|
||||
void AddFields(std::initializer_list<Field> fields);
|
||||
void RemoveField(const std::string_view& name) noexcept;
|
||||
void RemoveField(ConstIterator it) noexcept;
|
||||
void MakeEmpty() noexcept;
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
#include <ErrorsExt.h>
|
||||
#include <String.h>
|
||||
|
||||
class BDataIO;
|
||||
class BMallocIO;
|
||||
class BUrl;
|
||||
|
||||
|
||||
@@ -87,6 +89,9 @@ public:
|
||||
void SetMethod(const BHttpMethod& method);
|
||||
void SetUrl(const BUrl& url);
|
||||
|
||||
// Serialization
|
||||
ssize_t SerializeHeaderTo(BDataIO* target) const;
|
||||
BString HeaderToString() const;
|
||||
private:
|
||||
friend class BHttpSession;
|
||||
struct Data;
|
||||
|
||||
@@ -407,6 +407,16 @@ BHttpFields::AddField(const std::string_view& name, const std::string_view& valu
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
BHttpFields::AddFields(std::initializer_list<Field> fields)
|
||||
{
|
||||
for (auto& field: fields) {
|
||||
if (!field.IsEmpty())
|
||||
_AddField(Field(field));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
BHttpFields::RemoveField(const std::string_view& name) noexcept
|
||||
{
|
||||
|
||||
@@ -10,8 +10,11 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <ctype.h>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
|
||||
#include <DataIO.h>
|
||||
#include <HttpFields.h>
|
||||
#include <NetServicesDefs.h>
|
||||
#include <Url.h>
|
||||
|
||||
@@ -219,3 +222,83 @@ BHttpRequest::SetUrl(const BUrl& url)
|
||||
}
|
||||
fData->url = url;
|
||||
}
|
||||
|
||||
|
||||
[[nodiscard]] static inline ssize_t
|
||||
_write_to_dataio(BDataIO* target, const std::string_view& data)
|
||||
{
|
||||
if (auto status = target->WriteExactly(data.data(), data.size()); status != B_OK)
|
||||
throw BSystemError("BDataIO::WriteExactly()", status);
|
||||
return data.size();
|
||||
}
|
||||
|
||||
|
||||
[[nodiscard]] static inline ssize_t
|
||||
_write_to_dataio(BDataIO* target, const BString& string)
|
||||
{
|
||||
auto length = string.Length();
|
||||
if (auto status = target->WriteExactly(string.String(), length); status != B_OK)
|
||||
throw BSystemError("BDataIO::WriteExactly()", status);
|
||||
return length;
|
||||
}
|
||||
|
||||
|
||||
ssize_t
|
||||
BHttpRequest::SerializeHeaderTo(BDataIO* target) const
|
||||
{
|
||||
auto bytesWritten = _write_to_dataio(target, fData->method.Method());
|
||||
bytesWritten += _write_to_dataio(target, " "sv);
|
||||
|
||||
// TODO: proxy
|
||||
|
||||
if (fData->url.HasPath() && fData->url.Path().Length() > 0)
|
||||
bytesWritten += _write_to_dataio(target, fData->url.Path());
|
||||
else
|
||||
bytesWritten += _write_to_dataio(target, "/"sv);
|
||||
|
||||
// TODO: switch between HTTP 1.0 and 1.1 based on configuration
|
||||
bytesWritten += _write_to_dataio(target, " HTTP/1.1\r\n"sv);
|
||||
|
||||
BHttpFields outputFields;
|
||||
if (true /* http == 1.1 */) {
|
||||
BString host = fData->url.Host();
|
||||
int defaultPort = fData->url.Protocol() == "http" ? 80 : 443;
|
||||
if (fData->url.HasPort() && fData->url.Port() != defaultPort)
|
||||
host << ':' << fData->url.Port();
|
||||
|
||||
outputFields.AddFields({
|
||||
{"Host"sv, std::string_view(host.String())},
|
||||
{"Accept"sv, "*"sv},
|
||||
{"Accept-Encoding"sv, "gzip"sv},
|
||||
// Allows the server to compress data using the "gzip" format.
|
||||
// "deflate" is not supported, because there are two interpretations
|
||||
// of what it means (the RFC and Microsoft products), and we don't
|
||||
// want to handle this. Very few websites support only deflate,
|
||||
// and most of them will send gzip, or at worst, uncompressed data.
|
||||
{"Connection"sv, "close"sv}
|
||||
// Let the remote server close the connection after response since
|
||||
// we don't handle multiple request on a single connection
|
||||
});
|
||||
}
|
||||
|
||||
for (const auto& field: outputFields) {
|
||||
std::string_view name = field.Name();
|
||||
bytesWritten += _write_to_dataio(target, name);
|
||||
bytesWritten += _write_to_dataio(target, ": "sv);
|
||||
bytesWritten += _write_to_dataio(target, field.Value());
|
||||
bytesWritten += _write_to_dataio(target, "\r\n"sv);
|
||||
}
|
||||
|
||||
bytesWritten += _write_to_dataio(target, "\r\n"sv);
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
|
||||
BString
|
||||
BHttpRequest::HeaderToString() const
|
||||
{
|
||||
BMallocIO buffer;
|
||||
auto size = SerializeHeaderTo(&buffer);
|
||||
|
||||
return BString(static_cast<const char*>(buffer.Buffer()), size);
|
||||
}
|
||||
|
||||
@@ -234,6 +234,10 @@ HttpProtocolTest::HttpRequestTest()
|
||||
auto url = BUrl("https://www.haiku-os.org");
|
||||
request.SetUrl(url);
|
||||
CPPUNIT_ASSERT(request.Url() == url);
|
||||
|
||||
// Validate header serialization
|
||||
BString header = request.HeaderToString();
|
||||
CPPUNIT_ASSERT(header.Compare("GET / HTTP/1.1\r\nHost: www.haiku-os.org\r\nAccept: *\r\nAccept-Encoding: gzip\r\nConnection: close\r\n\r\n") == 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user