NetServices: Add/rework the MaxRedirections, Timeout and StopOnError options
Change-Id: I4e59b51c16c6777941dc92ce02505386257dad03
This commit is contained in:
parent
f9d9d20245
commit
2f3b9b18ac
@ -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
|
\struct BHttpAuthentication
|
||||||
\ingroup netservices
|
\ingroup netservices
|
||||||
@ -370,10 +336,22 @@ namespace Network {
|
|||||||
<td> Defaults to \ref BHttpMethod::Get </td>
|
<td> Defaults to \ref BHttpMethod::Get </td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td> \ref Redirect() </td>
|
<td> \ref MaxRedirections() </td>
|
||||||
<td> \ref SetRedirect() </td>
|
<td> \ref SetMaxRedirections() </td>
|
||||||
<td> Whether redirects should be followed, and if so, how many </td>
|
<td> How many redirections should be followed. Set to 0 to disable. </td>
|
||||||
<td> By default redirects are followed, up to 8 redirects for one request </td>
|
<td> Defaults to 8 redirections per request </td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td> \ref StopOnError() </td>
|
||||||
|
<td> \ref SetStopOnError() </td>
|
||||||
|
<td> Stop parsing the server response when there is a client or server error. </td>
|
||||||
|
<td> Defaults to \a false </td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td> \ref Timeout() </td>
|
||||||
|
<td> \ref SetTimeout() </td>
|
||||||
|
<td> The timeout determines how long is waited for the server to respond </td>
|
||||||
|
<td> \c B_INFINITE_TIMEOUT </td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
@ -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.
|
\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
|
\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.
|
\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
|
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
|
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
|
If redirects are set to 0, or the maximum number of redirects have been processed, then the
|
||||||
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
|
|
||||||
response will be set to the actual (last) received redirection response.
|
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.
|
\exception std::bad_alloc This exception may be raised if it is impossible to allocate memory.
|
||||||
|
|
||||||
|
@ -74,12 +74,6 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct BHttpRedirectOptions {
|
|
||||||
bool followRedirect = true;
|
|
||||||
uint8 maxRedirections = 8;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct BHttpAuthentication {
|
struct BHttpAuthentication {
|
||||||
BString username;
|
BString username;
|
||||||
BString password;
|
BString password;
|
||||||
@ -103,15 +97,19 @@ public:
|
|||||||
bool IsEmpty() const noexcept;
|
bool IsEmpty() const noexcept;
|
||||||
const BHttpAuthentication* Authentication() const noexcept;
|
const BHttpAuthentication* Authentication() const noexcept;
|
||||||
const BHttpFields& Fields() const noexcept;
|
const BHttpFields& Fields() const noexcept;
|
||||||
|
uint8 MaxRedirections() const noexcept;
|
||||||
const BHttpMethod& Method() 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;
|
const BUrl& Url() const noexcept;
|
||||||
|
|
||||||
// Named Setters
|
// Named Setters
|
||||||
void SetAuthentication(const BHttpAuthentication& authentication);
|
void SetAuthentication(const BHttpAuthentication& authentication);
|
||||||
void SetFields(const BHttpFields& fields);
|
void SetFields(const BHttpFields& fields);
|
||||||
|
void SetMaxRedirections(uint8 maxRedirections);
|
||||||
void SetMethod(const BHttpMethod& method);
|
void SetMethod(const BHttpMethod& method);
|
||||||
void SetRedirect(const BHttpRedirectOptions& redirectOptions);
|
void SetStopOnError(bool stopOnError);
|
||||||
|
void SetTimeout(bigtime_t timeout);
|
||||||
void SetUrl(const BUrl& url);
|
void SetUrl(const BUrl& url);
|
||||||
|
|
||||||
// Serialization
|
// Serialization
|
||||||
|
@ -156,15 +156,16 @@ BHttpMethod::Method() const noexcept
|
|||||||
// #pragma mark -- BHttpRequest::Data
|
// #pragma mark -- BHttpRequest::Data
|
||||||
static const BUrl kDefaultUrl = BUrl();
|
static const BUrl kDefaultUrl = BUrl();
|
||||||
static const BHttpMethod kDefaultMethod = BHttpMethod::Get;
|
static const BHttpMethod kDefaultMethod = BHttpMethod::Get;
|
||||||
static const BHttpRedirectOptions kDefaultRedirectOptions = BHttpRedirectOptions();
|
|
||||||
static const BHttpFields kDefaultOptionalFields = BHttpFields();
|
static const BHttpFields kDefaultOptionalFields = BHttpFields();
|
||||||
|
|
||||||
struct BHttpRequest::Data {
|
struct BHttpRequest::Data {
|
||||||
BUrl url = kDefaultUrl;
|
BUrl url = kDefaultUrl;
|
||||||
BHttpMethod method = kDefaultMethod;
|
BHttpMethod method = kDefaultMethod;
|
||||||
BHttpRedirectOptions redirectOptions;
|
uint8 maxRedirections = 8;
|
||||||
BHttpFields optionalFields;
|
BHttpFields optionalFields;
|
||||||
std::optional<BHttpAuthentication> authentication;
|
std::optional<BHttpAuthentication> 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&
|
const BHttpMethod&
|
||||||
BHttpRequest::Method() const noexcept
|
BHttpRequest::Method() const noexcept
|
||||||
{
|
{
|
||||||
@ -245,12 +255,21 @@ BHttpRequest::Method() const noexcept
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const BHttpRedirectOptions&
|
bool
|
||||||
BHttpRequest::Redirect() const noexcept
|
BHttpRequest::StopOnError() const noexcept
|
||||||
{
|
{
|
||||||
if (!fData)
|
if (!fData)
|
||||||
return kDefaultRedirectOptions;
|
return false;
|
||||||
return fData->redirectOptions;
|
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<Data>();
|
||||||
|
fData->maxRedirections = maxRedirections;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
BHttpRequest::SetMethod(const BHttpMethod& method)
|
BHttpRequest::SetMethod(const BHttpMethod& method)
|
||||||
{
|
{
|
||||||
@ -309,11 +337,20 @@ BHttpRequest::SetMethod(const BHttpMethod& method)
|
|||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
BHttpRequest::SetRedirect(const BHttpRedirectOptions& redirectOptions)
|
BHttpRequest::SetStopOnError(bool stopOnError)
|
||||||
{
|
{
|
||||||
if (!fData)
|
if (!fData)
|
||||||
fData = std::make_unique<Data>();
|
fData = std::make_unique<Data>();
|
||||||
fData->redirectOptions = redirectOptions;
|
fData->stopOnError = stopOnError;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
BHttpRequest::SetTimeout(bigtime_t timeout)
|
||||||
|
{
|
||||||
|
if (!fData)
|
||||||
|
fData = std::make_unique<Data>();
|
||||||
|
fData->timeout = timeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -594,10 +594,7 @@ BHttpSession::Request::Request(BHttpRequest&& request, std::unique_ptr<BDataIO>
|
|||||||
auto identifier = get_netservices_request_identifier();
|
auto identifier = get_netservices_request_identifier();
|
||||||
|
|
||||||
// interpret the remaining redirects
|
// interpret the remaining redirects
|
||||||
if (fRequest.Redirect().followRedirect)
|
fRemainingRedirects = fRequest.MaxRedirections();
|
||||||
fRemainingRedirects = fRequest.Redirect().maxRedirections;
|
|
||||||
else
|
|
||||||
fRemainingRedirects = 0;
|
|
||||||
|
|
||||||
// create shared data
|
// create shared data
|
||||||
fResult = std::make_shared<HttpResultPrivate>(identifier);
|
fResult = std::make_shared<HttpResultPrivate>(identifier);
|
||||||
@ -662,6 +659,9 @@ BHttpSession::Request::OpenConnection()
|
|||||||
fSocket = std::make_unique<BSocket>();
|
fSocket = std::make_unique<BSocket>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set timeout
|
||||||
|
fSocket->SetTimeout(fRequest.Timeout());
|
||||||
|
|
||||||
// Open connection
|
// Open connection
|
||||||
if (auto status = fSocket->Connect(fRemoteAddress); status != B_OK) {
|
if (auto status = fSocket->Connect(fRemoteAddress); status != B_OK) {
|
||||||
// TODO: inform listeners that the connection failed
|
// TODO: inform listeners that the connection failed
|
||||||
@ -781,6 +781,17 @@ BHttpSession::Request::ReceiveResult()
|
|||||||
// TODO: inform listeners of receiving the status code
|
// 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
|
// TODO: handle the case where we have an error code and we want to stop on error
|
||||||
|
|
||||||
fRequestStatus = StatusReceived;
|
fRequestStatus = StatusReceived;
|
||||||
|
@ -495,6 +495,7 @@ HttpIntegrationTest::AddTests(BTestSuite& parent)
|
|||||||
testCaller->addThread("NoContentTest", &HttpIntegrationTest::NoContentTest);
|
testCaller->addThread("NoContentTest", &HttpIntegrationTest::NoContentTest);
|
||||||
testCaller->addThread("AutoRedirectTest", &HttpIntegrationTest::AutoRedirectTest);
|
testCaller->addThread("AutoRedirectTest", &HttpIntegrationTest::AutoRedirectTest);
|
||||||
testCaller->addThread("BasicAuthTest", &HttpIntegrationTest::BasicAuthTest);
|
testCaller->addThread("BasicAuthTest", &HttpIntegrationTest::BasicAuthTest);
|
||||||
|
testCaller->addThread("StopOnErrorTest", &HttpIntegrationTest::StopOnErrorTest);
|
||||||
|
|
||||||
suite.addTest(testCaller);
|
suite.addTest(testCaller);
|
||||||
parent.addTest("HttpIntegrationTest", &suite);
|
parent.addTest("HttpIntegrationTest", &suite);
|
||||||
@ -516,6 +517,7 @@ HttpIntegrationTest::AddTests(BTestSuite& parent)
|
|||||||
testCaller->addThread("NoContentTest", &HttpIntegrationTest::NoContentTest);
|
testCaller->addThread("NoContentTest", &HttpIntegrationTest::NoContentTest);
|
||||||
testCaller->addThread("AutoRedirectTest", &HttpIntegrationTest::AutoRedirectTest);
|
testCaller->addThread("AutoRedirectTest", &HttpIntegrationTest::AutoRedirectTest);
|
||||||
testCaller->addThread("BasicAuthTest", &HttpIntegrationTest::BasicAuthTest);
|
testCaller->addThread("BasicAuthTest", &HttpIntegrationTest::BasicAuthTest);
|
||||||
|
testCaller->addThread("StopOnErrorTest", &HttpIntegrationTest::StopOnErrorTest);
|
||||||
|
|
||||||
suite.addTest(testCaller);
|
suite.addTest(testCaller);
|
||||||
parent.addTest("HttpsIntegrationTest", &suite);
|
parent.addTest("HttpsIntegrationTest", &suite);
|
||||||
@ -699,3 +701,16 @@ HttpIntegrationTest::BasicAuthTest()
|
|||||||
result = fSession.Execute(std::move(request));
|
result = fSession.Execute(std::move(request));
|
||||||
CPPUNIT_ASSERT(result.Status().code == 401);
|
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);
|
||||||
|
}
|
||||||
|
@ -44,6 +44,7 @@ public:
|
|||||||
void NoContentTest();
|
void NoContentTest();
|
||||||
void AutoRedirectTest();
|
void AutoRedirectTest();
|
||||||
void BasicAuthTest();
|
void BasicAuthTest();
|
||||||
|
void StopOnErrorTest();
|
||||||
|
|
||||||
static void AddTests(BTestSuite& suite);
|
static void AddTests(BTestSuite& suite);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user