diff --git a/docs/user/netservices/HttpRequest.dox b/docs/user/netservices/HttpRequest.dox
index d8d32d7808..575ba74b7d 100644
--- a/docs/user/netservices/HttpRequest.dox
+++ b/docs/user/netservices/HttpRequest.dox
@@ -329,6 +329,12 @@ namespace Network {
The URL. This must start with http or https. |
Defaults to an empty \ref BUrl |
+
+ | \ref Fields() |
+ \ref SetFields() |
+ Additional fields set in the request header. |
+ Defaults with no additional fields |
+
| \ref Method() |
\ref SetMethod() |
@@ -472,6 +478,16 @@ namespace Network {
//! @{
+/*!
+ \fn const BHttpFields& BHttpRequest::Fields() const noexcept
+ \brief Get the additional header fields set for the request.
+
+ The returned header fields may be empty if no additional header fields were set.
+
+ \since Haiku R1
+*/
+
+
/*!
\fn const BHttpMethod& BHttpRequest::Method() const noexcept
\brief Get the current method for the request.
@@ -515,6 +531,26 @@ namespace Network {
//! @{
+/*!
+ \fn void BHttpRequest::SetFields(const BHttpFields &fields)
+ \brief Set additional header \a fields for this request.
+
+ There are a few reserved fields, which cannot be set as optional fields. These currently are:
+ * \c Host
+ * \c Accept
+ * \c Accept-Encoding
+ * \c Connection
+
+ \param fields Additional fields for the header of the request.
+
+ \exception std::bad_alloc This exception may be raised if it is impossible to allocate memory.
+ \exception BHttpFields::InvalidData This exception is raised when the \a fields contain
+ reserved fields.
+
+ \since Haiku R1
+*/
+
+
/*!
\fn void BHttpRequest::SetMethod(const BHttpMethod &method)
\brief Set the \a method for this request.
diff --git a/headers/private/netservices2/HttpRequest.h b/headers/private/netservices2/HttpRequest.h
index b9dcac4fe2..fe1c905f00 100644
--- a/headers/private/netservices2/HttpRequest.h
+++ b/headers/private/netservices2/HttpRequest.h
@@ -23,6 +23,9 @@ namespace BPrivate {
namespace Network {
+class BHttpFields;
+
+
class BHttpMethod {
public:
// Constants for default methods in RFC 7230 section 4.2
@@ -92,11 +95,13 @@ public:
// Access
bool IsEmpty() const noexcept;
+ const BHttpFields& Fields() const noexcept;
const BHttpMethod& Method() const noexcept;
const BHttpRedirectOptions& Redirect() const noexcept;
const BUrl& Url() const noexcept;
// Named Setters
+ void SetFields(const BHttpFields& fields);
void SetMethod(const BHttpMethod& method);
void SetRedirect(const BHttpRedirectOptions& redirectOptions);
void SetUrl(const BUrl& url);
diff --git a/src/kits/network/libnetservices2/HttpRequest.cpp b/src/kits/network/libnetservices2/HttpRequest.cpp
index 4b11831b09..07e8fb6e9b 100644
--- a/src/kits/network/libnetservices2/HttpRequest.cpp
+++ b/src/kits/network/libnetservices2/HttpRequest.cpp
@@ -157,12 +157,13 @@ BHttpMethod::Method() const noexcept
static const BUrl kDefaultUrl = BUrl();
static const BHttpMethod kDefaultMethod = BHttpMethod::Get;
static const BHttpRedirectOptions kDefaultRedirectOptions = BHttpRedirectOptions();
-
+static const BHttpFields kDefaultOptionalFields = BHttpFields();
struct BHttpRequest::Data {
BUrl url = kDefaultUrl;
BHttpMethod method = kDefaultMethod;
BHttpRedirectOptions redirectOptions;
+ BHttpFields optionalFields;
};
@@ -200,6 +201,15 @@ BHttpRequest::IsEmpty() const noexcept
}
+const BHttpFields&
+BHttpRequest::Fields() const noexcept
+{
+ if (!fData)
+ return kDefaultOptionalFields;
+ return fData->optionalFields;
+}
+
+
const BHttpMethod&
BHttpRequest::Method() const noexcept
{
@@ -227,6 +237,32 @@ BHttpRequest::Url() const noexcept
}
+static constexpr std::array fReservedOptionalFieldNames = {
+ "Host"sv,
+ "Accept"sv,
+ "Accept-Encoding"sv,
+ "Connection"sv
+};
+
+
+void
+BHttpRequest::SetFields(const BHttpFields& fields)
+{
+ if (!fData)
+ fData = std::make_unique();
+
+ for (auto& field: fields) {
+ if (std::find(fReservedOptionalFieldNames.begin(), fReservedOptionalFieldNames.end(),
+ field.Name()) != fReservedOptionalFieldNames.end())
+ {
+ std::string_view fieldName = field.Name();
+ throw BHttpFields::InvalidInput(__PRETTY_FUNCTION__, BString(fieldName.data(), fieldName.size()));
+ }
+ }
+ fData->optionalFields = fields;
+}
+
+
void
BHttpRequest::SetMethod(const BHttpMethod& method)
{
@@ -326,6 +362,11 @@ BHttpRequest::SerializeHeaderTo(BDataIO* target) const
bytesWritten += _write_to_dataio(target, "\r\n"sv);
}
+ for (const auto& field: fData->optionalFields) {
+ bytesWritten += _write_to_dataio(target, field.RawField());
+ bytesWritten += _write_to_dataio(target, "\r\n"sv);
+ }
+
bytesWritten += _write_to_dataio(target, "\r\n"sv);
return bytesWritten;
}
diff --git a/src/tests/kits/net/netservices2/HttpProtocolTest.cpp b/src/tests/kits/net/netservices2/HttpProtocolTest.cpp
index 24412b7845..533c190b56 100644
--- a/src/tests/kits/net/netservices2/HttpProtocolTest.cpp
+++ b/src/tests/kits/net/netservices2/HttpProtocolTest.cpp
@@ -276,12 +276,13 @@ HttpProtocolTest::HttpMethodTest()
}
-constexpr std::string_view kHaikuGetRequestText =
+constexpr std::string_view kExpectedRequestText =
"GET / HTTP/1.1\r\n"
"Host: www.haiku-os.org\r\n"
"Accept: *\r\n"
"Accept-Encoding: gzip\r\n"
- "Connection: close\r\n\r\n";
+ "Connection: close\r\n"
+ "Api-Key: 01234567890abcdef\r\n\r\n";
void
@@ -294,9 +295,22 @@ HttpProtocolTest::HttpRequestTest()
request.SetUrl(url);
CPPUNIT_ASSERT(request.Url() == url);
+ // Add Invalid HTTP fields (should throw)
+ try {
+ BHttpFields invalidField = {{"Host"sv, "haiku-os.org"sv}};
+ request.SetFields(invalidField);
+ CPPUNIT_FAIL("Should not be able to add the invalid \"Host\" field to a request");
+ } catch (BHttpFields::InvalidInput& e) {
+ // Correct; do nothing
+ }
+
+ // Add valid HTTP field
+ BHttpFields validField = {{"Api-Key"sv, "01234567890abcdef"}};
+ request.SetFields(validField);
+
// Validate header serialization
BString header = request.HeaderToString();
- CPPUNIT_ASSERT(header.Compare(kHaikuGetRequestText.data(), kHaikuGetRequestText.size()) == 0);
+ CPPUNIT_ASSERT(header.Compare(kExpectedRequestText.data(), kExpectedRequestText.size()) == 0);
}
@@ -326,6 +340,14 @@ private:
};
+constexpr std::string_view kExpectedStreamText =
+ "GET / HTTP/1.1\r\n"
+ "Host: www.haiku-os.org\r\n"
+ "Accept: *\r\n"
+ "Accept-Encoding: gzip\r\n"
+ "Connection: close\r\n\r\n";
+
+
void
HttpProtocolTest::HttpRequestStreamTest()
{
@@ -336,11 +358,11 @@ HttpProtocolTest::HttpRequestStreamTest()
// Test streaming the request
BHttpRequestStream requestStream(request);
- RequestStreamTestIO testIO(kHaikuGetRequestText.data());
+ RequestStreamTestIO testIO(kExpectedStreamText.data());
bool finished = false;
ssize_t expectedBytesWritten = 8;
ssize_t expectedTotalBytesWritten = 8;
- const ssize_t expectedTotalSize = kHaikuGetRequestText.size();
+ const ssize_t expectedTotalSize = kExpectedStreamText.size();
if (expectedTotalSize < 8) {
expectedBytesWritten = expectedTotalSize;
expectedTotalBytesWritten = expectedTotalSize;