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 19namespace net { 20 21namespace { 22 23typedef std::list<URLRequestTestJob*> URLRequestJobList; 24base::LazyInstance<URLRequestJobList>::Leaky 25 g_pending_jobs = LAZY_INSTANCE_INITIALIZER; 26 27class 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 39GURL URLRequestTestJob::test_url_1() { 40 return GURL("test:url1"); 41} 42GURL URLRequestTestJob::test_url_2() { 43 return GURL("test:url2"); 44} 45GURL URLRequestTestJob::test_url_3() { 46 return GURL("test:url3"); 47} 48GURL URLRequestTestJob::test_url_4() { 49 return GURL("test:url4"); 50} 51GURL URLRequestTestJob::test_url_error() { 52 return GURL("test:error"); 53} 54GURL URLRequestTestJob::test_url_redirect_to_url_2() { 55 return GURL("test:redirect_to_2"); 56} 57 58// static getters for known URL responses 59std::string URLRequestTestJob::test_data_1() { 60 return std::string("<html><title>Test One</title></html>"); 61} 62std::string URLRequestTestJob::test_data_2() { 63 return std::string("<html><title>Test Two Two</title></html>"); 64} 65std::string URLRequestTestJob::test_data_3() { 66 return std::string("<html><title>Test Three Three Three</title></html>"); 67} 68std::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 73std::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 82std::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 91std::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 102std::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 110URLRequestJobFactory::ProtocolHandler* 111URLRequestTestJob::CreateProtocolHandler() { 112 return new TestJobProtocolHandler(); 113} 114 115URLRequestTestJob::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 127URLRequestTestJob::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 140URLRequestTestJob::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 157URLRequestTestJob::~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 164bool 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 171void URLRequestTestJob::SetPriority(RequestPriority priority) { 172 priority_ = priority; 173} 174 175void 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 183void 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 217bool 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 244void URLRequestTestJob::GetResponseInfo(HttpResponseInfo* info) { 245 if (response_headers_.get()) 246 info->headers = response_headers_; 247} 248 249void 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 260int URLRequestTestJob::GetResponseCode() const { 261 if (response_headers_.get()) 262 return response_headers_->response_code(); 263 return -1; 264} 265 266bool 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 280void 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 290void 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 326bool URLRequestTestJob::NextReadAsync() { 327 return false; 328} 329 330void 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 341bool 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