blob: e770e8184da19dffae06a6c11ccd698636d7b5fa [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/cronet/native/test/test_url_request_callback.h"
#include "base/bind.h"
#include "components/cronet/native/test/test_util.h"
namespace cronet {
namespace test {
TestUrlRequestCallback::UrlResponseInfo::UrlResponseInfo(
Cronet_UrlResponseInfoPtr response_info)
: url(Cronet_UrlResponseInfo_url_get(response_info)),
http_status_code(
Cronet_UrlResponseInfo_http_status_code_get(response_info)),
http_status_text(
Cronet_UrlResponseInfo_http_status_text_get(response_info)),
was_cached(Cronet_UrlResponseInfo_was_cached_get(response_info)),
negotiated_protocol(
Cronet_UrlResponseInfo_negotiated_protocol_get(response_info)),
proxy_server(Cronet_UrlResponseInfo_proxy_server_get(response_info)),
received_byte_count(
Cronet_UrlResponseInfo_received_byte_count_get(response_info)) {
for (uint32_t url = 0;
url < Cronet_UrlResponseInfo_url_chain_size(response_info); ++url) {
url_chain.push_back(
Cronet_UrlResponseInfo_url_chain_at(response_info, url));
}
for (uint32_t i = 0;
i < Cronet_UrlResponseInfo_all_headers_list_size(response_info); ++i) {
Cronet_HttpHeaderPtr header =
Cronet_UrlResponseInfo_all_headers_list_at(response_info, i);
all_headers.push_back(std::pair<std::string, std::string>(
Cronet_HttpHeader_name_get(header),
Cronet_HttpHeader_value_get(header)));
}
}
TestUrlRequestCallback::UrlResponseInfo::UrlResponseInfo(
const std::vector<std::string>& urls,
const std::string& message,
int32_t status_code,
int64_t received_bytes,
std::vector<std::string> headers)
: url(urls.back()),
url_chain(urls),
http_status_code(status_code),
http_status_text(message),
negotiated_protocol("unknown"),
proxy_server(":0"),
received_byte_count(received_bytes) {
for (uint32_t i = 0; i < headers.size(); i += 2) {
all_headers.push_back(
std::pair<std::string, std::string>(headers[i], headers[i + 1]));
}
}
TestUrlRequestCallback::UrlResponseInfo::~UrlResponseInfo() = default;
TestUrlRequestCallback::TestUrlRequestCallback(bool direct_executor)
: direct_executor_(direct_executor),
done_(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED),
step_block_(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED) {}
TestUrlRequestCallback::~TestUrlRequestCallback() {
ShutdownExecutor();
}
Cronet_ExecutorPtr TestUrlRequestCallback::GetExecutor() {
if (executor_)
return executor_;
if (direct_executor_) {
executor_ =
Cronet_Executor_CreateWith(TestUrlRequestCallback::ExecuteDirect);
} else {
executor_thread_ =
std::make_unique<base::Thread>("TestUrlRequestCallback executor");
executor_thread_->Start();
executor_ = Cronet_Executor_CreateWith(TestUrlRequestCallback::Execute);
Cronet_Executor_SetClientContext(executor_, this);
}
return executor_;
}
Cronet_UrlRequestCallbackPtr
TestUrlRequestCallback::CreateUrlRequestCallback() {
Cronet_UrlRequestCallbackPtr callback = Cronet_UrlRequestCallback_CreateWith(
TestUrlRequestCallback::OnRedirectReceived,
TestUrlRequestCallback::OnResponseStarted,
TestUrlRequestCallback::OnReadCompleted,
TestUrlRequestCallback::OnSucceeded, TestUrlRequestCallback::OnFailed,
TestUrlRequestCallback::OnCanceled);
Cronet_UrlRequestCallback_SetClientContext(callback, this);
return callback;
}
void TestUrlRequestCallback::OnRedirectReceived(Cronet_UrlRequestPtr request,
Cronet_UrlResponseInfoPtr info,
Cronet_String newLocationUrl) {
CheckExecutorThread();
CHECK(!Cronet_UrlRequest_IsDone(request));
CHECK(response_step_ == NOTHING || response_step_ == ON_RECEIVED_REDIRECT);
CHECK(!last_error_);
response_step_ = ON_RECEIVED_REDIRECT;
redirect_url_list_.push_back(newLocationUrl);
redirect_response_info_list_.push_back(
std::make_unique<UrlResponseInfo>(info));
++redirect_count_;
if (MaybeCancelOrPause(request)) {
return;
}
Cronet_UrlRequest_FollowRedirect(request);
}
void TestUrlRequestCallback::OnResponseStarted(Cronet_UrlRequestPtr request,
Cronet_UrlResponseInfoPtr info) {
CheckExecutorThread();
CHECK(!Cronet_UrlRequest_IsDone(request));
CHECK(response_step_ == NOTHING || response_step_ == ON_RECEIVED_REDIRECT);
CHECK(!last_error_);
response_step_ = ON_RESPONSE_STARTED;
response_info_ = std::make_unique<UrlResponseInfo>(info);
if (MaybeCancelOrPause(request)) {
return;
}
StartNextRead(request);
}
void TestUrlRequestCallback::OnReadCompleted(Cronet_UrlRequestPtr request,
Cronet_UrlResponseInfoPtr info,
Cronet_BufferPtr buffer,
uint64_t bytes_read) {
CheckExecutorThread();
CHECK(!Cronet_UrlRequest_IsDone(request));
CHECK(response_step_ == ON_RESPONSE_STARTED ||
response_step_ == ON_READ_COMPLETED);
CHECK(!last_error_);
response_step_ = ON_READ_COMPLETED;
response_info_ = std::make_unique<UrlResponseInfo>(info);
response_data_length_ += bytes_read;
if (accumulate_response_data_) {
std::string last_read_data(
reinterpret_cast<char*>(Cronet_Buffer_GetData(buffer)), bytes_read);
response_as_string_ += last_read_data;
}
if (MaybeCancelOrPause(request)) {
Cronet_Buffer_Destroy(buffer);
return;
}
StartNextRead(request, buffer);
}
void TestUrlRequestCallback::OnSucceeded(Cronet_UrlRequestPtr request,
Cronet_UrlResponseInfoPtr info) {
CheckExecutorThread();
CHECK(Cronet_UrlRequest_IsDone(request));
CHECK(response_step_ == ON_RESPONSE_STARTED ||
response_step_ == ON_READ_COMPLETED);
CHECK(!on_error_called_);
CHECK(!on_canceled_called_);
CHECK(!last_error_);
response_step_ = ON_SUCCEEDED;
response_info_ = std::make_unique<UrlResponseInfo>(info);
MaybeCancelOrPause(request);
SignalDone();
}
void TestUrlRequestCallback::OnFailed(Cronet_UrlRequestPtr request,
Cronet_UrlResponseInfoPtr info,
Cronet_ErrorPtr error) {
CheckExecutorThread();
CHECK(Cronet_UrlRequest_IsDone(request));
// Shouldn't happen after success.
CHECK(response_step_ != ON_SUCCEEDED);
// Should happen at most once for a single request.
CHECK(!on_error_called_);
CHECK(!on_canceled_called_);
CHECK(!last_error_);
response_step_ = ON_FAILED;
on_error_called_ = true;
// It is possible that |info| is nullptr if response has not started.
if (info)
response_info_ = std::make_unique<UrlResponseInfo>(info);
last_error_ = error;
last_error_code_ = Cronet_Error_error_code_get(error);
last_error_message_ = Cronet_Error_message_get(error);
MaybeCancelOrPause(request);
SignalDone();
}
void TestUrlRequestCallback::OnCanceled(Cronet_UrlRequestPtr request,
Cronet_UrlResponseInfoPtr info) {
CheckExecutorThread();
CHECK(Cronet_UrlRequest_IsDone(request));
CHECK(!on_error_called_);
// Should happen at most once for a single request.
CHECK(!on_canceled_called_);
CHECK(!last_error_);
response_step_ = ON_CANCELED;
on_canceled_called_ = true;
MaybeCancelOrPause(request);
SignalDone();
}
void TestUrlRequestCallback::ShutdownExecutor() {
base::AutoLock lock(executor_lock_);
if (executor_ == nullptr)
return;
Cronet_Executor_Destroy(executor_);
executor_ = nullptr;
// Stop executor thread outside of lock to allow runnables to complete.
auto executor_thread(std::move(executor_thread_));
executor_lock_.Release();
executor_thread.reset();
executor_lock_.Acquire();
}
void TestUrlRequestCallback::CheckExecutorThread() {
base::AutoLock lock(executor_lock_);
if (executor_thread_ && !direct_executor_)
CHECK(executor_thread_->task_runner()->BelongsToCurrentThread());
}
bool TestUrlRequestCallback::MaybeCancelOrPause(Cronet_UrlRequestPtr request) {
CheckExecutorThread();
if (response_step_ != failure_step_ || failure_type_ == NONE) {
if (!auto_advance_) {
step_block_.Signal();
return true;
}
return false;
}
if (failure_type_ == CANCEL_SYNC) {
Cronet_UrlRequest_Cancel(request);
}
if (failure_type_ == CANCEL_ASYNC ||
failure_type_ == CANCEL_ASYNC_WITHOUT_PAUSE) {
if (direct_executor_) {
Cronet_UrlRequest_Cancel(request);
} else {
base::AutoLock lock(executor_lock_);
CHECK(executor_thread_);
executor_thread_->task_runner()->PostTask(
FROM_HERE, base::BindOnce(&Cronet_UrlRequest_Cancel, request));
}
}
return failure_type_ != CANCEL_ASYNC_WITHOUT_PAUSE;
}
/* static */
TestUrlRequestCallback* TestUrlRequestCallback::GetThis(
Cronet_UrlRequestCallbackPtr self) {
return static_cast<TestUrlRequestCallback*>(
Cronet_UrlRequestCallback_GetClientContext(self));
}
/* static */
void TestUrlRequestCallback::OnRedirectReceived(
Cronet_UrlRequestCallbackPtr self,
Cronet_UrlRequestPtr request,
Cronet_UrlResponseInfoPtr info,
Cronet_String newLocationUrl) {
GetThis(self)->OnRedirectReceived(request, info, newLocationUrl);
}
/* static */
void TestUrlRequestCallback::OnResponseStarted(
Cronet_UrlRequestCallbackPtr self,
Cronet_UrlRequestPtr request,
Cronet_UrlResponseInfoPtr info) {
GetThis(self)->OnResponseStarted(request, info);
}
/* static */
void TestUrlRequestCallback::OnReadCompleted(Cronet_UrlRequestCallbackPtr self,
Cronet_UrlRequestPtr request,
Cronet_UrlResponseInfoPtr info,
Cronet_BufferPtr buffer,
uint64_t bytesRead) {
GetThis(self)->OnReadCompleted(request, info, buffer, bytesRead);
}
/* static */
void TestUrlRequestCallback::OnSucceeded(Cronet_UrlRequestCallbackPtr self,
Cronet_UrlRequestPtr request,
Cronet_UrlResponseInfoPtr info) {
GetThis(self)->OnSucceeded(request, info);
}
/* static */
void TestUrlRequestCallback::OnFailed(Cronet_UrlRequestCallbackPtr self,
Cronet_UrlRequestPtr request,
Cronet_UrlResponseInfoPtr info,
Cronet_ErrorPtr error) {
GetThis(self)->OnFailed(request, info, error);
}
/* static */
void TestUrlRequestCallback::OnCanceled(Cronet_UrlRequestCallbackPtr self,
Cronet_UrlRequestPtr request,
Cronet_UrlResponseInfoPtr info) {
GetThis(self)->OnCanceled(request, info);
}
/* static */
void TestUrlRequestCallback::Execute(Cronet_ExecutorPtr self,
Cronet_RunnablePtr runnable) {
CHECK(self);
auto* callback = static_cast<TestUrlRequestCallback*>(
Cronet_Executor_GetClientContext(self));
CHECK(callback);
base::AutoLock lock(callback->executor_lock_);
CHECK(callback->executor_thread_);
// Post |runnable| onto executor thread.
callback->executor_thread_->task_runner()->PostTask(
FROM_HERE, RunnableWrapper::CreateOnceClosure(runnable));
}
/* static */
void TestUrlRequestCallback::ExecuteDirect(Cronet_ExecutorPtr self,
Cronet_RunnablePtr runnable) {
// Run |runnable| directly.
Cronet_Runnable_Run(runnable);
Cronet_Runnable_Destroy(runnable);
}
} // namespace test
} // namespace cronet