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