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