Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(314)

Side by Side Diff: net/url_request/url_request_http_job.cc

Issue 6382003: Reorder the methods in net/url_request/. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Compiling net_unittests != compiling the rest of chrome Created 9 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « net/url_request/url_request_http_job.h ('k') | net/url_request/url_request_job.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "net/url_request/url_request_http_job.h" 5 #include "net/url_request/url_request_http_job.h"
6 6
7 #include "base/base_switches.h" 7 #include "base/base_switches.h"
8 #include "base/command_line.h" 8 #include "base/command_line.h"
9 #include "base/compiler_specific.h" 9 #include "base/compiler_specific.h"
10 #include "base/file_util.h" 10 #include "base/file_util.h"
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after
110 GURL new_location = request->url().ReplaceComponents(replacements); 110 GURL new_location = request->url().ReplaceComponents(replacements);
111 return new URLRequestRedirectJob(request, new_location); 111 return new URLRequestRedirectJob(request, new_location);
112 } else { 112 } else {
113 // TODO(agl): implement opportunistic HTTPS upgrade. 113 // TODO(agl): implement opportunistic HTTPS upgrade.
114 } 114 }
115 } 115 }
116 116
117 return new URLRequestHttpJob(request); 117 return new URLRequestHttpJob(request);
118 } 118 }
119 119
120
120 URLRequestHttpJob::URLRequestHttpJob(URLRequest* request) 121 URLRequestHttpJob::URLRequestHttpJob(URLRequest* request)
121 : URLRequestJob(request), 122 : URLRequestJob(request),
122 response_info_(NULL), 123 response_info_(NULL),
123 response_cookies_save_index_(0), 124 response_cookies_save_index_(0),
124 proxy_auth_state_(AUTH_STATE_DONT_NEED_AUTH), 125 proxy_auth_state_(AUTH_STATE_DONT_NEED_AUTH),
125 server_auth_state_(AUTH_STATE_DONT_NEED_AUTH), 126 server_auth_state_(AUTH_STATE_DONT_NEED_AUTH),
126 ALLOW_THIS_IN_INITIALIZER_LIST(can_get_cookies_callback_( 127 ALLOW_THIS_IN_INITIALIZER_LIST(can_get_cookies_callback_(
127 this, &URLRequestHttpJob::OnCanGetCookiesCompleted)), 128 this, &URLRequestHttpJob::OnCanGetCookiesCompleted)),
128 ALLOW_THIS_IN_INITIALIZER_LIST(can_set_cookie_callback_( 129 ALLOW_THIS_IN_INITIALIZER_LIST(can_set_cookie_callback_(
129 this, &URLRequestHttpJob::OnCanSetCookieCompleted)), 130 this, &URLRequestHttpJob::OnCanSetCookieCompleted)),
130 ALLOW_THIS_IN_INITIALIZER_LIST(start_callback_( 131 ALLOW_THIS_IN_INITIALIZER_LIST(start_callback_(
131 this, &URLRequestHttpJob::OnStartCompleted)), 132 this, &URLRequestHttpJob::OnStartCompleted)),
132 ALLOW_THIS_IN_INITIALIZER_LIST(read_callback_( 133 ALLOW_THIS_IN_INITIALIZER_LIST(read_callback_(
133 this, &URLRequestHttpJob::OnReadCompleted)), 134 this, &URLRequestHttpJob::OnReadCompleted)),
134 read_in_progress_(false), 135 read_in_progress_(false),
135 transaction_(NULL), 136 transaction_(NULL),
136 throttling_entry_(URLRequestThrottlerManager::GetInstance()-> 137 throttling_entry_(URLRequestThrottlerManager::GetInstance()->
137 RegisterRequestUrl(request->url())), 138 RegisterRequestUrl(request->url())),
138 sdch_dictionary_advertised_(false), 139 sdch_dictionary_advertised_(false),
139 sdch_test_activated_(false), 140 sdch_test_activated_(false),
140 sdch_test_control_(false), 141 sdch_test_control_(false),
141 is_cached_content_(false), 142 is_cached_content_(false),
142 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { 143 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
143 } 144 }
144 145
145 URLRequestHttpJob::~URLRequestHttpJob() { 146 void URLRequestHttpJob::NotifyHeadersComplete() {
146 DCHECK(!sdch_test_control_ || !sdch_test_activated_); 147 DCHECK(!response_info_);
147 if (!IsCachedContent()) { 148
148 if (sdch_test_control_) 149 response_info_ = transaction_->GetResponseInfo();
149 RecordPacketStats(SDCH_EXPERIMENT_HOLDBACK); 150
150 if (sdch_test_activated_) 151 // Save boolean, as we'll need this info at destruction time, and filters may
151 RecordPacketStats(SDCH_EXPERIMENT_DECODE); 152 // also need this info.
152 } 153 is_cached_content_ = response_info_->was_cached;
153 // Make sure SDCH filters are told to emit histogram data while this class 154
154 // can still service the IsCachedContent() call. 155 if (!is_cached_content_) {
155 DestroyFilters(); 156 URLRequestThrottlerHeaderAdapter response_adapter(
156 157 response_info_->headers);
157 if (sdch_dictionary_url_.is_valid()) { 158 throttling_entry_->UpdateWithResponse(&response_adapter);
158 // Prior to reaching the destructor, request_ has been set to a NULL 159 }
159 // pointer, so request_->url() is no longer valid in the destructor, and we 160
160 // use an alternate copy |request_info_.url|. 161 ProcessStrictTransportSecurityHeader();
161 SdchManager* manager = SdchManager::Global(); 162
162 // To be extra safe, since this is a "different time" from when we decided 163 if (SdchManager::Global() &&
163 // to get the dictionary, we'll validate that an SdchManager is available. 164 SdchManager::Global()->IsInSupportedDomain(request_->url())) {
164 // At shutdown time, care is taken to be sure that we don't delete this 165 static const std::string name = "Get-Dictionary";
165 // globally useful instance "too soon," so this check is just defensive 166 std::string url_text;
166 // coding to assure that IF the system is shutting down, we don't have any 167 void* iter = NULL;
167 // problem if the manager was deleted ahead of time. 168 // TODO(jar): We need to not fetch dictionaries the first time they are
168 if (manager) // Defensive programming. 169 // seen, but rather wait until we can justify their usefulness.
169 manager->FetchDictionary(request_info_.url, sdch_dictionary_url_); 170 // For now, we will only fetch the first dictionary, which will at least
170 } 171 // require multiple suggestions before we get additional ones for this site.
171 } 172 // Eventually we should wait until a dictionary is requested several times
172 173 // before we even download it (so that we don't waste memory or bandwidth).
173 void URLRequestHttpJob::SetUpload(UploadData* upload) { 174 if (response_info_->headers->EnumerateHeader(&iter, name, &url_text)) {
174 DCHECK(!transaction_.get()) << "cannot change once started"; 175 // request_->url() won't be valid in the destructor, so we use an
175 request_info_.upload_data = upload; 176 // alternate copy.
176 } 177 DCHECK(request_->url() == request_info_.url);
177 178 // Resolve suggested URL relative to request url.
178 void URLRequestHttpJob::SetExtraRequestHeaders( 179 sdch_dictionary_url_ = request_info_.url.Resolve(url_text);
179 const HttpRequestHeaders& headers) { 180 }
180 DCHECK(!transaction_.get()) << "cannot change once started"; 181 }
181 request_info_.extra_headers.CopyFrom(headers); 182
182 } 183 // The HTTP transaction may be restarted several times for the purposes
183 184 // of sending authorization information. Each time it restarts, we get
184 void URLRequestHttpJob::Start() { 185 // notified of the headers completion so that we can update the cookie store.
185 DCHECK(!transaction_.get()); 186 if (transaction_->IsReadyToRestartForAuth()) {
186 187 DCHECK(!response_info_->auth_challenge.get());
187 // Ensure that we do not send username and password fields in the referrer. 188 RestartTransactionWithAuth(string16(), string16());
188 GURL referrer(request_->GetSanitizedReferrer());
189
190 request_info_.url = request_->url();
191 request_info_.referrer = referrer;
192 request_info_.method = request_->method();
193 request_info_.load_flags = request_->load_flags();
194 request_info_.priority = request_->priority();
195
196 if (request_->context()) {
197 request_info_.extra_headers.SetHeader(
198 HttpRequestHeaders::kUserAgent,
199 request_->context()->GetUserAgent(request_->url()));
200 }
201
202 AddExtraHeaders();
203 AddCookieHeaderAndStart();
204 }
205
206 void URLRequestHttpJob::Kill() {
207 if (!transaction_.get())
208 return; 189 return;
209 190 }
210 DestroyTransaction(); 191
211 URLRequestJob::Kill(); 192 URLRequestJob::NotifyHeadersComplete();
212 } 193 }
213 194
214 LoadState URLRequestHttpJob::GetLoadState() const { 195 void URLRequestHttpJob::DestroyTransaction() {
215 return transaction_.get() ?
216 transaction_->GetLoadState() : LOAD_STATE_IDLE;
217 }
218
219 uint64 URLRequestHttpJob::GetUploadProgress() const {
220 return transaction_.get() ? transaction_->GetUploadProgress() : 0;
221 }
222
223 bool URLRequestHttpJob::GetMimeType(std::string* mime_type) const {
224 DCHECK(transaction_.get()); 196 DCHECK(transaction_.get());
225 197
226 if (!response_info_) 198 transaction_.reset();
227 return false; 199 response_info_ = NULL;
228 200 context_ = NULL;
229 return response_info_->headers->GetMimeType(mime_type); 201 }
230 } 202
231 203 void URLRequestHttpJob::StartTransaction() {
232 bool URLRequestHttpJob::GetCharset(std::string* charset) { 204 // NOTE: This method assumes that request_info_ is already setup properly.
233 DCHECK(transaction_.get()); 205
234 206 // If we already have a transaction, then we should restart the transaction
235 if (!response_info_) 207 // with auth provided by username_ and password_.
236 return false; 208
237 209 int rv;
238 return response_info_->headers->GetCharset(charset); 210
239 } 211 if (transaction_.get()) {
240 212 rv = transaction_->RestartWithAuth(username_, password_, &start_callback_);
241 void URLRequestHttpJob::GetResponseInfo(HttpResponseInfo* info) { 213 username_.clear();
242 DCHECK(request_); 214 password_.clear();
243 DCHECK(transaction_.get());
244
245 if (response_info_)
246 *info = *response_info_;
247 }
248
249 bool URLRequestHttpJob::GetResponseCookies(
250 std::vector<std::string>* cookies) {
251 DCHECK(transaction_.get());
252
253 if (!response_info_)
254 return false;
255
256 // TODO(darin): Why are we extracting response cookies again? Perhaps we
257 // should just leverage response_cookies_.
258
259 cookies->clear();
260 FetchResponseCookies(response_info_, cookies);
261 return true;
262 }
263
264 int URLRequestHttpJob::GetResponseCode() const {
265 DCHECK(transaction_.get());
266
267 if (!response_info_)
268 return -1;
269
270 return response_info_->headers->response_code();
271 }
272
273 bool URLRequestHttpJob::GetContentEncodings(
274 std::vector<Filter::FilterType>* encoding_types) {
275 DCHECK(transaction_.get());
276 if (!response_info_)
277 return false;
278 DCHECK(encoding_types->empty());
279
280 std::string encoding_type;
281 void* iter = NULL;
282 while (response_info_->headers->EnumerateHeader(&iter, "Content-Encoding",
283 &encoding_type)) {
284 encoding_types->push_back(Filter::ConvertEncodingToType(encoding_type));
285 }
286
287 // Even if encoding types are empty, there is a chance that we need to add
288 // some decoding, as some proxies strip encoding completely. In such cases,
289 // we may need to add (for example) SDCH filtering (when the context suggests
290 // it is appropriate).
291 Filter::FixupEncodingTypes(*this, encoding_types);
292
293 return !encoding_types->empty();
294 }
295
296 bool URLRequestHttpJob::IsCachedContent() const {
297 return is_cached_content_;
298 }
299
300 bool URLRequestHttpJob::IsSdchResponse() const {
301 return sdch_dictionary_advertised_;
302 }
303
304 bool URLRequestHttpJob::IsSafeRedirect(const GURL& location) {
305 // We only allow redirects to certain "safe" protocols. This does not
306 // restrict redirects to externally handled protocols. Our consumer would
307 // need to take care of those.
308
309 if (!URLRequest::IsHandledURL(location))
310 return true;
311
312 static const char* kSafeSchemes[] = {
313 "http",
314 "https",
315 "ftp"
316 };
317
318 for (size_t i = 0; i < arraysize(kSafeSchemes); ++i) {
319 if (location.SchemeIs(kSafeSchemes[i]))
320 return true;
321 }
322
323 return false;
324 }
325
326 bool URLRequestHttpJob::NeedsAuth() {
327 int code = GetResponseCode();
328 if (code == -1)
329 return false;
330
331 // Check if we need either Proxy or WWW Authentication. This could happen
332 // because we either provided no auth info, or provided incorrect info.
333 switch (code) {
334 case 407:
335 if (proxy_auth_state_ == AUTH_STATE_CANCELED)
336 return false;
337 proxy_auth_state_ = AUTH_STATE_NEED_AUTH;
338 return true;
339 case 401:
340 if (server_auth_state_ == AUTH_STATE_CANCELED)
341 return false;
342 server_auth_state_ = AUTH_STATE_NEED_AUTH;
343 return true;
344 }
345 return false;
346 }
347
348 void URLRequestHttpJob::GetAuthChallengeInfo(
349 scoped_refptr<AuthChallengeInfo>* result) {
350 DCHECK(transaction_.get());
351 DCHECK(response_info_);
352
353 // sanity checks:
354 DCHECK(proxy_auth_state_ == AUTH_STATE_NEED_AUTH ||
355 server_auth_state_ == AUTH_STATE_NEED_AUTH);
356 DCHECK(response_info_->headers->response_code() == 401 ||
357 response_info_->headers->response_code() == 407);
358
359 *result = response_info_->auth_challenge;
360 }
361
362 void URLRequestHttpJob::SetAuth(const string16& username,
363 const string16& password) {
364 DCHECK(transaction_.get());
365
366 // Proxy gets set first, then WWW.
367 if (proxy_auth_state_ == AUTH_STATE_NEED_AUTH) {
368 proxy_auth_state_ = AUTH_STATE_HAVE_AUTH;
369 } else { 215 } else {
370 DCHECK(server_auth_state_ == AUTH_STATE_NEED_AUTH); 216 DCHECK(request_->context());
371 server_auth_state_ = AUTH_STATE_HAVE_AUTH; 217 DCHECK(request_->context()->http_transaction_factory());
372 } 218
373 219 rv = request_->context()->http_transaction_factory()->CreateTransaction(
374 RestartTransactionWithAuth(username, password); 220 &transaction_);
375 } 221 if (rv == OK) {
376 222 if (!throttling_entry_->IsDuringExponentialBackoff() ||
377 void URLRequestHttpJob::RestartTransactionWithAuth( 223 !net::URLRequestThrottlerManager::GetInstance()->
378 const string16& username, 224 enforce_throttling()) {
379 const string16& password) { 225 rv = transaction_->Start(
380 username_ = username; 226 &request_info_, &start_callback_, request_->net_log());
381 password_ = password; 227 } else {
382 228 // Special error code for the exponential back-off module.
383 // These will be reset in OnStartCompleted. 229 rv = ERR_TEMPORARILY_THROTTLED;
384 response_info_ = NULL; 230 }
385 response_cookies_.clear(); 231 // Make sure the context is alive for the duration of the
386 232 // transaction.
387 // Update the cookies, since the cookie store may have been updated from the 233 context_ = request_->context();
388 // headers in the 401/407. Since cookies were already appended to 234 }
389 // extra_headers, we need to strip them out before adding them again. 235 }
390 request_info_.extra_headers.RemoveHeader( 236
391 HttpRequestHeaders::kCookie);
392
393 AddCookieHeaderAndStart();
394 }
395
396 void URLRequestHttpJob::CancelAuth() {
397 // Proxy gets set first, then WWW.
398 if (proxy_auth_state_ == AUTH_STATE_NEED_AUTH) {
399 proxy_auth_state_ = AUTH_STATE_CANCELED;
400 } else {
401 DCHECK(server_auth_state_ == AUTH_STATE_NEED_AUTH);
402 server_auth_state_ = AUTH_STATE_CANCELED;
403 }
404
405 // These will be reset in OnStartCompleted.
406 response_info_ = NULL;
407 response_cookies_.clear();
408
409 // OK, let the consumer read the error page...
410 //
411 // Because we set the AUTH_STATE_CANCELED flag, NeedsAuth will return false,
412 // which will cause the consumer to receive OnResponseStarted instead of
413 // OnAuthRequired.
414 //
415 // We have to do this via InvokeLater to avoid "recursing" the consumer.
416 //
417 MessageLoop::current()->PostTask(
418 FROM_HERE,
419 method_factory_.NewRunnableMethod(
420 &URLRequestHttpJob::OnStartCompleted, OK));
421 }
422
423 void URLRequestHttpJob::ContinueWithCertificate(
424 X509Certificate* client_cert) {
425 DCHECK(transaction_.get());
426
427 DCHECK(!response_info_) << "should not have a response yet";
428
429 // No matter what, we want to report our status as IO pending since we will
430 // be notifying our consumer asynchronously via OnStartCompleted.
431 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
432
433 int rv = transaction_->RestartWithCertificate(client_cert, &start_callback_);
434 if (rv == ERR_IO_PENDING) 237 if (rv == ERR_IO_PENDING)
435 return; 238 return;
436 239
437 // The transaction started synchronously, but we need to notify the 240 // The transaction started synchronously, but we need to notify the
438 // URLRequest delegate via the message loop. 241 // URLRequest delegate via the message loop.
439 MessageLoop::current()->PostTask( 242 MessageLoop::current()->PostTask(
440 FROM_HERE, 243 FROM_HERE,
441 method_factory_.NewRunnableMethod( 244 method_factory_.NewRunnableMethod(
442 &URLRequestHttpJob::OnStartCompleted, rv)); 245 &URLRequestHttpJob::OnStartCompleted, rv));
443 } 246 }
444 247
445 void URLRequestHttpJob::ContinueDespiteLastError() { 248 void URLRequestHttpJob::AddExtraHeaders() {
446 // If the transaction was destroyed, then the job was cancelled. 249 // TODO(jar): Consider optimizing away SDCH advertising bytes when the URL is
447 if (!transaction_.get()) 250 // probably an img or such (and SDCH encoding is not likely).
448 return; 251 bool advertise_sdch = SdchManager::Global() &&
449 252 SdchManager::Global()->IsInSupportedDomain(request_->url());
450 DCHECK(!response_info_) << "should not have a response yet"; 253 std::string avail_dictionaries;
451 254 if (advertise_sdch) {
255 SdchManager::Global()->GetAvailDictionaryList(request_->url(),
256 &avail_dictionaries);
257
258 // The AllowLatencyExperiment() is only true if we've successfully done a
259 // full SDCH compression recently in this browser session for this host.
260 // Note that for this path, there might be no applicable dictionaries, and
261 // hence we can't participate in the experiment.
262 if (!avail_dictionaries.empty() &&
263 SdchManager::Global()->AllowLatencyExperiment(request_->url())) {
264 // We are participating in the test (or control), and hence we'll
265 // eventually record statistics via either SDCH_EXPERIMENT_DECODE or
266 // SDCH_EXPERIMENT_HOLDBACK, and we'll need some packet timing data.
267 EnablePacketCounting(kSdchPacketHistogramCount);
268 if (base::RandDouble() < .01) {
269 sdch_test_control_ = true; // 1% probability.
270 advertise_sdch = false;
271 } else {
272 sdch_test_activated_ = true;
273 }
274 }
275 }
276
277 // Supply Accept-Encoding headers first so that it is more likely that they
278 // will be in the first transmitted packet. This can sometimes make it easier
279 // to filter and analyze the streams to assure that a proxy has not damaged
280 // these headers. Some proxies deliberately corrupt Accept-Encoding headers.
281 if (!advertise_sdch) {
282 // Tell the server what compression formats we support (other than SDCH).
283 request_info_.extra_headers.SetHeader(
284 HttpRequestHeaders::kAcceptEncoding, "gzip,deflate");
285 } else {
286 // Include SDCH in acceptable list.
287 request_info_.extra_headers.SetHeader(
288 HttpRequestHeaders::kAcceptEncoding, "gzip,deflate,sdch");
289 if (!avail_dictionaries.empty()) {
290 request_info_.extra_headers.SetHeader(
291 kAvailDictionaryHeader,
292 avail_dictionaries);
293 sdch_dictionary_advertised_ = true;
294 // Since we're tagging this transaction as advertising a dictionary, we'll
295 // definately employ an SDCH filter (or tentative sdch filter) when we get
296 // a response. When done, we'll record histograms via SDCH_DECODE or
297 // SDCH_PASSTHROUGH. Hence we need to record packet arrival times.
298 EnablePacketCounting(kSdchPacketHistogramCount);
299 }
300 }
301
302 URLRequestContext* context = request_->context();
303 if (context) {
304 // Only add default Accept-Language and Accept-Charset if the request
305 // didn't have them specified.
306 if (!request_info_.extra_headers.HasHeader(
307 HttpRequestHeaders::kAcceptLanguage)) {
308 request_info_.extra_headers.SetHeader(
309 HttpRequestHeaders::kAcceptLanguage,
310 context->accept_language());
311 }
312 if (!request_info_.extra_headers.HasHeader(
313 HttpRequestHeaders::kAcceptCharset)) {
314 request_info_.extra_headers.SetHeader(
315 HttpRequestHeaders::kAcceptCharset,
316 context->accept_charset());
317 }
318 }
319 }
320
321 void URLRequestHttpJob::AddCookieHeaderAndStart() {
452 // No matter what, we want to report our status as IO pending since we will 322 // No matter what, we want to report our status as IO pending since we will
453 // be notifying our consumer asynchronously via OnStartCompleted. 323 // be notifying our consumer asynchronously via OnStartCompleted.
454 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); 324 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
455 325
456 int rv = transaction_->RestartIgnoringLastError(&start_callback_); 326 AddRef(); // Balanced in OnCanGetCookiesCompleted
457 if (rv == ERR_IO_PENDING) 327
328 int policy = OK;
329
330 if (request_info_.load_flags & LOAD_DO_NOT_SEND_COOKIES) {
331 policy = ERR_FAILED;
332 } else if (request_->context()->cookie_policy()) {
333 policy = request_->context()->cookie_policy()->CanGetCookies(
334 request_->url(),
335 request_->first_party_for_cookies(),
336 &can_get_cookies_callback_);
337 if (policy == ERR_IO_PENDING)
338 return; // Wait for completion callback
339 }
340
341 OnCanGetCookiesCompleted(policy);
342 }
343
344 void URLRequestHttpJob::SaveCookiesAndNotifyHeadersComplete() {
345 DCHECK(transaction_.get());
346
347 const HttpResponseInfo* response_info = transaction_->GetResponseInfo();
348 DCHECK(response_info);
349
350 response_cookies_.clear();
351 response_cookies_save_index_ = 0;
352
353 FetchResponseCookies(response_info, &response_cookies_);
354
355 // Now, loop over the response cookies, and attempt to persist each.
356 SaveNextCookie();
357 }
358
359 void URLRequestHttpJob::SaveNextCookie() {
360 if (response_cookies_save_index_ == response_cookies_.size()) {
361 response_cookies_.clear();
362 response_cookies_save_index_ = 0;
363 SetStatus(URLRequestStatus()); // Clear the IO_PENDING status
364 NotifyHeadersComplete();
458 return; 365 return;
459 366 }
460 // The transaction started synchronously, but we need to notify the 367
461 // URLRequest delegate via the message loop. 368 // No matter what, we want to report our status as IO pending since we will
462 MessageLoop::current()->PostTask( 369 // be notifying our consumer asynchronously via OnStartCompleted.
463 FROM_HERE, 370 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
464 method_factory_.NewRunnableMethod( 371
465 &URLRequestHttpJob::OnStartCompleted, rv)); 372 AddRef(); // Balanced in OnCanSetCookieCompleted
466 } 373
467 374 int policy = OK;
468 bool URLRequestHttpJob::ReadRawData(IOBuffer* buf, int buf_size, 375
469 int *bytes_read) { 376 if (request_info_.load_flags & LOAD_DO_NOT_SAVE_COOKIES) {
470 DCHECK_NE(buf_size, 0); 377 policy = ERR_FAILED;
471 DCHECK(bytes_read); 378 } else if (request_->context()->cookie_policy()) {
472 DCHECK(!read_in_progress_); 379 policy = request_->context()->cookie_policy()->CanSetCookie(
473 380 request_->url(),
474 int rv = transaction_->Read(buf, buf_size, &read_callback_); 381 request_->first_party_for_cookies(),
475 if (rv >= 0) { 382 response_cookies_[response_cookies_save_index_],
476 *bytes_read = rv; 383 &can_set_cookie_callback_);
477 return true; 384 if (policy == ERR_IO_PENDING)
478 } 385 return; // Wait for completion callback
479 386 }
480 if (rv == ERR_IO_PENDING) { 387
481 read_in_progress_ = true; 388 OnCanSetCookieCompleted(policy);
482 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); 389 }
483 } else { 390
484 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv)); 391 void URLRequestHttpJob::FetchResponseCookies(
485 } 392 const HttpResponseInfo* response_info,
486 393 std::vector<std::string>* cookies) {
487 return false; 394 std::string name = "Set-Cookie";
488 } 395 std::string value;
489 396
490 void URLRequestHttpJob::StopCaching() { 397 void* iter = NULL;
491 if (transaction_.get()) 398 while (response_info->headers->EnumerateHeader(&iter, name, &value)) {
492 transaction_->StopCaching(); 399 if (!value.empty())
400 cookies->push_back(value);
401 }
402 }
403
404 void URLRequestHttpJob::ProcessStrictTransportSecurityHeader() {
405 DCHECK(response_info_);
406
407 URLRequestContext* ctx = request_->context();
408 if (!ctx || !ctx->transport_security_state())
409 return;
410
411 const bool https = response_info_->ssl_info.is_valid();
412 const bool valid_https =
413 https && !IsCertStatusError(response_info_->ssl_info.cert_status);
414
415 std::string name = "Strict-Transport-Security";
416 std::string value;
417
418 int max_age;
419 bool include_subdomains;
420
421 void* iter = NULL;
422 while (response_info_->headers->EnumerateHeader(&iter, name, &value)) {
423 const bool ok = TransportSecurityState::ParseHeader(
424 value, &max_age, &include_subdomains);
425 if (!ok)
426 continue;
427 // We will only accept strict mode if we saw the header from an HTTPS
428 // connection with no certificate problems.
429 if (!valid_https)
430 continue;
431 base::Time current_time(base::Time::Now());
432 base::TimeDelta max_age_delta = base::TimeDelta::FromSeconds(max_age);
433
434 TransportSecurityState::DomainState domain_state;
435 domain_state.expiry = current_time + max_age_delta;
436 domain_state.mode = TransportSecurityState::DomainState::MODE_STRICT;
437 domain_state.include_subdomains = include_subdomains;
438
439 ctx->transport_security_state()->EnableHost(request_info_.url.host(),
440 domain_state);
441 }
442
443 // TODO(agl): change this over when we have fixed things at the server end.
444 // The string should be "Opportunistic-Transport-Security";
445 name = "X-Bodge-Transport-Security";
446
447 while (response_info_->headers->EnumerateHeader(&iter, name, &value)) {
448 const bool ok = TransportSecurityState::ParseHeader(
449 value, &max_age, &include_subdomains);
450 if (!ok)
451 continue;
452 // If we saw an opportunistic request over HTTPS, then clearly we can make
453 // HTTPS connections to the host so we should remember this.
454 if (https) {
455 base::Time current_time(base::Time::Now());
456 base::TimeDelta max_age_delta = base::TimeDelta::FromSeconds(max_age);
457
458 TransportSecurityState::DomainState domain_state;
459 domain_state.expiry = current_time + max_age_delta;
460 domain_state.mode =
461 TransportSecurityState::DomainState::MODE_SPDY_ONLY;
462 domain_state.include_subdomains = include_subdomains;
463
464 ctx->transport_security_state()->EnableHost(request_info_.url.host(),
465 domain_state);
466 continue;
467 }
468
469 if (!request())
470 break;
471
472 // At this point, we have a request for opportunistic encryption over HTTP.
473 // In this case we need to probe to check that we can make HTTPS
474 // connections to that host.
475 HTTPSProber* const prober = HTTPSProber::GetInstance();
476 if (prober->HaveProbed(request_info_.url.host()) ||
477 prober->InFlight(request_info_.url.host())) {
478 continue;
479 }
480
481 HTTPSProberDelegateImpl* delegate =
482 new HTTPSProberDelegateImpl(request_info_.url.host(), max_age,
483 include_subdomains,
484 ctx->transport_security_state());
485 if (!prober->ProbeHost(request_info_.url.host(), request()->context(),
486 delegate)) {
487 delete delegate;
488 }
489 }
493 } 490 }
494 491
495 void URLRequestHttpJob::OnCanGetCookiesCompleted(int policy) { 492 void URLRequestHttpJob::OnCanGetCookiesCompleted(int policy) {
496 // If the request was destroyed, then there is no more work to do. 493 // If the request was destroyed, then there is no more work to do.
497 if (request_ && request_->delegate()) { 494 if (request_ && request_->delegate()) {
498 if (request_->context()->cookie_store()) { 495 if (request_->context()->cookie_store()) {
499 if (policy == ERR_ACCESS_DENIED) { 496 if (policy == ERR_ACCESS_DENIED) {
500 request_->delegate()->OnGetCookies(request_, true); 497 request_->delegate()->OnGetCookies(request_, true);
501 } else if (policy == OK) { 498 } else if (policy == OK) {
502 request_->delegate()->OnGetCookies(request_, false); 499 request_->delegate()->OnGetCookies(request_, false);
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after
613 610
614 TransportSecurityState::DomainState domain_state; 611 TransportSecurityState::DomainState domain_state;
615 // TODO(agl): don't ignore opportunistic mode. 612 // TODO(agl): don't ignore opportunistic mode.
616 const bool r = context_->transport_security_state()->IsEnabledForHost( 613 const bool r = context_->transport_security_state()->IsEnabledForHost(
617 &domain_state, request_info_.url.host()); 614 &domain_state, request_info_.url.host());
618 615
619 return !r || domain_state.mode == 616 return !r || domain_state.mode ==
620 TransportSecurityState::DomainState::MODE_OPPORTUNISTIC; 617 TransportSecurityState::DomainState::MODE_OPPORTUNISTIC;
621 } 618 }
622 619
623 void URLRequestHttpJob::NotifyHeadersComplete() { 620 void URLRequestHttpJob::RestartTransactionWithAuth(
624 DCHECK(!response_info_); 621 const string16& username,
625 622 const string16& password) {
626 response_info_ = transaction_->GetResponseInfo(); 623 username_ = username;
627 624 password_ = password;
628 // Save boolean, as we'll need this info at destruction time, and filters may 625
629 // also need this info. 626 // These will be reset in OnStartCompleted.
630 is_cached_content_ = response_info_->was_cached; 627 response_info_ = NULL;
631 628 response_cookies_.clear();
632 if (!is_cached_content_) { 629
633 URLRequestThrottlerHeaderAdapter response_adapter( 630 // Update the cookies, since the cookie store may have been updated from the
634 response_info_->headers); 631 // headers in the 401/407. Since cookies were already appended to
635 throttling_entry_->UpdateWithResponse(&response_adapter); 632 // extra_headers, we need to strip them out before adding them again.
636 } 633 request_info_.extra_headers.RemoveHeader(
637 634 HttpRequestHeaders::kCookie);
638 ProcessStrictTransportSecurityHeader(); 635
639 636 AddCookieHeaderAndStart();
640 if (SdchManager::Global() && 637 }
641 SdchManager::Global()->IsInSupportedDomain(request_->url())) { 638
642 static const std::string name = "Get-Dictionary"; 639 void URLRequestHttpJob::SetUpload(UploadData* upload) {
643 std::string url_text; 640 DCHECK(!transaction_.get()) << "cannot change once started";
644 void* iter = NULL; 641 request_info_.upload_data = upload;
645 // TODO(jar): We need to not fetch dictionaries the first time they are 642 }
646 // seen, but rather wait until we can justify their usefulness. 643
647 // For now, we will only fetch the first dictionary, which will at least 644 void URLRequestHttpJob::SetExtraRequestHeaders(
648 // require multiple suggestions before we get additional ones for this site. 645 const HttpRequestHeaders& headers) {
649 // Eventually we should wait until a dictionary is requested several times 646 DCHECK(!transaction_.get()) << "cannot change once started";
650 // before we even download it (so that we don't waste memory or bandwidth). 647 request_info_.extra_headers.CopyFrom(headers);
651 if (response_info_->headers->EnumerateHeader(&iter, name, &url_text)) { 648 }
652 // request_->url() won't be valid in the destructor, so we use an 649
653 // alternate copy. 650 void URLRequestHttpJob::Start() {
654 DCHECK(request_->url() == request_info_.url); 651 DCHECK(!transaction_.get());
655 // Resolve suggested URL relative to request url. 652
656 sdch_dictionary_url_ = request_info_.url.Resolve(url_text); 653 // Ensure that we do not send username and password fields in the referrer.
657 } 654 GURL referrer(request_->GetSanitizedReferrer());
658 } 655
659 656 request_info_.url = request_->url();
660 // The HTTP transaction may be restarted several times for the purposes 657 request_info_.referrer = referrer;
661 // of sending authorization information. Each time it restarts, we get 658 request_info_.method = request_->method();
662 // notified of the headers completion so that we can update the cookie store. 659 request_info_.load_flags = request_->load_flags();
663 if (transaction_->IsReadyToRestartForAuth()) { 660 request_info_.priority = request_->priority();
664 DCHECK(!response_info_->auth_challenge.get()); 661
665 RestartTransactionWithAuth(string16(), string16()); 662 if (request_->context()) {
663 request_info_.extra_headers.SetHeader(
664 HttpRequestHeaders::kUserAgent,
665 request_->context()->GetUserAgent(request_->url()));
666 }
667
668 AddExtraHeaders();
669 AddCookieHeaderAndStart();
670 }
671
672 void URLRequestHttpJob::Kill() {
673 if (!transaction_.get())
666 return; 674 return;
667 } 675
668 676 DestroyTransaction();
669 URLRequestJob::NotifyHeadersComplete(); 677 URLRequestJob::Kill();
670 } 678 }
671 679
672 void URLRequestHttpJob::DestroyTransaction() { 680 LoadState URLRequestHttpJob::GetLoadState() const {
673 DCHECK(transaction_.get()); 681 return transaction_.get() ?
674 682 transaction_->GetLoadState() : LOAD_STATE_IDLE;
675 transaction_.reset(); 683 }
684
685 uint64 URLRequestHttpJob::GetUploadProgress() const {
686 return transaction_.get() ? transaction_->GetUploadProgress() : 0;
687 }
688
689 bool URLRequestHttpJob::GetMimeType(std::string* mime_type) const {
690 DCHECK(transaction_.get());
691
692 if (!response_info_)
693 return false;
694
695 return response_info_->headers->GetMimeType(mime_type);
696 }
697
698 bool URLRequestHttpJob::GetCharset(std::string* charset) {
699 DCHECK(transaction_.get());
700
701 if (!response_info_)
702 return false;
703
704 return response_info_->headers->GetCharset(charset);
705 }
706
707 void URLRequestHttpJob::GetResponseInfo(HttpResponseInfo* info) {
708 DCHECK(request_);
709 DCHECK(transaction_.get());
710
711 if (response_info_)
712 *info = *response_info_;
713 }
714
715 bool URLRequestHttpJob::GetResponseCookies(
716 std::vector<std::string>* cookies) {
717 DCHECK(transaction_.get());
718
719 if (!response_info_)
720 return false;
721
722 // TODO(darin): Why are we extracting response cookies again? Perhaps we
723 // should just leverage response_cookies_.
724
725 cookies->clear();
726 FetchResponseCookies(response_info_, cookies);
727 return true;
728 }
729
730 int URLRequestHttpJob::GetResponseCode() const {
731 DCHECK(transaction_.get());
732
733 if (!response_info_)
734 return -1;
735
736 return response_info_->headers->response_code();
737 }
738
739 bool URLRequestHttpJob::GetContentEncodings(
740 std::vector<Filter::FilterType>* encoding_types) {
741 DCHECK(transaction_.get());
742 if (!response_info_)
743 return false;
744 DCHECK(encoding_types->empty());
745
746 std::string encoding_type;
747 void* iter = NULL;
748 while (response_info_->headers->EnumerateHeader(&iter, "Content-Encoding",
749 &encoding_type)) {
750 encoding_types->push_back(Filter::ConvertEncodingToType(encoding_type));
751 }
752
753 // Even if encoding types are empty, there is a chance that we need to add
754 // some decoding, as some proxies strip encoding completely. In such cases,
755 // we may need to add (for example) SDCH filtering (when the context suggests
756 // it is appropriate).
757 Filter::FixupEncodingTypes(*this, encoding_types);
758
759 return !encoding_types->empty();
760 }
761
762 bool URLRequestHttpJob::IsCachedContent() const {
763 return is_cached_content_;
764 }
765
766 bool URLRequestHttpJob::IsSdchResponse() const {
767 return sdch_dictionary_advertised_;
768 }
769
770 bool URLRequestHttpJob::IsSafeRedirect(const GURL& location) {
771 // We only allow redirects to certain "safe" protocols. This does not
772 // restrict redirects to externally handled protocols. Our consumer would
773 // need to take care of those.
774
775 if (!URLRequest::IsHandledURL(location))
776 return true;
777
778 static const char* kSafeSchemes[] = {
779 "http",
780 "https",
781 "ftp"
782 };
783
784 for (size_t i = 0; i < arraysize(kSafeSchemes); ++i) {
785 if (location.SchemeIs(kSafeSchemes[i]))
786 return true;
787 }
788
789 return false;
790 }
791
792 bool URLRequestHttpJob::NeedsAuth() {
793 int code = GetResponseCode();
794 if (code == -1)
795 return false;
796
797 // Check if we need either Proxy or WWW Authentication. This could happen
798 // because we either provided no auth info, or provided incorrect info.
799 switch (code) {
800 case 407:
801 if (proxy_auth_state_ == AUTH_STATE_CANCELED)
802 return false;
803 proxy_auth_state_ = AUTH_STATE_NEED_AUTH;
804 return true;
805 case 401:
806 if (server_auth_state_ == AUTH_STATE_CANCELED)
807 return false;
808 server_auth_state_ = AUTH_STATE_NEED_AUTH;
809 return true;
810 }
811 return false;
812 }
813
814 void URLRequestHttpJob::GetAuthChallengeInfo(
815 scoped_refptr<AuthChallengeInfo>* result) {
816 DCHECK(transaction_.get());
817 DCHECK(response_info_);
818
819 // sanity checks:
820 DCHECK(proxy_auth_state_ == AUTH_STATE_NEED_AUTH ||
821 server_auth_state_ == AUTH_STATE_NEED_AUTH);
822 DCHECK(response_info_->headers->response_code() == 401 ||
823 response_info_->headers->response_code() == 407);
824
825 *result = response_info_->auth_challenge;
826 }
827
828 void URLRequestHttpJob::SetAuth(const string16& username,
829 const string16& password) {
830 DCHECK(transaction_.get());
831
832 // Proxy gets set first, then WWW.
833 if (proxy_auth_state_ == AUTH_STATE_NEED_AUTH) {
834 proxy_auth_state_ = AUTH_STATE_HAVE_AUTH;
835 } else {
836 DCHECK(server_auth_state_ == AUTH_STATE_NEED_AUTH);
837 server_auth_state_ = AUTH_STATE_HAVE_AUTH;
838 }
839
840 RestartTransactionWithAuth(username, password);
841 }
842
843 void URLRequestHttpJob::CancelAuth() {
844 // Proxy gets set first, then WWW.
845 if (proxy_auth_state_ == AUTH_STATE_NEED_AUTH) {
846 proxy_auth_state_ = AUTH_STATE_CANCELED;
847 } else {
848 DCHECK(server_auth_state_ == AUTH_STATE_NEED_AUTH);
849 server_auth_state_ = AUTH_STATE_CANCELED;
850 }
851
852 // These will be reset in OnStartCompleted.
676 response_info_ = NULL; 853 response_info_ = NULL;
677 context_ = NULL; 854 response_cookies_.clear();
678 } 855
679 856 // OK, let the consumer read the error page...
680 void URLRequestHttpJob::StartTransaction() { 857 //
681 // NOTE: This method assumes that request_info_ is already setup properly. 858 // Because we set the AUTH_STATE_CANCELED flag, NeedsAuth will return false,
682 859 // which will cause the consumer to receive OnResponseStarted instead of
683 // If we already have a transaction, then we should restart the transaction 860 // OnAuthRequired.
684 // with auth provided by username_ and password_. 861 //
685 862 // We have to do this via InvokeLater to avoid "recursing" the consumer.
686 int rv; 863 //
687 864 MessageLoop::current()->PostTask(
688 if (transaction_.get()) { 865 FROM_HERE,
689 rv = transaction_->RestartWithAuth(username_, password_, &start_callback_); 866 method_factory_.NewRunnableMethod(
690 username_.clear(); 867 &URLRequestHttpJob::OnStartCompleted, OK));
691 password_.clear(); 868 }
692 } else { 869
693 DCHECK(request_->context()); 870 void URLRequestHttpJob::ContinueWithCertificate(
694 DCHECK(request_->context()->http_transaction_factory()); 871 X509Certificate* client_cert) {
695 872 DCHECK(transaction_.get());
696 rv = request_->context()->http_transaction_factory()->CreateTransaction( 873
697 &transaction_); 874 DCHECK(!response_info_) << "should not have a response yet";
698 if (rv == OK) { 875
699 if (!throttling_entry_->IsDuringExponentialBackoff() || 876 // No matter what, we want to report our status as IO pending since we will
700 !net::URLRequestThrottlerManager::GetInstance()-> 877 // be notifying our consumer asynchronously via OnStartCompleted.
701 enforce_throttling()) { 878 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
702 rv = transaction_->Start( 879
703 &request_info_, &start_callback_, request_->net_log()); 880 int rv = transaction_->RestartWithCertificate(client_cert, &start_callback_);
704 } else {
705 // Special error code for the exponential back-off module.
706 rv = ERR_TEMPORARILY_THROTTLED;
707 }
708 // Make sure the context is alive for the duration of the
709 // transaction.
710 context_ = request_->context();
711 }
712 }
713
714 if (rv == ERR_IO_PENDING) 881 if (rv == ERR_IO_PENDING)
715 return; 882 return;
716 883
717 // The transaction started synchronously, but we need to notify the 884 // The transaction started synchronously, but we need to notify the
718 // URLRequest delegate via the message loop. 885 // URLRequest delegate via the message loop.
719 MessageLoop::current()->PostTask( 886 MessageLoop::current()->PostTask(
720 FROM_HERE, 887 FROM_HERE,
721 method_factory_.NewRunnableMethod( 888 method_factory_.NewRunnableMethod(
722 &URLRequestHttpJob::OnStartCompleted, rv)); 889 &URLRequestHttpJob::OnStartCompleted, rv));
723 } 890 }
724 891
725 void URLRequestHttpJob::AddExtraHeaders() { 892 void URLRequestHttpJob::ContinueDespiteLastError() {
726 // TODO(jar): Consider optimizing away SDCH advertising bytes when the URL is 893 // If the transaction was destroyed, then the job was cancelled.
727 // probably an img or such (and SDCH encoding is not likely). 894 if (!transaction_.get())
728 bool advertise_sdch = SdchManager::Global() && 895 return;
729 SdchManager::Global()->IsInSupportedDomain(request_->url()); 896
730 std::string avail_dictionaries; 897 DCHECK(!response_info_) << "should not have a response yet";
731 if (advertise_sdch) { 898
732 SdchManager::Global()->GetAvailDictionaryList(request_->url(),
733 &avail_dictionaries);
734
735 // The AllowLatencyExperiment() is only true if we've successfully done a
736 // full SDCH compression recently in this browser session for this host.
737 // Note that for this path, there might be no applicable dictionaries, and
738 // hence we can't participate in the experiment.
739 if (!avail_dictionaries.empty() &&
740 SdchManager::Global()->AllowLatencyExperiment(request_->url())) {
741 // We are participating in the test (or control), and hence we'll
742 // eventually record statistics via either SDCH_EXPERIMENT_DECODE or
743 // SDCH_EXPERIMENT_HOLDBACK, and we'll need some packet timing data.
744 EnablePacketCounting(kSdchPacketHistogramCount);
745 if (base::RandDouble() < .01) {
746 sdch_test_control_ = true; // 1% probability.
747 advertise_sdch = false;
748 } else {
749 sdch_test_activated_ = true;
750 }
751 }
752 }
753
754 // Supply Accept-Encoding headers first so that it is more likely that they
755 // will be in the first transmitted packet. This can sometimes make it easier
756 // to filter and analyze the streams to assure that a proxy has not damaged
757 // these headers. Some proxies deliberately corrupt Accept-Encoding headers.
758 if (!advertise_sdch) {
759 // Tell the server what compression formats we support (other than SDCH).
760 request_info_.extra_headers.SetHeader(
761 HttpRequestHeaders::kAcceptEncoding, "gzip,deflate");
762 } else {
763 // Include SDCH in acceptable list.
764 request_info_.extra_headers.SetHeader(
765 HttpRequestHeaders::kAcceptEncoding, "gzip,deflate,sdch");
766 if (!avail_dictionaries.empty()) {
767 request_info_.extra_headers.SetHeader(
768 kAvailDictionaryHeader,
769 avail_dictionaries);
770 sdch_dictionary_advertised_ = true;
771 // Since we're tagging this transaction as advertising a dictionary, we'll
772 // definately employ an SDCH filter (or tentative sdch filter) when we get
773 // a response. When done, we'll record histograms via SDCH_DECODE or
774 // SDCH_PASSTHROUGH. Hence we need to record packet arrival times.
775 EnablePacketCounting(kSdchPacketHistogramCount);
776 }
777 }
778
779 URLRequestContext* context = request_->context();
780 if (context) {
781 // Only add default Accept-Language and Accept-Charset if the request
782 // didn't have them specified.
783 if (!request_info_.extra_headers.HasHeader(
784 HttpRequestHeaders::kAcceptLanguage)) {
785 request_info_.extra_headers.SetHeader(
786 HttpRequestHeaders::kAcceptLanguage,
787 context->accept_language());
788 }
789 if (!request_info_.extra_headers.HasHeader(
790 HttpRequestHeaders::kAcceptCharset)) {
791 request_info_.extra_headers.SetHeader(
792 HttpRequestHeaders::kAcceptCharset,
793 context->accept_charset());
794 }
795 }
796 }
797
798 void URLRequestHttpJob::AddCookieHeaderAndStart() {
799 // No matter what, we want to report our status as IO pending since we will 899 // No matter what, we want to report our status as IO pending since we will
800 // be notifying our consumer asynchronously via OnStartCompleted. 900 // be notifying our consumer asynchronously via OnStartCompleted.
801 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); 901 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
802 902
803 AddRef(); // Balanced in OnCanGetCookiesCompleted 903 int rv = transaction_->RestartIgnoringLastError(&start_callback_);
804 904 if (rv == ERR_IO_PENDING)
805 int policy = OK;
806
807 if (request_info_.load_flags & LOAD_DO_NOT_SEND_COOKIES) {
808 policy = ERR_FAILED;
809 } else if (request_->context()->cookie_policy()) {
810 policy = request_->context()->cookie_policy()->CanGetCookies(
811 request_->url(),
812 request_->first_party_for_cookies(),
813 &can_get_cookies_callback_);
814 if (policy == ERR_IO_PENDING)
815 return; // Wait for completion callback
816 }
817
818 OnCanGetCookiesCompleted(policy);
819 }
820
821 void URLRequestHttpJob::SaveCookiesAndNotifyHeadersComplete() {
822 DCHECK(transaction_.get());
823
824 const HttpResponseInfo* response_info = transaction_->GetResponseInfo();
825 DCHECK(response_info);
826
827 response_cookies_.clear();
828 response_cookies_save_index_ = 0;
829
830 FetchResponseCookies(response_info, &response_cookies_);
831
832 // Now, loop over the response cookies, and attempt to persist each.
833 SaveNextCookie();
834 }
835
836 void URLRequestHttpJob::SaveNextCookie() {
837 if (response_cookies_save_index_ == response_cookies_.size()) {
838 response_cookies_.clear();
839 response_cookies_save_index_ = 0;
840 SetStatus(URLRequestStatus()); // Clear the IO_PENDING status
841 NotifyHeadersComplete();
842 return; 905 return;
843 } 906
844 907 // The transaction started synchronously, but we need to notify the
845 // No matter what, we want to report our status as IO pending since we will 908 // URLRequest delegate via the message loop.
846 // be notifying our consumer asynchronously via OnStartCompleted. 909 MessageLoop::current()->PostTask(
847 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); 910 FROM_HERE,
848 911 method_factory_.NewRunnableMethod(
849 AddRef(); // Balanced in OnCanSetCookieCompleted 912 &URLRequestHttpJob::OnStartCompleted, rv));
850 913 }
851 int policy = OK; 914
852 915 bool URLRequestHttpJob::ReadRawData(IOBuffer* buf, int buf_size,
853 if (request_info_.load_flags & LOAD_DO_NOT_SAVE_COOKIES) { 916 int *bytes_read) {
854 policy = ERR_FAILED; 917 DCHECK_NE(buf_size, 0);
855 } else if (request_->context()->cookie_policy()) { 918 DCHECK(bytes_read);
856 policy = request_->context()->cookie_policy()->CanSetCookie( 919 DCHECK(!read_in_progress_);
857 request_->url(), 920
858 request_->first_party_for_cookies(), 921 int rv = transaction_->Read(buf, buf_size, &read_callback_);
859 response_cookies_[response_cookies_save_index_], 922 if (rv >= 0) {
860 &can_set_cookie_callback_); 923 *bytes_read = rv;
861 if (policy == ERR_IO_PENDING) 924 return true;
862 return; // Wait for completion callback 925 }
863 } 926
864 927 if (rv == ERR_IO_PENDING) {
865 OnCanSetCookieCompleted(policy); 928 read_in_progress_ = true;
866 } 929 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
867 930 } else {
868 void URLRequestHttpJob::FetchResponseCookies( 931 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv));
869 const HttpResponseInfo* response_info, 932 }
870 std::vector<std::string>* cookies) { 933
871 std::string name = "Set-Cookie"; 934 return false;
872 std::string value; 935 }
873 936
874 void* iter = NULL; 937 void URLRequestHttpJob::StopCaching() {
875 while (response_info->headers->EnumerateHeader(&iter, name, &value)) { 938 if (transaction_.get())
876 if (!value.empty()) 939 transaction_->StopCaching();
877 cookies->push_back(value); 940 }
878 } 941
879 } 942 URLRequestHttpJob::~URLRequestHttpJob() {
880 943 DCHECK(!sdch_test_control_ || !sdch_test_activated_);
881 void URLRequestHttpJob::ProcessStrictTransportSecurityHeader() { 944 if (!IsCachedContent()) {
882 DCHECK(response_info_); 945 if (sdch_test_control_)
883 946 RecordPacketStats(SDCH_EXPERIMENT_HOLDBACK);
884 URLRequestContext* ctx = request_->context(); 947 if (sdch_test_activated_)
885 if (!ctx || !ctx->transport_security_state()) 948 RecordPacketStats(SDCH_EXPERIMENT_DECODE);
886 return; 949 }
887 950 // Make sure SDCH filters are told to emit histogram data while this class
888 const bool https = response_info_->ssl_info.is_valid(); 951 // can still service the IsCachedContent() call.
889 const bool valid_https = 952 DestroyFilters();
890 https && !IsCertStatusError(response_info_->ssl_info.cert_status); 953
891 954 if (sdch_dictionary_url_.is_valid()) {
892 std::string name = "Strict-Transport-Security"; 955 // Prior to reaching the destructor, request_ has been set to a NULL
893 std::string value; 956 // pointer, so request_->url() is no longer valid in the destructor, and we
894 957 // use an alternate copy |request_info_.url|.
895 int max_age; 958 SdchManager* manager = SdchManager::Global();
896 bool include_subdomains; 959 // To be extra safe, since this is a "different time" from when we decided
897 960 // to get the dictionary, we'll validate that an SdchManager is available.
898 void* iter = NULL; 961 // At shutdown time, care is taken to be sure that we don't delete this
899 while (response_info_->headers->EnumerateHeader(&iter, name, &value)) { 962 // globally useful instance "too soon," so this check is just defensive
900 const bool ok = TransportSecurityState::ParseHeader( 963 // coding to assure that IF the system is shutting down, we don't have any
901 value, &max_age, &include_subdomains); 964 // problem if the manager was deleted ahead of time.
902 if (!ok) 965 if (manager) // Defensive programming.
903 continue; 966 manager->FetchDictionary(request_info_.url, sdch_dictionary_url_);
904 // We will only accept strict mode if we saw the header from an HTTPS 967 }
905 // connection with no certificate problems. 968 }
906 if (!valid_https) 969
907 continue;
908 base::Time current_time(base::Time::Now());
909 base::TimeDelta max_age_delta = base::TimeDelta::FromSeconds(max_age);
910
911 TransportSecurityState::DomainState domain_state;
912 domain_state.expiry = current_time + max_age_delta;
913 domain_state.mode = TransportSecurityState::DomainState::MODE_STRICT;
914 domain_state.include_subdomains = include_subdomains;
915
916 ctx->transport_security_state()->EnableHost(request_info_.url.host(),
917 domain_state);
918 }
919
920 // TODO(agl): change this over when we have fixed things at the server end.
921 // The string should be "Opportunistic-Transport-Security";
922 name = "X-Bodge-Transport-Security";
923
924 while (response_info_->headers->EnumerateHeader(&iter, name, &value)) {
925 const bool ok = TransportSecurityState::ParseHeader(
926 value, &max_age, &include_subdomains);
927 if (!ok)
928 continue;
929 // If we saw an opportunistic request over HTTPS, then clearly we can make
930 // HTTPS connections to the host so we should remember this.
931 if (https) {
932 base::Time current_time(base::Time::Now());
933 base::TimeDelta max_age_delta = base::TimeDelta::FromSeconds(max_age);
934
935 TransportSecurityState::DomainState domain_state;
936 domain_state.expiry = current_time + max_age_delta;
937 domain_state.mode =
938 TransportSecurityState::DomainState::MODE_SPDY_ONLY;
939 domain_state.include_subdomains = include_subdomains;
940
941 ctx->transport_security_state()->EnableHost(request_info_.url.host(),
942 domain_state);
943 continue;
944 }
945
946 if (!request())
947 break;
948
949 // At this point, we have a request for opportunistic encryption over HTTP.
950 // In this case we need to probe to check that we can make HTTPS
951 // connections to that host.
952 HTTPSProber* const prober = HTTPSProber::GetInstance();
953 if (prober->HaveProbed(request_info_.url.host()) ||
954 prober->InFlight(request_info_.url.host())) {
955 continue;
956 }
957
958 HTTPSProberDelegateImpl* delegate =
959 new HTTPSProberDelegateImpl(request_info_.url.host(), max_age,
960 include_subdomains,
961 ctx->transport_security_state());
962 if (!prober->ProbeHost(request_info_.url.host(), request()->context(),
963 delegate)) {
964 delete delegate;
965 }
966 }
967 }
968
969 } // namespace net 970 } // namespace net
OLDNEW
« no previous file with comments | « net/url_request/url_request_http_job.h ('k') | net/url_request/url_request_job.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698