1 // Copyright 2012 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/url_request/url_request_test_job.h"
6
7 #include <algorithm>
8 #include <list>
9 #include <memory>
10
11 #include "base/compiler_specific.h"
12 #include "base/functional/bind.h"
13 #include "base/lazy_instance.h"
14 #include "base/location.h"
15 #include "base/strings/string_util.h"
16 #include "base/task/single_thread_task_runner.h"
17 #include "base/time/time.h"
18 #include "net/base/io_buffer.h"
19 #include "net/base/net_errors.h"
20 #include "net/http/http_response_headers.h"
21 #include "net/http/http_util.h"
22
23 namespace net {
24
25 namespace {
26
27 typedef std::list<URLRequestTestJob*> URLRequestJobList;
28 base::LazyInstance<URLRequestJobList>::Leaky
29 g_pending_jobs = LAZY_INSTANCE_INITIALIZER;
30
31 } // namespace
32
33 // static getters for known URLs
test_url_1()34 GURL URLRequestTestJob::test_url_1() {
35 return GURL("test:url1");
36 }
37
test_url_2()38 GURL URLRequestTestJob::test_url_2() {
39 return GURL("test:url2");
40 }
41
test_url_3()42 GURL URLRequestTestJob::test_url_3() {
43 return GURL("test:url3");
44 }
45
test_url_4()46 GURL URLRequestTestJob::test_url_4() {
47 return GURL("test:url4");
48 }
49
test_url_auto_advance_async_reads_1()50 GURL URLRequestTestJob::test_url_auto_advance_async_reads_1() {
51 return GURL("test:url_auto_advance_async_reads_1");
52 }
53
test_url_error()54 GURL URLRequestTestJob::test_url_error() {
55 return GURL("test:error");
56 }
57
test_url_redirect_to_url_1()58 GURL URLRequestTestJob::test_url_redirect_to_url_1() {
59 return GURL("test:redirect_to_1");
60 }
61
test_url_redirect_to_url_2()62 GURL URLRequestTestJob::test_url_redirect_to_url_2() {
63 return GURL("test:redirect_to_2");
64 }
65
66 // static getters for known URL responses
test_data_1()67 std::string URLRequestTestJob::test_data_1() {
68 return std::string("<html><title>Test One</title></html>");
69 }
test_data_2()70 std::string URLRequestTestJob::test_data_2() {
71 return std::string("<html><title>Test Two Two</title></html>");
72 }
test_data_3()73 std::string URLRequestTestJob::test_data_3() {
74 return std::string("<html><title>Test Three Three Three</title></html>");
75 }
test_data_4()76 std::string URLRequestTestJob::test_data_4() {
77 return std::string("<html><title>Test Four Four Four Four</title></html>");
78 }
79
80 // static getter for simple response headers
test_headers()81 std::string URLRequestTestJob::test_headers() {
82 static const char kHeaders[] =
83 "HTTP/1.1 200 OK\n"
84 "Content-type: text/html\n"
85 "\n";
86 return std::string(kHeaders, std::size(kHeaders));
87 }
88
89 // static getter for redirect response headers
test_redirect_headers()90 std::string URLRequestTestJob::test_redirect_headers() {
91 static const char kHeaders[] =
92 "HTTP/1.1 302 MOVED\n"
93 "Location: somewhere\n"
94 "\n";
95 return std::string(kHeaders, std::size(kHeaders));
96 }
97
98 // static getter for redirect response headers
test_redirect_to_url_1_headers()99 std::string URLRequestTestJob::test_redirect_to_url_1_headers() {
100 std::string headers = "HTTP/1.1 302 MOVED";
101 headers.push_back('\n');
102 headers += "Location: ";
103 headers += test_url_1().spec();
104 headers.push_back('\n');
105 headers.push_back('\n');
106 return headers;
107 }
108
109 // static getter for redirect response headers
test_redirect_to_url_2_headers()110 std::string URLRequestTestJob::test_redirect_to_url_2_headers() {
111 std::string headers = "HTTP/1.1 302 MOVED";
112 headers.push_back('\n');
113 headers += "Location: ";
114 headers += test_url_2().spec();
115 headers.push_back('\n');
116 headers.push_back('\n');
117 return headers;
118 }
119
120 // static getter for error response headers
test_error_headers()121 std::string URLRequestTestJob::test_error_headers() {
122 static const char kHeaders[] =
123 "HTTP/1.1 500 BOO HOO\n"
124 "\n";
125 return std::string(kHeaders, std::size(kHeaders));
126 }
127
URLRequestTestJob(URLRequest * request,bool auto_advance)128 URLRequestTestJob::URLRequestTestJob(URLRequest* request, bool auto_advance)
129 : URLRequestJob(request),
130 auto_advance_(auto_advance),
131 response_headers_length_(0) {}
132
URLRequestTestJob(URLRequest * request,const std::string & response_headers,const std::string & response_data,bool auto_advance)133 URLRequestTestJob::URLRequestTestJob(URLRequest* request,
134 const std::string& response_headers,
135 const std::string& response_data,
136 bool auto_advance)
137 : URLRequestJob(request),
138 auto_advance_(auto_advance),
139 response_data_(response_data),
140 response_headers_(base::MakeRefCounted<net::HttpResponseHeaders>(
141 net::HttpUtil::AssembleRawHeaders(response_headers))),
142 response_headers_length_(response_headers.size()) {}
143
~URLRequestTestJob()144 URLRequestTestJob::~URLRequestTestJob() {
145 std::erase(g_pending_jobs.Get(), this);
146 }
147
GetMimeType(std::string * mime_type) const148 bool URLRequestTestJob::GetMimeType(std::string* mime_type) const {
149 DCHECK(mime_type);
150 if (!response_headers_.get())
151 return false;
152 return response_headers_->GetMimeType(mime_type);
153 }
154
SetPriority(RequestPriority priority)155 void URLRequestTestJob::SetPriority(RequestPriority priority) {
156 priority_ = priority;
157 }
158
Start()159 void URLRequestTestJob::Start() {
160 // Start reading asynchronously so that all error reporting and data
161 // callbacks happen as they would for network requests.
162 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
163 FROM_HERE, base::BindOnce(&URLRequestTestJob::StartAsync,
164 weak_factory_.GetWeakPtr()));
165 }
166
StartAsync()167 void URLRequestTestJob::StartAsync() {
168 if (!response_headers_.get()) {
169 SetResponseHeaders(test_headers());
170 if (request_->url() == test_url_1()) {
171 response_data_ = test_data_1();
172 stage_ = DATA_AVAILABLE; // Simulate a synchronous response for this one.
173 } else if (request_->url() == test_url_2()) {
174 response_data_ = test_data_2();
175 } else if (request_->url() == test_url_3()) {
176 response_data_ = test_data_3();
177 } else if (request_->url() == test_url_4()) {
178 response_data_ = test_data_4();
179 } else if (request_->url() == test_url_auto_advance_async_reads_1()) {
180 response_data_ = test_data_1();
181 stage_ = DATA_AVAILABLE; // Data is available immediately.
182 async_reads_ = true; // All reads complete asynchronously.
183 } else if (request_->url() == test_url_redirect_to_url_1()) {
184 SetResponseHeaders(test_redirect_to_url_1_headers());
185 } else if (request_->url() == test_url_redirect_to_url_2()) {
186 SetResponseHeaders(test_redirect_to_url_2_headers());
187 } else {
188 AdvanceJob();
189
190 // Return an error on unexpected urls.
191 NotifyStartError(ERR_INVALID_URL);
192 return;
193 }
194 }
195
196 AdvanceJob();
197
198 this->NotifyHeadersComplete();
199 }
200
SetResponseHeaders(const std::string & response_headers)201 void URLRequestTestJob::SetResponseHeaders(
202 const std::string& response_headers) {
203 response_headers_ = base::MakeRefCounted<HttpResponseHeaders>(
204 net::HttpUtil::AssembleRawHeaders(response_headers));
205 response_headers_length_ = response_headers.size();
206 }
207
CopyDataForRead(IOBuffer * buf,int buf_size)208 int URLRequestTestJob::CopyDataForRead(IOBuffer* buf, int buf_size) {
209 int bytes_read = 0;
210 if (offset_ < static_cast<int>(response_data_.length())) {
211 bytes_read = buf_size;
212 if (bytes_read + offset_ > static_cast<int>(response_data_.length()))
213 bytes_read = static_cast<int>(response_data_.length()) - offset_;
214
215 memcpy(buf->data(), &response_data_.c_str()[offset_], bytes_read);
216 offset_ += bytes_read;
217 }
218 return bytes_read;
219 }
220
ReadRawData(IOBuffer * buf,int buf_size)221 int URLRequestTestJob::ReadRawData(IOBuffer* buf, int buf_size) {
222 if (stage_ == WAITING || async_reads_) {
223 async_buf_ = buf;
224 async_buf_size_ = buf_size;
225 if (stage_ != WAITING) {
226 stage_ = WAITING;
227 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
228 FROM_HERE, base::BindOnce(&URLRequestTestJob::ProcessNextOperation,
229 weak_factory_.GetWeakPtr()));
230 }
231 return ERR_IO_PENDING;
232 }
233
234 return CopyDataForRead(buf, buf_size);
235 }
236
GetResponseInfo(HttpResponseInfo * info)237 void URLRequestTestJob::GetResponseInfo(HttpResponseInfo* info) {
238 if (response_headers_.get())
239 info->headers = response_headers_;
240 }
241
GetLoadTimingInfo(LoadTimingInfo * load_timing_info) const242 void URLRequestTestJob::GetLoadTimingInfo(
243 LoadTimingInfo* load_timing_info) const {
244 // Preserve the times the URLRequest is responsible for, but overwrite all
245 // the others.
246 base::TimeTicks request_start = load_timing_info->request_start;
247 base::Time request_start_time = load_timing_info->request_start_time;
248 *load_timing_info = load_timing_info_;
249 load_timing_info->request_start = request_start;
250 load_timing_info->request_start_time = request_start_time;
251 }
252
GetTotalReceivedBytes() const253 int64_t URLRequestTestJob::GetTotalReceivedBytes() const {
254 return response_headers_length_ + offset_;
255 }
256
IsRedirectResponse(GURL * location,int * http_status_code,bool * insecure_scheme_was_upgraded)257 bool URLRequestTestJob::IsRedirectResponse(GURL* location,
258 int* http_status_code,
259 bool* insecure_scheme_was_upgraded) {
260 if (!response_headers_.get())
261 return false;
262
263 std::string value;
264 if (!response_headers_->IsRedirect(&value))
265 return false;
266
267 *insecure_scheme_was_upgraded = false;
268 *location = request_->url().Resolve(value);
269 *http_status_code = response_headers_->response_code();
270 return true;
271 }
272
Kill()273 void URLRequestTestJob::Kill() {
274 stage_ = DONE;
275 URLRequestJob::Kill();
276 weak_factory_.InvalidateWeakPtrs();
277 std::erase(g_pending_jobs.Get(), this);
278 }
279
ProcessNextOperation()280 void URLRequestTestJob::ProcessNextOperation() {
281 switch (stage_) {
282 case WAITING:
283 // Must call AdvanceJob() prior to NotifyReadComplete() since that may
284 // delete |this|.
285 AdvanceJob();
286 stage_ = DATA_AVAILABLE;
287 // OK if ReadRawData wasn't called yet.
288 if (async_buf_) {
289 int result = CopyDataForRead(async_buf_.get(), async_buf_size_);
290 if (result < 0)
291 NOTREACHED() << "Reads should not fail in DATA_AVAILABLE.";
292 if (NextReadAsync()) {
293 // Make all future reads return io pending until the next
294 // ProcessNextOperation().
295 stage_ = WAITING;
296 }
297 ReadRawDataComplete(result);
298 }
299 break;
300 case DATA_AVAILABLE:
301 AdvanceJob();
302 stage_ = ALL_DATA; // done sending data
303 break;
304 case ALL_DATA:
305 stage_ = DONE;
306 return;
307 case DONE:
308 return;
309 default:
310 NOTREACHED() << "Invalid stage";
311 return;
312 }
313 }
314
NextReadAsync()315 bool URLRequestTestJob::NextReadAsync() {
316 return false;
317 }
318
AdvanceJob()319 void URLRequestTestJob::AdvanceJob() {
320 if (auto_advance_) {
321 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
322 FROM_HERE, base::BindOnce(&URLRequestTestJob::ProcessNextOperation,
323 weak_factory_.GetWeakPtr()));
324 return;
325 }
326 g_pending_jobs.Get().push_back(this);
327 }
328
329 // static
ProcessOnePendingMessage()330 bool URLRequestTestJob::ProcessOnePendingMessage() {
331 if (g_pending_jobs.Get().empty())
332 return false;
333
334 URLRequestTestJob* next_job(g_pending_jobs.Get().front());
335 g_pending_jobs.Get().pop_front();
336
337 DCHECK(!next_job->auto_advance()); // auto_advance jobs should be in this q
338 next_job->ProcessNextOperation();
339 return true;
340 }
341
342 } // namespace net
343