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
test_url_1()24 GURL URLRequestTestJob::test_url_1() {
25 return GURL("test:url1");
26 }
test_url_2()27 GURL URLRequestTestJob::test_url_2() {
28 return GURL("test:url2");
29 }
test_url_3()30 GURL URLRequestTestJob::test_url_3() {
31 return GURL("test:url3");
32 }
test_url_error()33 GURL URLRequestTestJob::test_url_error() {
34 return GURL("test:error");
35 }
36
37 // static getters for known URL responses
test_data_1()38 std::string URLRequestTestJob::test_data_1() {
39 return std::string("<html><title>Test One</title></html>");
40 }
test_data_2()41 std::string URLRequestTestJob::test_data_2() {
42 return std::string("<html><title>Test Two Two</title></html>");
43 }
test_data_3()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
test_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
test_redirect_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
test_error_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
Factory(URLRequest * request,const std::string & scheme)75 URLRequestJob* URLRequestTestJob::Factory(URLRequest* request,
76 const std::string& scheme) {
77 return new URLRequestTestJob(request);
78 }
79
URLRequestTestJob(URLRequest * request)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
URLRequestTestJob(URLRequest * request,bool auto_advance)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
URLRequestTestJob(URLRequest * request,const std::string & response_headers,const std::string & response_data,bool auto_advance)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
~URLRequestTestJob()113 URLRequestTestJob::~URLRequestTestJob() {
114 }
115
GetMimeType(std::string * mime_type) const116 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
Start()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
StartAsync()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
ReadRawData(IOBuffer * buf,int buf_size,int * bytes_read)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
GetResponseInfo(HttpResponseInfo * info)184 void URLRequestTestJob::GetResponseInfo(HttpResponseInfo* info) {
185 if (response_headers_)
186 info->headers = response_headers_;
187 }
188
GetResponseCode() const189 int URLRequestTestJob::GetResponseCode() const {
190 if (response_headers_)
191 return response_headers_->response_code();
192 return -1;
193 }
194
IsRedirectResponse(GURL * location,int * http_status_code)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
Kill()210 void URLRequestTestJob::Kill() {
211 stage_ = DONE;
212 URLRequestJob::Kill();
213 }
214
ProcessNextOperation()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
AdvanceJob()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
ProcessOnePendingMessage()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