1 // Copyright 2017 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/test/spawned_test_server/remote_test_server_spawner_request.h"
6
7 #include <memory>
8 #include <utility>
9
10 #include "base/functional/bind.h"
11 #include "base/location.h"
12 #include "base/logging.h"
13 #include "base/synchronization/waitable_event.h"
14 #include "base/task/single_thread_task_runner.h"
15 #include "build/build_config.h"
16 #include "net/base/elements_upload_data_stream.h"
17 #include "net/base/io_buffer.h"
18 #include "net/base/port_util.h"
19 #include "net/base/upload_bytes_element_reader.h"
20 #include "net/http/http_response_headers.h"
21 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
22 #include "net/url_request/url_request.h"
23 #include "net/url_request/url_request_context.h"
24 #include "net/url_request/url_request_context_builder.h"
25 #include "net/url_request/url_request_test_util.h"
26 #include "url/gurl.h"
27
28 namespace net {
29
30 static const int kBufferSize = 2048;
31
32 class RemoteTestServerSpawnerRequest::Core : public URLRequest::Delegate {
33 public:
34 Core();
35
36 Core(const Core&) = delete;
37 Core& operator=(const Core&) = delete;
38
39 ~Core() override;
40
41 void SendRequest(const GURL& url, const std::string& post_data);
42
43 // Blocks until request is finished. If |response| isn't nullptr then server
44 // response is copied to *response. Returns true if the request was completed
45 // successfully.
46 [[nodiscard]] bool WaitForCompletion(std::string* response);
47
48 private:
49 // URLRequest::Delegate methods.
50 void OnResponseStarted(URLRequest* request, int net_error) override;
51 void OnReadCompleted(URLRequest* request, int num_bytes) override;
52
53 void ReadResponse();
54 void OnCommandCompleted(int net_error);
55
56 // Request results.
57 int result_code_ = 0;
58 std::string data_received_;
59
60 // WaitableEvent to notify when the request is finished.
61 base::WaitableEvent event_;
62
63 std::unique_ptr<URLRequestContext> context_;
64 std::unique_ptr<URLRequest> request_;
65
66 scoped_refptr<IOBuffer> read_buffer_;
67
68 THREAD_CHECKER(thread_checker_);
69 };
70
Core()71 RemoteTestServerSpawnerRequest::Core::Core()
72 : event_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
73 base::WaitableEvent::InitialState::NOT_SIGNALED),
74 read_buffer_(base::MakeRefCounted<IOBufferWithSize>(kBufferSize)) {
75 DETACH_FROM_THREAD(thread_checker_);
76 }
77
SendRequest(const GURL & url,const std::string & post_data)78 void RemoteTestServerSpawnerRequest::Core::SendRequest(
79 const GURL& url,
80 const std::string& post_data) {
81 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
82
83 // Prepare the URLRequest for sending the command.
84 DCHECK(!request_.get());
85 context_ = CreateTestURLRequestContextBuilder()->Build();
86 request_ = context_->CreateRequest(url, DEFAULT_PRIORITY, this,
87 TRAFFIC_ANNOTATION_FOR_TESTS);
88
89 if (post_data.empty()) {
90 request_->set_method("GET");
91 } else {
92 request_->set_method("POST");
93 std::unique_ptr<UploadElementReader> reader(
94 UploadOwnedBytesElementReader::CreateWithString(post_data));
95 request_->set_upload(
96 ElementsUploadDataStream::CreateWithReader(std::move(reader), 0));
97 request_->SetExtraRequestHeaderByName(HttpRequestHeaders::kContentType,
98 "application/json",
99 /*overwrite=*/true);
100 }
101
102 request_->Start();
103 }
104
~Core()105 RemoteTestServerSpawnerRequest::Core::~Core() {
106 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
107 }
108
WaitForCompletion(std::string * response)109 bool RemoteTestServerSpawnerRequest::Core::WaitForCompletion(
110 std::string* response) {
111 // Called by RemoteTestServerSpawnerRequest::WaitForCompletion() on the main
112 // thread.
113
114 event_.Wait();
115 if (response)
116 *response = data_received_;
117 return result_code_ == OK;
118 }
119
OnCommandCompleted(int net_error)120 void RemoteTestServerSpawnerRequest::Core::OnCommandCompleted(int net_error) {
121 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
122 DCHECK_NE(ERR_IO_PENDING, net_error);
123 DCHECK(!event_.IsSignaled());
124
125 // If request has failed, return the error code.
126 if (net_error != OK) {
127 LOG(ERROR) << "request failed, error: " << ErrorToString(net_error);
128 result_code_ = net_error;
129 } else if (request_->GetResponseCode() != 200) {
130 LOG(ERROR) << "Spawner server returned bad status: "
131 << request_->response_headers()->GetStatusLine() << ", "
132 << data_received_;
133 result_code_ = ERR_FAILED;
134 }
135
136 if (result_code_ != OK)
137 data_received_.clear();
138
139 request_.reset();
140 context_.reset();
141
142 event_.Signal();
143 }
144
ReadResponse()145 void RemoteTestServerSpawnerRequest::Core::ReadResponse() {
146 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
147
148 while (true) {
149 int result = request_->Read(read_buffer_.get(), kBufferSize);
150 if (result == ERR_IO_PENDING)
151 return;
152
153 if (result <= 0) {
154 OnCommandCompleted(result);
155 return;
156 }
157
158 data_received_.append(read_buffer_->data(), result);
159 }
160 }
161
OnResponseStarted(URLRequest * request,int net_error)162 void RemoteTestServerSpawnerRequest::Core::OnResponseStarted(
163 URLRequest* request,
164 int net_error) {
165 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
166 DCHECK_NE(ERR_IO_PENDING, net_error);
167 DCHECK_EQ(request, request_.get());
168
169 if (net_error != OK) {
170 OnCommandCompleted(net_error);
171 return;
172 }
173
174 ReadResponse();
175 }
176
OnReadCompleted(URLRequest * request,int read_result)177 void RemoteTestServerSpawnerRequest::Core::OnReadCompleted(URLRequest* request,
178 int read_result) {
179 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
180 DCHECK_NE(ERR_IO_PENDING, read_result);
181 DCHECK_EQ(request, request_.get());
182
183 if (read_result <= 0) {
184 OnCommandCompleted(read_result);
185 return;
186 }
187
188 data_received_.append(read_buffer_->data(), read_result);
189
190 ReadResponse();
191 }
192
RemoteTestServerSpawnerRequest(scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,const GURL & url,const std::string & post_data)193 RemoteTestServerSpawnerRequest::RemoteTestServerSpawnerRequest(
194 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
195 const GURL& url,
196 const std::string& post_data)
197 : io_task_runner_(io_task_runner),
198 core_(std::make_unique<Core>()),
199 allowed_port_(
200 std::make_unique<ScopedPortException>(url.EffectiveIntPort())) {
201 io_task_runner_->PostTask(
202 FROM_HERE, base::BindOnce(&Core::SendRequest,
203 base::Unretained(core_.get()), url, post_data));
204 }
205
~RemoteTestServerSpawnerRequest()206 RemoteTestServerSpawnerRequest::~RemoteTestServerSpawnerRequest() {
207 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
208 io_task_runner_->DeleteSoon(FROM_HERE, core_.release());
209 }
210
WaitForCompletion(std::string * response)211 bool RemoteTestServerSpawnerRequest::WaitForCompletion(std::string* response) {
212 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
213 return core_->WaitForCompletion(response);
214 }
215
216 } // namespace net
217