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