diff --git a/docs/user/netservices/HttpRequest.dox b/docs/user/netservices/HttpRequest.dox
index ab05f5783d..049145c9d1 100644
--- a/docs/user/netservices/HttpRequest.dox
+++ b/docs/user/netservices/HttpRequest.dox
@@ -274,40 +274,6 @@ namespace Network {
*/
-/*!
- \struct BHttpRedirectOptions
- \ingroup netservices
- \brief Describe redirection options for a \ref BHttpRequest.
-
- \see These options are used by \ref BHttpRequest::Redirect() and
- \ref BHttpRequest::SetRedirect()
-
- \since Haiku R1
-*/
-
-
-/*!
- \var bool BHttpRedirectOptions::followRedirect
- \brief Describe whether the request should follow redirects.
-
- \see These options are used by \ref BHttpRequest::Redirect() and
- \ref BHttpRequest::SetRedirect()
-
- \since Haiku R1
-*/
-
-
-/*!
- \var uint8 BHttpRedirectOptions::maxRedirections
- \brief The maximum number of redirects that should be followed.
-
- \see These options are used by \ref BHttpRequest::Redirect() and
- \ref BHttpRequest::SetRedirect()
-
- \since Haiku R1
-*/
-
-
/*!
\struct BHttpAuthentication
\ingroup netservices
@@ -370,10 +336,22 @@ namespace Network {
Defaults to \ref BHttpMethod::Get |
- \ref Redirect() |
- \ref SetRedirect() |
- Whether redirects should be followed, and if so, how many |
- By default redirects are followed, up to 8 redirects for one request |
+ \ref MaxRedirections() |
+ \ref SetMaxRedirections() |
+ How many redirections should be followed. Set to 0 to disable. |
+ Defaults to 8 redirections per request |
+
+
+ \ref StopOnError() |
+ \ref SetStopOnError() |
+ Stop parsing the server response when there is a client or server error. |
+ Defaults to \a false |
+
+
+ \ref Timeout() |
+ \ref SetTimeout() |
+ The timeout determines how long is waited for the server to respond |
+ \c B_INFINITE_TIMEOUT |
@@ -540,10 +518,30 @@ namespace Network {
/*!
- \fn const BHttpRedirectOptions& BHttpRequest::Redirect() const noexcept
+ \fn uint8 BHttpRequest::MaxRedirections() const noexcept
\brief Get the current redirection options for this request.
- \see \ref BHttpRequest::SetRedirect() for details on the options.
+ \see \ref BHttpRequest::SetMaxRedirections() for details on the options.
+
+ \since Haiku R1
+*/
+
+
+/*!
+ \fn bool BHttpRequest::StopOnError() const noexcept
+ \brief Is the request set to parse the full response on error.
+
+ \retval true When encountering a HTTP status of the client error class (4xx) or server error
+ class (5xx), then the response will not be parsed.
+ \retval false The full response will be parsed, even with an error status code.
+
+ \since Haiku R1
+*/
+
+
+/*!
+ \fn bigtime_t BHttpRequest::Timeout() const noexcept
+ \brief Get the current timeout for the server to respond.
\since Haiku R1
*/
@@ -623,22 +621,57 @@ namespace Network {
/*!
- \fn void BHttpRequest::SetRedirect(const BHttpRedirectOptions &redirectOptions)
+ \fn void BHttpRequest::SetMaxRedirections(uint8 maxRedirections)
\brief Set the redirection options for this request.
The HTTP protocol allows the server to redirect requests if the resources have moved to a new
location. For your convenience, you can instruct the network services kit to follow these
- redirections.
+ redirections. You can set how many redirects should be followed. The maximum value is that of
+ an unsigned 8 bit int, so maximum is 256 redirects. This prevents the request from staying
+ stuck in a redirection loop.
- The \ref BHttpRedirectOptions allows you to set what the redirection policy should be. You can
- set whether redirects should be followed at all, and if so, how many redirects should be
- followed. The maximum value is that of an unsigned 8 bit int, so maximum is 256 redirects. This
- prevents the request from staying stuck in a redirection loop.
-
- If redirects are disabled, or the maximum number of redirects have been processed, then the
+ If redirects are set to 0, or the maximum number of redirects have been processed, then the
response will be set to the actual (last) received redirection response.
- \param redirectOptions The options for redirections.
+ \param maxRedirections The number of redirections to follow. Set to 0 to disable.
+
+ \exception std::bad_alloc This exception may be raised if it is impossible to allocate memory.
+
+ \since Haiku R1
+*/
+
+
+/*!
+ \fn void BHttpRequest::SetStopOnError(bool stopOnError)
+ \brief Set whether the entire response will be parsed on a client or server error.
+
+ When the server encounters an error processing a request, it may respond with an error code.
+ Error responses can be either an error with the request, like the 404 Not Found error, or
+ errors on the server side, like a 500 Internal Server Error. Error responses may still have
+ header fields and bodies.
+
+ If your application is not interested in the rest of the response in case a client error or
+ a server error occurs, you can set this option to stop parsing. This will allow you to use the
+ \ref BHttpResult object as normal, but the response fields will be empty, and there will be no
+ body data.
+
+ \param stopOnError Set to \c true to stop parsing the HTTP response when a client error or
+ server error occurs.
+
+ \exception std::bad_alloc This exception may be raised if it is impossible to allocate memory.
+
+ \since Haiku R1
+*/
+
+
+/*!
+ \fn void BHttpRequest::SetTimeout(bigtime_t timeout)
+ \brief Set the maximum time waiting for the server to respond.
+
+ If the request times out, then the response will hold the \ref BNetworkRequestError of the
+ \c NetworkError type. By default, the request does not time out.
+
+ \param timeout The timeout in milliseconds.
\exception std::bad_alloc This exception may be raised if it is impossible to allocate memory.
diff --git a/headers/private/netservices2/HttpRequest.h b/headers/private/netservices2/HttpRequest.h
index 95f55a6ff2..15b0517ad6 100644
--- a/headers/private/netservices2/HttpRequest.h
+++ b/headers/private/netservices2/HttpRequest.h
@@ -74,12 +74,6 @@ private:
};
-struct BHttpRedirectOptions {
- bool followRedirect = true;
- uint8 maxRedirections = 8;
-};
-
-
struct BHttpAuthentication {
BString username;
BString password;
@@ -103,15 +97,19 @@ public:
bool IsEmpty() const noexcept;
const BHttpAuthentication* Authentication() const noexcept;
const BHttpFields& Fields() const noexcept;
+ uint8 MaxRedirections() const noexcept;
const BHttpMethod& Method() const noexcept;
- const BHttpRedirectOptions& Redirect() const noexcept;
+ bool StopOnError() const noexcept;
+ bigtime_t Timeout() const noexcept;
const BUrl& Url() const noexcept;
// Named Setters
void SetAuthentication(const BHttpAuthentication& authentication);
void SetFields(const BHttpFields& fields);
+ void SetMaxRedirections(uint8 maxRedirections);
void SetMethod(const BHttpMethod& method);
- void SetRedirect(const BHttpRedirectOptions& redirectOptions);
+ void SetStopOnError(bool stopOnError);
+ void SetTimeout(bigtime_t timeout);
void SetUrl(const BUrl& url);
// Serialization
diff --git a/src/kits/network/libnetservices2/HttpRequest.cpp b/src/kits/network/libnetservices2/HttpRequest.cpp
index 8f3dd7ec05..016851b8f8 100644
--- a/src/kits/network/libnetservices2/HttpRequest.cpp
+++ b/src/kits/network/libnetservices2/HttpRequest.cpp
@@ -156,15 +156,16 @@ BHttpMethod::Method() const noexcept
// #pragma mark -- BHttpRequest::Data
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;
+ uint8 maxRedirections = 8;
BHttpFields optionalFields;
std::optional authentication;
+ bool stopOnError = false;
+ bigtime_t timeout = B_INFINITE_TIMEOUT;
};
@@ -236,6 +237,15 @@ BHttpRequest::Fields() const noexcept
}
+uint8
+BHttpRequest::MaxRedirections() const noexcept
+{
+ if (!fData)
+ return 8;
+ return fData->maxRedirections;
+}
+
+
const BHttpMethod&
BHttpRequest::Method() const noexcept
{
@@ -245,12 +255,21 @@ BHttpRequest::Method() const noexcept
}
-const BHttpRedirectOptions&
-BHttpRequest::Redirect() const noexcept
+bool
+BHttpRequest::StopOnError() const noexcept
{
if (!fData)
- return kDefaultRedirectOptions;
- return fData->redirectOptions;
+ return false;
+ return fData->stopOnError;
+}
+
+
+bigtime_t
+BHttpRequest::Timeout() const noexcept
+{
+ if (!fData)
+ return B_INFINITE_TIMEOUT;
+ return fData->timeout;
}
@@ -299,6 +318,15 @@ BHttpRequest::SetFields(const BHttpFields& fields)
}
+void
+BHttpRequest::SetMaxRedirections(uint8 maxRedirections)
+{
+ if (!fData)
+ fData = std::make_unique();
+ fData->maxRedirections = maxRedirections;
+}
+
+
void
BHttpRequest::SetMethod(const BHttpMethod& method)
{
@@ -309,11 +337,20 @@ BHttpRequest::SetMethod(const BHttpMethod& method)
void
-BHttpRequest::SetRedirect(const BHttpRedirectOptions& redirectOptions)
+BHttpRequest::SetStopOnError(bool stopOnError)
{
if (!fData)
fData = std::make_unique();
- fData->redirectOptions = redirectOptions;
+ fData->stopOnError = stopOnError;
+}
+
+
+void
+BHttpRequest::SetTimeout(bigtime_t timeout)
+{
+ if (!fData)
+ fData = std::make_unique();
+ fData->timeout = timeout;
}
diff --git a/src/kits/network/libnetservices2/HttpSession.cpp b/src/kits/network/libnetservices2/HttpSession.cpp
index bc88eab8fa..12ad6964f9 100644
--- a/src/kits/network/libnetservices2/HttpSession.cpp
+++ b/src/kits/network/libnetservices2/HttpSession.cpp
@@ -594,10 +594,7 @@ BHttpSession::Request::Request(BHttpRequest&& request, std::unique_ptr
auto identifier = get_netservices_request_identifier();
// interpret the remaining redirects
- if (fRequest.Redirect().followRedirect)
- fRemainingRedirects = fRequest.Redirect().maxRedirections;
- else
- fRemainingRedirects = 0;
+ fRemainingRedirects = fRequest.MaxRedirections();
// create shared data
fResult = std::make_shared(identifier);
@@ -662,6 +659,9 @@ BHttpSession::Request::OpenConnection()
fSocket = std::make_unique();
}
+ // Set timeout
+ fSocket->SetTimeout(fRequest.Timeout());
+
// Open connection
if (auto status = fSocket->Connect(fRemoteAddress); status != B_OK) {
// TODO: inform listeners that the connection failed
@@ -781,6 +781,17 @@ BHttpSession::Request::ReceiveResult()
// TODO: inform listeners of receiving the status code
}
+ if ((status.StatusClass() == BHttpStatusClass::ClientError
+ || status.StatusClass() == BHttpStatusClass::ServerError)
+ && fRequest.StopOnError())
+ {
+ fRequestStatus = ContentReceived;
+ fResult->SetStatus(std::move(status));
+ fResult->SetFields(BHttpFields());
+ fResult->SetBody();
+ return true;
+ }
+
// TODO: handle the case where we have an error code and we want to stop on error
fRequestStatus = StatusReceived;
diff --git a/src/tests/kits/net/netservices2/HttpProtocolTest.cpp b/src/tests/kits/net/netservices2/HttpProtocolTest.cpp
index 9c0795735d..1bb9632eec 100644
--- a/src/tests/kits/net/netservices2/HttpProtocolTest.cpp
+++ b/src/tests/kits/net/netservices2/HttpProtocolTest.cpp
@@ -495,6 +495,7 @@ HttpIntegrationTest::AddTests(BTestSuite& parent)
testCaller->addThread("NoContentTest", &HttpIntegrationTest::NoContentTest);
testCaller->addThread("AutoRedirectTest", &HttpIntegrationTest::AutoRedirectTest);
testCaller->addThread("BasicAuthTest", &HttpIntegrationTest::BasicAuthTest);
+ testCaller->addThread("StopOnErrorTest", &HttpIntegrationTest::StopOnErrorTest);
suite.addTest(testCaller);
parent.addTest("HttpIntegrationTest", &suite);
@@ -516,6 +517,7 @@ HttpIntegrationTest::AddTests(BTestSuite& parent)
testCaller->addThread("NoContentTest", &HttpIntegrationTest::NoContentTest);
testCaller->addThread("AutoRedirectTest", &HttpIntegrationTest::AutoRedirectTest);
testCaller->addThread("BasicAuthTest", &HttpIntegrationTest::BasicAuthTest);
+ testCaller->addThread("StopOnErrorTest", &HttpIntegrationTest::StopOnErrorTest);
suite.addTest(testCaller);
parent.addTest("HttpsIntegrationTest", &suite);
@@ -699,3 +701,16 @@ HttpIntegrationTest::BasicAuthTest()
result = fSession.Execute(std::move(request));
CPPUNIT_ASSERT(result.Status().code == 401);
}
+
+
+void
+HttpIntegrationTest::StopOnErrorTest()
+{
+ // Test the Stop on Error functionality
+ auto request = BHttpRequest(BUrl(fTestServer.BaseUrl(), "/400"));
+ request.SetStopOnError(true);
+ auto result = fSession.Execute(std::move(request));
+ CPPUNIT_ASSERT(result.Status().code == 400);
+ CPPUNIT_ASSERT(result.Fields().CountFields() == 0);
+ CPPUNIT_ASSERT(result.Body().text.Length() == 0);
+}
diff --git a/src/tests/kits/net/netservices2/HttpProtocolTest.h b/src/tests/kits/net/netservices2/HttpProtocolTest.h
index eaed90726b..41cb424138 100644
--- a/src/tests/kits/net/netservices2/HttpProtocolTest.h
+++ b/src/tests/kits/net/netservices2/HttpProtocolTest.h
@@ -44,6 +44,7 @@ public:
void NoContentTest();
void AutoRedirectTest();
void BasicAuthTest();
+ void StopOnErrorTest();
static void AddTests(BTestSuite& suite);