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);