| // Copyright 2017 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 "net/test/spawned_test_server/remote_test_server_spawner_request.h" |
| |
| #include <utility> |
| |
| #include "base/location.h" |
| #include "base/logging.h" |
| #include "base/macros.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/synchronization/waitable_event.h" |
| #include "base/test/test_timeouts.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "base/time/time.h" |
| #include "base/timer/timer.h" |
| #include "build/build_config.h" |
| #include "net/base/elements_upload_data_stream.h" |
| #include "net/base/io_buffer.h" |
| #include "net/base/port_util.h" |
| #include "net/base/upload_bytes_element_reader.h" |
| #include "net/http/http_response_headers.h" |
| #include "net/url_request/url_request.h" |
| #include "net/url_request/url_request_test_util.h" |
| #include "url/gurl.h" |
| |
| namespace net { |
| |
| static const int kBufferSize = 2048; |
| |
| class RemoteTestServerSpawnerRequest::Core : public URLRequest::Delegate { |
| public: |
| Core(); |
| ~Core() override; |
| |
| void SendRequest(const GURL& url, const std::string& post_data); |
| |
| // Blocks until request is finished. If |response| isn't nullptr then server |
| // response is copied to *response. Returns true if the request was completed |
| // successfully. |
| bool WaitForCompletion(std::string* response) WARN_UNUSED_RESULT; |
| |
| private: |
| // URLRequest::Delegate methods. |
| void OnResponseStarted(URLRequest* request, int net_error) override; |
| void OnReadCompleted(URLRequest* request, int num_bytes) override; |
| |
| void ReadResponse(); |
| void OnCommandCompleted(int net_error); |
| void OnTimeout(); |
| |
| // Request results. |
| int result_code_ = 0; |
| std::string data_received_; |
| |
| // WaitableEvent to notify when the request is finished. |
| base::WaitableEvent event_; |
| |
| std::unique_ptr<URLRequestContext> context_; |
| std::unique_ptr<URLRequest> request_; |
| |
| scoped_refptr<IOBuffer> read_buffer_; |
| |
| std::unique_ptr<base::OneShotTimer> timeout_timer_; |
| |
| THREAD_CHECKER(thread_checker_); |
| |
| DISALLOW_COPY_AND_ASSIGN(Core); |
| }; |
| |
| RemoteTestServerSpawnerRequest::Core::Core() |
| : event_(base::WaitableEvent::ResetPolicy::AUTOMATIC, |
| base::WaitableEvent::InitialState::NOT_SIGNALED), |
| read_buffer_(base::MakeRefCounted<IOBuffer>(kBufferSize)) { |
| DETACH_FROM_THREAD(thread_checker_); |
| } |
| |
| void RemoteTestServerSpawnerRequest::Core::SendRequest( |
| const GURL& url, |
| const std::string& post_data) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| |
| // Prepare the URLRequest for sending the command. |
| DCHECK(!request_.get()); |
| context_.reset(new TestURLRequestContext); |
| request_ = context_->CreateRequest(url, DEFAULT_PRIORITY, this); |
| |
| if (post_data.empty()) { |
| request_->set_method("GET"); |
| } else { |
| request_->set_method("POST"); |
| std::unique_ptr<UploadElementReader> reader( |
| UploadOwnedBytesElementReader::CreateWithString(post_data)); |
| request_->set_upload( |
| ElementsUploadDataStream::CreateWithReader(std::move(reader), 0)); |
| request_->SetExtraRequestHeaderByName(HttpRequestHeaders::kContentType, |
| "application/json", |
| /*override=*/true); |
| } |
| |
| timeout_timer_ = std::make_unique<base::OneShotTimer>(); |
| timeout_timer_->Start(FROM_HERE, TestTimeouts::action_max_timeout(), |
| base::Bind(&Core::OnTimeout, base::Unretained(this))); |
| |
| request_->Start(); |
| } |
| |
| RemoteTestServerSpawnerRequest::Core::~Core() { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| } |
| |
| bool RemoteTestServerSpawnerRequest::Core::WaitForCompletion( |
| std::string* response) { |
| // Called by RemoteTestServerSpawnerRequest::WaitForCompletion() on the main |
| // thread. |
| |
| event_.Wait(); |
| if (response) |
| *response = data_received_; |
| return result_code_ == OK; |
| } |
| |
| void RemoteTestServerSpawnerRequest::Core::OnTimeout() { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| |
| int result = request_->CancelWithError(ERR_TIMED_OUT); |
| OnCommandCompleted(result); |
| } |
| |
| void RemoteTestServerSpawnerRequest::Core::OnCommandCompleted(int net_error) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| DCHECK_NE(ERR_IO_PENDING, net_error); |
| DCHECK(!event_.IsSignaled()); |
| |
| // If request has failed, return the error code. |
| if (net_error != OK) { |
| LOG(ERROR) << "request failed, error: " << ErrorToString(net_error); |
| result_code_ = net_error; |
| } else if (request_->GetResponseCode() != 200) { |
| LOG(ERROR) << "Spawner server returned bad status: " |
| << request_->response_headers()->GetStatusLine() << ", " |
| << data_received_; |
| result_code_ = ERR_FAILED; |
| } |
| |
| if (result_code_ != OK) |
| data_received_.clear(); |
| |
| request_.reset(); |
| context_.reset(); |
| timeout_timer_.reset(); |
| |
| event_.Signal(); |
| } |
| |
| void RemoteTestServerSpawnerRequest::Core::ReadResponse() { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| |
| while (true) { |
| int result = request_->Read(read_buffer_.get(), kBufferSize); |
| if (result == ERR_IO_PENDING) |
| return; |
| |
| if (result <= 0) { |
| OnCommandCompleted(result); |
| return; |
| } |
| |
| data_received_.append(read_buffer_->data(), result); |
| } |
| } |
| |
| void RemoteTestServerSpawnerRequest::Core::OnResponseStarted( |
| URLRequest* request, |
| int net_error) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| DCHECK_NE(ERR_IO_PENDING, net_error); |
| DCHECK_EQ(request, request_.get()); |
| |
| if (net_error != OK) { |
| OnCommandCompleted(net_error); |
| return; |
| } |
| |
| ReadResponse(); |
| } |
| |
| void RemoteTestServerSpawnerRequest::Core::OnReadCompleted(URLRequest* request, |
| int read_result) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| DCHECK_NE(ERR_IO_PENDING, read_result); |
| DCHECK_EQ(request, request_.get()); |
| |
| if (read_result <= 0) { |
| OnCommandCompleted(read_result); |
| return; |
| } |
| |
| data_received_.append(read_buffer_->data(), read_result); |
| |
| ReadResponse(); |
| } |
| |
| RemoteTestServerSpawnerRequest::RemoteTestServerSpawnerRequest( |
| scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, |
| const GURL& url, |
| const std::string& post_data) |
| : io_task_runner_(io_task_runner), |
| core_(new Core()), |
| allowed_port_(new ScopedPortException(url.EffectiveIntPort())) { |
| io_task_runner_->PostTask( |
| FROM_HERE, base::BindOnce(&Core::SendRequest, |
| base::Unretained(core_.get()), url, post_data)); |
| } |
| |
| RemoteTestServerSpawnerRequest::~RemoteTestServerSpawnerRequest() { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| io_task_runner_->DeleteSoon(FROM_HERE, core_.release()); |
| } |
| |
| bool RemoteTestServerSpawnerRequest::WaitForCompletion(std::string* response) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| return core_->WaitForCompletion(response); |
| } |
| |
| } // namespace net |