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