1// Copyright (c) 2011 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 <vector> 6 7#include "net/url_request/url_request_test_job.h" 8 9#include "base/message_loop.h" 10#include "base/string_util.h" 11#include "net/base/io_buffer.h" 12#include "net/base/net_errors.h" 13#include "net/http/http_response_headers.h" 14#include "net/url_request/url_request.h" 15 16namespace net { 17 18// This emulates the global message loop for the test URL request class, since 19// this is only test code, it's probably not too dangerous to have this static 20// object. 21static std::vector< scoped_refptr<URLRequestTestJob> > g_pending_jobs; 22 23// static getters for known URLs 24GURL URLRequestTestJob::test_url_1() { 25 return GURL("test:url1"); 26} 27GURL URLRequestTestJob::test_url_2() { 28 return GURL("test:url2"); 29} 30GURL URLRequestTestJob::test_url_3() { 31 return GURL("test:url3"); 32} 33GURL URLRequestTestJob::test_url_error() { 34 return GURL("test:error"); 35} 36 37// static getters for known URL responses 38std::string URLRequestTestJob::test_data_1() { 39 return std::string("<html><title>Test One</title></html>"); 40} 41std::string URLRequestTestJob::test_data_2() { 42 return std::string("<html><title>Test Two Two</title></html>"); 43} 44std::string URLRequestTestJob::test_data_3() { 45 return std::string("<html><title>Test Three Three Three</title></html>"); 46} 47 48// static getter for simple response headers 49std::string URLRequestTestJob::test_headers() { 50 const char headers[] = 51 "HTTP/1.1 200 OK\0" 52 "Content-type: text/html\0" 53 "\0"; 54 return std::string(headers, arraysize(headers)); 55} 56 57// static getter for redirect response headers 58std::string URLRequestTestJob::test_redirect_headers() { 59 const char headers[] = 60 "HTTP/1.1 302 MOVED\0" 61 "Location: somewhere\0" 62 "\0"; 63 return std::string(headers, arraysize(headers)); 64} 65 66// static getter for error response headers 67std::string URLRequestTestJob::test_error_headers() { 68 const char headers[] = 69 "HTTP/1.1 500 BOO HOO\0" 70 "\0"; 71 return std::string(headers, arraysize(headers)); 72} 73 74// static 75URLRequestJob* URLRequestTestJob::Factory(URLRequest* request, 76 const std::string& scheme) { 77 return new URLRequestTestJob(request); 78} 79 80URLRequestTestJob::URLRequestTestJob(URLRequest* request) 81 : URLRequestJob(request), 82 auto_advance_(false), 83 stage_(WAITING), 84 offset_(0), 85 async_buf_(NULL), 86 async_buf_size_(0) { 87} 88 89URLRequestTestJob::URLRequestTestJob(URLRequest* request, 90 bool auto_advance) 91 : URLRequestJob(request), 92 auto_advance_(auto_advance), 93 stage_(WAITING), 94 offset_(0), 95 async_buf_(NULL), 96 async_buf_size_(0) { 97} 98 99URLRequestTestJob::URLRequestTestJob(URLRequest* request, 100 const std::string& response_headers, 101 const std::string& response_data, 102 bool auto_advance) 103 : URLRequestJob(request), 104 auto_advance_(auto_advance), 105 stage_(WAITING), 106 response_headers_(new HttpResponseHeaders(response_headers)), 107 response_data_(response_data), 108 offset_(0), 109 async_buf_(NULL), 110 async_buf_size_(0) { 111} 112 113URLRequestTestJob::~URLRequestTestJob() { 114} 115 116bool URLRequestTestJob::GetMimeType(std::string* mime_type) const { 117 DCHECK(mime_type); 118 if (!response_headers_) 119 return false; 120 return response_headers_->GetMimeType(mime_type); 121} 122 123void URLRequestTestJob::Start() { 124 // Start reading asynchronously so that all error reporting and data 125 // callbacks happen as they would for network requests. 126 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( 127 this, &URLRequestTestJob::StartAsync)); 128} 129 130void URLRequestTestJob::StartAsync() { 131 if (!response_headers_) { 132 response_headers_ = new HttpResponseHeaders(test_headers()); 133 if (request_->url().spec() == test_url_1().spec()) { 134 response_data_ = test_data_1(); 135 stage_ = DATA_AVAILABLE; // Simulate a synchronous response for this one. 136 } else if (request_->url().spec() == test_url_2().spec()) { 137 response_data_ = test_data_2(); 138 } else if (request_->url().spec() == test_url_3().spec()) { 139 response_data_ = test_data_3(); 140 } else { 141 // unexpected url, return error 142 // FIXME(brettw) we may want to use WININET errors or have some more types 143 // of errors 144 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, 145 ERR_INVALID_URL)); 146 // FIXME(brettw): this should emulate a network error, and not just fail 147 // initiating a connection 148 return; 149 } 150 } 151 152 AdvanceJob(); 153 154 this->NotifyHeadersComplete(); 155} 156 157bool URLRequestTestJob::ReadRawData(IOBuffer* buf, int buf_size, 158 int *bytes_read) { 159 if (stage_ == WAITING) { 160 async_buf_ = buf; 161 async_buf_size_ = buf_size; 162 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); 163 return false; 164 } 165 166 DCHECK(bytes_read); 167 *bytes_read = 0; 168 169 if (offset_ >= static_cast<int>(response_data_.length())) { 170 return true; // done reading 171 } 172 173 int to_read = buf_size; 174 if (to_read + offset_ > static_cast<int>(response_data_.length())) 175 to_read = static_cast<int>(response_data_.length()) - offset_; 176 177 memcpy(buf->data(), &response_data_.c_str()[offset_], to_read); 178 offset_ += to_read; 179 180 *bytes_read = to_read; 181 return true; 182} 183 184void URLRequestTestJob::GetResponseInfo(HttpResponseInfo* info) { 185 if (response_headers_) 186 info->headers = response_headers_; 187} 188 189int URLRequestTestJob::GetResponseCode() const { 190 if (response_headers_) 191 return response_headers_->response_code(); 192 return -1; 193} 194 195bool URLRequestTestJob::IsRedirectResponse(GURL* location, 196 int* http_status_code) { 197 if (!response_headers_) 198 return false; 199 200 std::string value; 201 if (!response_headers_->IsRedirect(&value)) 202 return false; 203 204 *location = request_->url().Resolve(value); 205 *http_status_code = response_headers_->response_code(); 206 return true; 207} 208 209 210void URLRequestTestJob::Kill() { 211 stage_ = DONE; 212 URLRequestJob::Kill(); 213} 214 215void URLRequestTestJob::ProcessNextOperation() { 216 switch (stage_) { 217 case WAITING: 218 stage_ = DATA_AVAILABLE; 219 // OK if ReadRawData wasn't called yet. 220 if (async_buf_) { 221 int bytes_read; 222 if (!ReadRawData(async_buf_, async_buf_size_, &bytes_read)) 223 NOTREACHED() << "This should not return false in DATA_AVAILABLE."; 224 SetStatus(URLRequestStatus()); // clear the io pending flag 225 NotifyReadComplete(bytes_read); 226 } 227 break; 228 case DATA_AVAILABLE: 229 stage_ = ALL_DATA; // done sending data 230 break; 231 case ALL_DATA: 232 stage_ = DONE; 233 return; 234 case DONE: 235 return; 236 default: 237 NOTREACHED() << "Invalid stage"; 238 return; 239 } 240 AdvanceJob(); 241} 242 243void URLRequestTestJob::AdvanceJob() { 244 if (auto_advance_) { 245 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( 246 this, &URLRequestTestJob::ProcessNextOperation)); 247 return; 248 } 249 g_pending_jobs.push_back(scoped_refptr<URLRequestTestJob>(this)); 250} 251 252// static 253bool URLRequestTestJob::ProcessOnePendingMessage() { 254 if (g_pending_jobs.empty()) 255 return false; 256 257 scoped_refptr<URLRequestTestJob> next_job(g_pending_jobs[0]); 258 g_pending_jobs.erase(g_pending_jobs.begin()); 259 260 DCHECK(!next_job->auto_advance()); // auto_advance jobs should be in this q 261 next_job->ProcessNextOperation(); 262 return true; 263} 264 265} // namespace net 266