| // 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 |