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