NetServices: Implement BHttpSession::Cancel()

Change-Id: Iff0a7726e57f3e6bd4e9d0ebac08a370d25a62d7
This commit is contained in:
Niels Sascha Reedijk 2022-06-03 13:37:26 +01:00
parent 2f3b9b18ac
commit 6cbbd9bf4e
5 changed files with 100 additions and 0 deletions

View File

@ -183,6 +183,42 @@ namespace Network {
*/
/*!
\fn void BHttpSession::Cancel(int32 identifier)
\brief Cancel a running request.
When a request that is scheduled or running is cancelled, its \ref BHttpResult object will
be set to the \ref BNetworkRequestError exception with the \c Cancelled type.
There are three possible outcomes:
1. If the request is not yet scheduled, then it will never start. The result will be set to
the cancelled error state.
2. If the request was started, then processing it will be terminated. The result will be set to
the cancelled error state.
3. If the request was already finished, then nothing happens. The result will keep the value it
had assigned. The same if the request identifier is invalid.
\param identifier The identifier for the request to cancel.
\exception std::bad_alloc Error in case memory cannot be allocated.
\since Haiku R1
*/
/*!
\fn void BHttpSession::Cancel(const BHttpResult& request)
\brief Cancel a running \a request.
See the \ref BHttpSession::Cancel(int32 identifier) method for more details on how this method
works.
\exception std::bad_alloc Error in case memory cannot be allocated.
\since Haiku R1
*/
} // namespace Network
} // namespace BPrivate

View File

@ -37,6 +37,8 @@ public:
BHttpResult Execute(BHttpRequest&& request,
std::unique_ptr<BDataIO> target = nullptr,
BMessenger observer = BMessenger());
void Cancel(int32 identifier);
void Cancel(const BHttpResult& request);
private:
struct Redirect;

View File

@ -151,6 +151,7 @@ public:
BHttpResult Execute(BHttpRequest&& request,
std::unique_ptr<BDataIO> target,
BMessenger observer);
void Cancel(int32 identifier);
private:
// Thread functions
@ -239,6 +240,30 @@ BHttpSession::Impl::Execute(BHttpRequest&& request, std::unique_ptr<BDataIO> tar
}
#include <iostream>
void
BHttpSession::Impl::Cancel(int32 identifier)
{
std::cout << "BHttpSession::Impl::Cancel for " << identifier << std::endl;
auto lock = AutoLocker<BLocker>(fLock);
// Check if the item is on the control queue
auto item = std::find_if(fControlQueue.begin(), fControlQueue.end(),
[&identifier](const auto& arg){ return arg.Id() == identifier; });
if (item != fControlQueue.end()) {
try {
throw BNetworkRequestError(__PRETTY_FUNCTION__, BNetworkRequestError::Canceled);
} catch (...) {
item->SetError(std::current_exception());
}
fControlQueue.erase(item);
return;
}
// Get it on the list for deletion in the data queue
fCancelList.push_back(identifier);
release_sem(fDataQueueSem);
}
/*static*/ status_t
BHttpSession::Impl::ControlThreadFunc(void* arg)
{
@ -389,6 +414,7 @@ BHttpSession::Impl::DataThreadFunc(void* arg)
for (auto it = data->connectionMap.cbegin(); it != data->connectionMap.cend(); it++) {
offset++;
if (it->second.Id() == id) {
std::cout << "DataThreadFunc() [" << id << "] cancelling request" << std::endl;
data->objectList[offset].events = EVENT_CANCELLED;
break;
}
@ -585,6 +611,20 @@ BHttpSession::Execute(BHttpRequest&& request, std::unique_ptr<BDataIO> target, B
}
void
BHttpSession::Cancel(int32 identifier)
{
fImpl->Cancel(identifier);
}
void
BHttpSession::Cancel(const BHttpResult& request)
{
fImpl->Cancel(request.Identity());
}
// #pragma mark -- BHttpSession::Request (helpers)
BHttpSession::Request::Request(BHttpRequest&& request, std::unique_ptr<BDataIO> target,

View File

@ -496,6 +496,7 @@ HttpIntegrationTest::AddTests(BTestSuite& parent)
testCaller->addThread("AutoRedirectTest", &HttpIntegrationTest::AutoRedirectTest);
testCaller->addThread("BasicAuthTest", &HttpIntegrationTest::BasicAuthTest);
testCaller->addThread("StopOnErrorTest", &HttpIntegrationTest::StopOnErrorTest);
testCaller->addThread("RequestCancelTest", &HttpIntegrationTest::RequestCancelTest);
suite.addTest(testCaller);
parent.addTest("HttpIntegrationTest", &suite);
@ -518,6 +519,7 @@ HttpIntegrationTest::AddTests(BTestSuite& parent)
testCaller->addThread("AutoRedirectTest", &HttpIntegrationTest::AutoRedirectTest);
testCaller->addThread("BasicAuthTest", &HttpIntegrationTest::BasicAuthTest);
testCaller->addThread("StopOnErrorTest", &HttpIntegrationTest::StopOnErrorTest);
testCaller->addThread("RequestCancelTest", &HttpIntegrationTest::RequestCancelTest);
suite.addTest(testCaller);
parent.addTest("HttpsIntegrationTest", &suite);
@ -714,3 +716,22 @@ HttpIntegrationTest::StopOnErrorTest()
CPPUNIT_ASSERT(result.Fields().CountFields() == 0);
CPPUNIT_ASSERT(result.Body().text.Length() == 0);
}
void
HttpIntegrationTest::RequestCancelTest()
{
// Test the cancellation functionality
// TODO: this test potentially fails if the case is executed before the cancellation is
// processed. In practise, the cancellation always comes first. When the server
// supports a wait parameter, then this test can be made more robust.
auto request = BHttpRequest(BUrl(fTestServer.BaseUrl(), "/"));
auto result = fSession.Execute(std::move(request));
fSession.Cancel(result);
try {
result.Body();
CPPUNIT_FAIL("Expected exception because request was cancelled");
} catch (const BNetworkRequestError& e) {
CPPUNIT_ASSERT(e.Type() == BNetworkRequestError::Canceled);
}
}

View File

@ -45,6 +45,7 @@ public:
void AutoRedirectTest();
void BasicAuthTest();
void StopOnErrorTest();
void RequestCancelTest();
static void AddTests(BTestSuite& suite);