1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
|
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/net/url_fetcher.h"
#include "base/compiler_specific.h"
#include "base/string_util.h"
#include "base/thread.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_thread.h"
#include "googleurl/src/gurl.h"
#include "net/base/load_flags.h"
#include "net/base/io_buffer.h"
#include "net/http/http_response_headers.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_context.h"
static const int kBufferSize = 4096;
class URLFetcher::Core
: public base::RefCountedThreadSafe<URLFetcher::Core>,
public URLRequest::Delegate {
public:
// For POST requests, set |content_type| to the MIME type of the content
// and set |content| to the data to upload. |flags| are flags to apply to
// the load operation--these should be one or more of the LOAD_* flags
// defined in url_request.h.
Core(URLFetcher* fetcher,
const GURL& original_url,
RequestType request_type,
URLFetcher::Delegate* d);
// Starts the load. It's important that this not happen in the constructor
// because it causes the IO thread to begin AddRef()ing and Release()ing
// us. If our caller hasn't had time to fully construct us and take a
// reference, the IO thread could interrupt things, run a task, Release()
// us, and destroy us, leaving the caller with an already-destroyed object
// when construction finishes.
void Start();
// Stops any in-progress load and ensures no callback will happen. It is
// safe to call this multiple times.
void Stop();
// URLRequest::Delegate implementations
virtual void OnReceivedRedirect(URLRequest* request,
const GURL& new_url) { }
virtual void OnResponseStarted(URLRequest* request);
virtual void OnReadCompleted(URLRequest* request, int bytes_read);
private:
// Wrapper functions that allow us to ensure actions happen on the right
// thread.
void StartURLRequest();
void CancelURLRequest();
void OnCompletedURLRequest(const URLRequestStatus& status);
URLFetcher* fetcher_; // Corresponding fetcher object
GURL original_url_; // The URL we were asked to fetch
GURL url_; // The URL we eventually wound up at
RequestType request_type_; // What type of request is this?
URLFetcher::Delegate* delegate_; // Object to notify on completion
MessageLoop* delegate_loop_; // Message loop of the creating thread
MessageLoop* io_loop_; // Message loop of the IO thread
URLRequest* request_; // The actual request this wraps
int load_flags_; // Flags for the load operation
int response_code_; // HTTP status code for the request
std::string data_; // Results of the request
scoped_refptr<net::IOBuffer> buffer_;
// Read buffer
scoped_refptr<URLRequestContext> request_context_;
// Cookie/cache info for the request
ResponseCookies cookies_; // Response cookies
std::string extra_request_headers_;// Extra headers for the request, if any
scoped_refptr<net::HttpResponseHeaders> response_headers_;
std::string upload_content_; // HTTP POST payload
std::string upload_content_type_; // MIME type of POST payload
// The overload protection entry for this URL. This is used to
// incrementally back off how rapidly we'll send requests to a particular
// URL, to avoid placing too much demand on the remote resource. We update
// this with the status of all requests as they return, and in turn use it
// to determine how long to wait before making another request.
URLFetcherProtectEntry* protect_entry_;
// |num_retries_| indicates how many times we've failed to successfully
// fetch this URL. Once this value exceeds the maximum number of retries
// specified by the protection manager, we'll give up.
int num_retries_;
friend class URLFetcher;
DISALLOW_COPY_AND_ASSIGN(Core);
};
URLFetcher::URLFetcher(const GURL& url,
RequestType request_type,
Delegate* d)
: ALLOW_THIS_IN_INITIALIZER_LIST(
core_(new Core(this, url, request_type, d))) {
}
URLFetcher::~URLFetcher() {
core_->Stop();
}
URLFetcher::Core::Core(URLFetcher* fetcher,
const GURL& original_url,
RequestType request_type,
URLFetcher::Delegate* d)
: fetcher_(fetcher),
original_url_(original_url),
request_type_(request_type),
delegate_(d),
delegate_loop_(MessageLoop::current()),
io_loop_(ChromeThread::GetMessageLoop(ChromeThread::IO)),
request_(NULL),
load_flags_(net::LOAD_NORMAL),
response_code_(-1),
buffer_(new net::IOBuffer(kBufferSize)),
protect_entry_(URLFetcherProtectManager::GetInstance()->Register(
original_url_.host())),
num_retries_(0) {
}
void URLFetcher::Core::Start() {
DCHECK(delegate_loop_);
DCHECK(io_loop_);
DCHECK(request_context_) << "We need an URLRequestContext!";
io_loop_->PostDelayedTask(FROM_HERE, NewRunnableMethod(
this, &Core::StartURLRequest),
protect_entry_->UpdateBackoff(URLFetcherProtectEntry::SEND));
}
void URLFetcher::Core::Stop() {
DCHECK_EQ(MessageLoop::current(), delegate_loop_);
delegate_ = NULL;
io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
this, &Core::CancelURLRequest));
}
void URLFetcher::Core::OnResponseStarted(URLRequest* request) {
DCHECK(request == request_);
DCHECK(MessageLoop::current() == io_loop_);
if (request_->status().is_success()) {
response_code_ = request_->GetResponseCode();
response_headers_ = request_->response_headers();
}
int bytes_read = 0;
// Some servers may treat HEAD requests as GET requests. To free up the
// network connection as soon as possible, signal that the request has
// completed immediately, without trying to read any data back (all we care
// about is the response code and headers, which we already have).
if (request_->status().is_success() && (request_type_ != HEAD))
request_->Read(buffer_, kBufferSize, &bytes_read);
OnReadCompleted(request_, bytes_read);
}
void URLFetcher::Core::OnReadCompleted(URLRequest* request, int bytes_read) {
DCHECK(request == request_);
DCHECK(MessageLoop::current() == io_loop_);
url_ = request->url();
do {
if (!request_->status().is_success() || bytes_read <= 0)
break;
data_.append(buffer_->data(), bytes_read);
} while (request_->Read(buffer_, kBufferSize, &bytes_read));
if (request_->status().is_success())
request_->GetResponseCookies(&cookies_);
// See comments re: HEAD requests in OnResponseStarted().
if (!request_->status().is_io_pending() || (request_type_ == HEAD)) {
delegate_loop_->PostTask(FROM_HERE, NewRunnableMethod(
this, &Core::OnCompletedURLRequest, request_->status()));
delete request_;
request_ = NULL;
}
}
void URLFetcher::Core::StartURLRequest() {
DCHECK(MessageLoop::current() == io_loop_);
DCHECK(!request_);
request_ = new URLRequest(original_url_, this);
request_->set_load_flags(
request_->load_flags() | net::LOAD_DISABLE_INTERCEPT | load_flags_);
request_->set_context(request_context_.get());
switch (request_type_) {
case GET:
break;
case POST:
DCHECK(!upload_content_.empty());
DCHECK(!upload_content_type_.empty());
request_->set_method("POST");
if (!extra_request_headers_.empty())
extra_request_headers_ += "\r\n";
StringAppendF(&extra_request_headers_,
"Content-Type: %s", upload_content_type_.c_str());
request_->AppendBytesToUpload(upload_content_.data(),
static_cast<int>(upload_content_.size()));
break;
case HEAD:
request_->set_method("HEAD");
break;
default:
NOTREACHED();
}
if (!extra_request_headers_.empty())
request_->SetExtraRequestHeaders(extra_request_headers_);
request_->Start();
}
void URLFetcher::Core::CancelURLRequest() {
DCHECK(MessageLoop::current() == io_loop_);
if (request_) {
request_->Cancel();
delete request_;
request_ = NULL;
}
// Release the reference to the request context. There could be multiple
// references to URLFetcher::Core at this point so it may take a while to
// delete the object, but we cannot delay the destruction of the request
// context.
request_context_ = NULL;
}
void URLFetcher::Core::OnCompletedURLRequest(const URLRequestStatus& status) {
DCHECK(MessageLoop::current() == delegate_loop_);
// Checks the response from server.
if (response_code_ >= 500) {
// When encountering a server error, we will send the request again
// after backoff time.
const int wait =
protect_entry_->UpdateBackoff(URLFetcherProtectEntry::FAILURE);
++num_retries_;
// Restarts the request if we still need to notify the delegate.
if (delegate_) {
if (num_retries_ <= protect_entry_->max_retries()) {
io_loop_->PostDelayedTask(FROM_HERE, NewRunnableMethod(
this, &Core::StartURLRequest), wait);
} else {
delegate_->OnURLFetchComplete(fetcher_, url_, status, response_code_,
cookies_, data_);
}
}
} else {
protect_entry_->UpdateBackoff(URLFetcherProtectEntry::SUCCESS);
if (delegate_)
delegate_->OnURLFetchComplete(fetcher_, url_, status, response_code_,
cookies_, data_);
}
}
void URLFetcher::set_io_loop(MessageLoop* io_loop) {
core_->io_loop_ = io_loop;
}
void URLFetcher::set_upload_data(const std::string& upload_content_type,
const std::string& upload_content) {
core_->upload_content_type_ = upload_content_type;
core_->upload_content_ = upload_content;
}
void URLFetcher::set_load_flags(int load_flags) {
core_->load_flags_ = load_flags;
}
void URLFetcher::set_extra_request_headers(
const std::string& extra_request_headers) {
core_->extra_request_headers_ = extra_request_headers;
}
void URLFetcher::set_request_context(URLRequestContext* request_context) {
core_->request_context_ = request_context;
}
net::HttpResponseHeaders* URLFetcher::response_headers() const {
return core_->response_headers_;
}
void URLFetcher::Start() {
core_->Start();
}
const GURL& URLFetcher::url() const {
return core_->url_;
}
|