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
|
\fn void BHttpFields::RemoveField(const std::string_view &name) noexcept
|
||||||
\brief Remove all fields with the \a name.
|
\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 Network
|
||||||
|
|
||||||
} // namespace BPrivate
|
} // namespace BPrivate
|
||||||
|
|||||||
@@ -219,7 +219,7 @@ namespace Network {
|
|||||||
to initialize it to \c __PRETTY_FUNCTION__ by default.
|
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
|
\param type The error type that describes what the issue was that prevented the completion of
|
||||||
the request.
|
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.
|
documentation on which error types expect a system error.
|
||||||
|
|
||||||
\since Haiku R1
|
\since Haiku R1
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ public:
|
|||||||
|
|
||||||
// Modifiers
|
// Modifiers
|
||||||
void AddField(const std::string_view& name, const std::string_view& value);
|
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(const std::string_view& name) noexcept;
|
||||||
void RemoveField(ConstIterator it) noexcept;
|
void RemoveField(ConstIterator it) noexcept;
|
||||||
void MakeEmpty() noexcept;
|
void MakeEmpty() noexcept;
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
#include <ErrorsExt.h>
|
#include <ErrorsExt.h>
|
||||||
#include <String.h>
|
#include <String.h>
|
||||||
|
|
||||||
|
class BDataIO;
|
||||||
|
class BMallocIO;
|
||||||
class BUrl;
|
class BUrl;
|
||||||
|
|
||||||
|
|
||||||
@@ -87,6 +89,9 @@ public:
|
|||||||
void SetMethod(const BHttpMethod& method);
|
void SetMethod(const BHttpMethod& method);
|
||||||
void SetUrl(const BUrl& url);
|
void SetUrl(const BUrl& url);
|
||||||
|
|
||||||
|
// Serialization
|
||||||
|
ssize_t SerializeHeaderTo(BDataIO* target) const;
|
||||||
|
BString HeaderToString() const;
|
||||||
private:
|
private:
|
||||||
friend class BHttpSession;
|
friend class BHttpSession;
|
||||||
struct Data;
|
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
|
void
|
||||||
BHttpFields::RemoveField(const std::string_view& name) noexcept
|
BHttpFields::RemoveField(const std::string_view& name) noexcept
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,8 +10,11 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
#include <sstream>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
#include <DataIO.h>
|
||||||
|
#include <HttpFields.h>
|
||||||
#include <NetServicesDefs.h>
|
#include <NetServicesDefs.h>
|
||||||
#include <Url.h>
|
#include <Url.h>
|
||||||
|
|
||||||
@@ -219,3 +222,83 @@ BHttpRequest::SetUrl(const BUrl& url)
|
|||||||
}
|
}
|
||||||
fData->url = 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");
|
auto url = BUrl("https://www.haiku-os.org");
|
||||||
request.SetUrl(url);
|
request.SetUrl(url);
|
||||||
CPPUNIT_ASSERT(request.Url() == 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