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 16 namespace 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. 21 static std::vector< scoped_refptr<URLRequestTestJob> > g_pending_jobs; 22 23 // static getters for known URLs 24 GURL URLRequestTestJob::test_url_1() { 25 return GURL("test:url1"); 26 } 27 GURL URLRequestTestJob::test_url_2() { 28 return GURL("test:url2"); 29 } 30 GURL URLRequestTestJob::test_url_3() { 31 return GURL("test:url3"); 32 } 33 GURL URLRequestTestJob::test_url_error() { 34 return GURL("test:error"); 35 } 36 37 // static getters for known URL responses 38 std::string URLRequestTestJob::test_data_1() { 39 return std::string("<html><title>Test One</title></html>"); 40 } 41 std::string URLRequestTestJob::test_data_2() { 42 return std::string("<html><title>Test Two Two</title></html>"); 43 } 44 std::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 49 std::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 58 std::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 67 std::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 75 URLRequestJob* URLRequestTestJob::Factory(URLRequest* request, 76 const std::string& scheme) { 77 return new URLRequestTestJob(request); 78 } 79 80 URLRequestTestJob::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 89 URLRequestTestJob::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 99 URLRequestTestJob::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 113 URLRequestTestJob::~URLRequestTestJob() { 114 } 115 116 bool 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 123 void 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 130 void 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 157 bool 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 184 void URLRequestTestJob::GetResponseInfo(HttpResponseInfo* info) { 185 if (response_headers_) 186 info->headers = response_headers_; 187 } 188 189 int URLRequestTestJob::GetResponseCode() const { 190 if (response_headers_) 191 return response_headers_->response_code(); 192 return -1; 193 } 194 195 bool 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 210 void URLRequestTestJob::Kill() { 211 stage_ = DONE; 212 URLRequestJob::Kill(); 213 } 214 215 void 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 243 void 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 253 bool 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