blob: d432f547fa2760c8754e0fa1ff7360b881b99699 [file] [log] [blame]
[email protected]0b45559b2009-06-12 21:45:111// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
license.botbf09a502008-08-24 00:55:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commit586acc5fe2008-07-26 22:42:524
[email protected]175adac2008-07-30 17:28:045#include "net/url_request/url_request_http_job.h"
initial.commit586acc5fe2008-07-26 22:42:526
[email protected]4ed2755f2008-12-15 09:01:337#include "base/base_switches.h"
8#include "base/command_line.h"
[email protected]39ce5c02008-08-22 04:03:449#include "base/compiler_specific.h"
[email protected]60889422008-09-23 01:18:1610#include "base/file_util.h"
11#include "base/file_version_info.h"
initial.commit586acc5fe2008-07-26 22:42:5212#include "base/message_loop.h"
[email protected]5b90b5d2009-04-30 23:06:0113#include "base/rand_util.h"
initial.commit586acc5fe2008-07-26 22:42:5214#include "base/string_util.h"
[email protected]a9cea7542009-05-20 04:30:2315#include "net/base/cert_status_flags.h"
[email protected]423041b2008-10-27 17:39:2816#include "net/base/filter.h"
[email protected]a9cea7542009-05-20 04:30:2317#include "net/base/force_tls_state.h"
[email protected]b8430722008-09-17 20:05:4418#include "net/base/load_flags.h"
initial.commit586acc5fe2008-07-26 22:42:5219#include "net/base/net_errors.h"
20#include "net/base/net_util.h"
[email protected]60889422008-09-23 01:18:1621#include "net/base/sdch_manager.h"
[email protected]0b45559b2009-06-12 21:45:1122#include "net/base/ssl_cert_request_info.h"
[email protected]319d9e6f2009-02-18 19:47:2123#include "net/http/http_response_headers.h"
initial.commit586acc5fe2008-07-26 22:42:5224#include "net/http/http_response_info.h"
25#include "net/http/http_transaction.h"
26#include "net/http/http_transaction_factory.h"
[email protected]0757e7702009-03-27 04:00:2227#include "net/http/http_util.h"
initial.commit586acc5fe2008-07-26 22:42:5228#include "net/url_request/url_request.h"
[email protected]319d9e6f2009-02-18 19:47:2129#include "net/url_request/url_request_context.h"
initial.commit586acc5fe2008-07-26 22:42:5230#include "net/url_request/url_request_error_job.h"
31
[email protected]bcb84f8b2009-08-31 16:20:1432// static
33std::set<int> URLRequestHttpJob::explicitly_allowed_ports_;
34
initial.commit586acc5fe2008-07-26 22:42:5235// TODO(darin): make sure the port blocking code is not lost
36
initial.commit586acc5fe2008-07-26 22:42:5237// static
[email protected]175adac2008-07-30 17:28:0438URLRequestJob* URLRequestHttpJob::Factory(URLRequest* request,
39 const std::string& scheme) {
initial.commit586acc5fe2008-07-26 22:42:5240 DCHECK(scheme == "http" || scheme == "https");
41
[email protected]bcb84f8b2009-08-31 16:20:1442 int port = request->url().IntPort();
43 if (!net::IsPortAllowedByDefault(port) && !IsPortAllowedByOverride(port))
initial.commit586acc5fe2008-07-26 22:42:5244 return new URLRequestErrorJob(request, net::ERR_UNSAFE_PORT);
45
46 if (!request->context() ||
47 !request->context()->http_transaction_factory()) {
48 NOTREACHED() << "requires a valid context";
49 return new URLRequestErrorJob(request, net::ERR_INVALID_ARGUMENT);
50 }
51
[email protected]4ed2755f2008-12-15 09:01:3352 // We cache the value of the switch because this code path is hit on every
53 // network request.
54 static const bool kForceHTTPS =
[email protected]bb975362009-01-21 01:00:2255 CommandLine::ForCurrentProcess()->HasSwitch(switches::kForceHTTPS);
[email protected]a9cea7542009-05-20 04:30:2356 if (kForceHTTPS && scheme == "http" &&
57 request->context()->force_tls_state() &&
58 request->context()->force_tls_state()->IsEnabledForHost(
59 request->url().host()))
[email protected]4ed2755f2008-12-15 09:01:3360 return new URLRequestErrorJob(request, net::ERR_DISALLOWED_URL_SCHEME);
61
[email protected]175adac2008-07-30 17:28:0462 return new URLRequestHttpJob(request);
initial.commit586acc5fe2008-07-26 22:42:5263}
64
[email protected]bcb84f8b2009-08-31 16:20:1465// static
66void URLRequestHttpJob::SetExplicitlyAllowedPorts(
67 const std::wstring& allowed_ports) {
68 if (allowed_ports.empty())
69 return;
70
71 std::set<int> ports;
72 size_t last = 0;
73 size_t size = allowed_ports.size();
74 // The comma delimiter.
75 const std::wstring::value_type kComma = L',';
76
77 // Overflow is still possible for evil user inputs.
78 for (size_t i = 0; i <= size; ++i) {
79 // The string should be composed of only digits and commas.
80 if (i != size && !IsAsciiDigit(allowed_ports[i]) &&
81 (allowed_ports[i] != kComma))
82 return;
83 if (i == size || allowed_ports[i] == kComma) {
84 size_t length = i - last;
85 if (length > 0)
86 ports.insert(StringToInt(WideToASCII(
87 allowed_ports.substr(last, length))));
88 last = i + 1;
89 }
90 }
91 explicitly_allowed_ports_ = ports;
92}
93
[email protected]175adac2008-07-30 17:28:0494URLRequestHttpJob::URLRequestHttpJob(URLRequest* request)
initial.commit586acc5fe2008-07-26 22:42:5295 : URLRequestJob(request),
[email protected]2aecf7382009-06-17 04:14:2796 context_(request->context()),
initial.commit586acc5fe2008-07-26 22:42:5297 response_info_(NULL),
[email protected]a9bb6f692008-07-30 16:40:1098 proxy_auth_state_(net::AUTH_STATE_DONT_NEED_AUTH),
99 server_auth_state_(net::AUTH_STATE_DONT_NEED_AUTH),
[email protected]39ce5c02008-08-22 04:03:44100 ALLOW_THIS_IN_INITIALIZER_LIST(
101 start_callback_(this, &URLRequestHttpJob::OnStartCompleted)),
102 ALLOW_THIS_IN_INITIALIZER_LIST(
103 read_callback_(this, &URLRequestHttpJob::OnReadCompleted)),
[email protected]3589e552008-08-20 23:11:34104 read_in_progress_(false),
[email protected]2aecf7382009-06-17 04:14:27105 transaction_(NULL),
[email protected]5b90b5d2009-04-30 23:06:01106 sdch_dictionary_advertised_(false),
107 sdch_test_activated_(false),
[email protected]d8fd5132009-05-15 01:06:53108 sdch_test_control_(false),
109 is_cached_content_(false) {
initial.commit586acc5fe2008-07-26 22:42:52110}
111
[email protected]175adac2008-07-30 17:28:04112URLRequestHttpJob::~URLRequestHttpJob() {
[email protected]5b90b5d2009-04-30 23:06:01113 DCHECK(!sdch_test_control_ || !sdch_test_activated_);
[email protected]d8fd5132009-05-15 01:06:53114 if (!IsCachedContent()) {
115 if (sdch_test_control_)
116 RecordPacketStats(SDCH_EXPERIMENT_HOLDBACK);
117 if (sdch_test_activated_)
118 RecordPacketStats(SDCH_EXPERIMENT_DECODE);
119 }
[email protected]284c373d42009-05-19 23:39:03120 // Make sure SDCH filters are told to emit histogram data while this class
121 // can still service the IsCachedContent() call.
122 DestroyFilters();
[email protected]5b90b5d2009-04-30 23:06:01123
[email protected]7234e6c2009-02-11 21:37:04124 if (sdch_dictionary_url_.is_valid()) {
[email protected]d55ad15d2009-02-17 19:40:50125 // Prior to reaching the destructor, request_ has been set to a NULL
126 // pointer, so request_->url() is no longer valid in the destructor, and we
127 // use an alternate copy |request_info_.url|.
[email protected]a41fae82009-02-21 06:11:45128 SdchManager* manager = SdchManager::Global();
129 // To be extra safe, since this is a "different time" from when we decided
130 // to get the dictionary, we'll validate that an SdchManager is available.
131 // At shutdown time, care is taken to be sure that we don't delete this
132 // globally useful instance "too soon," so this check is just defensive
133 // coding to assure that IF the system is shutting down, we don't have any
134 // problem if the manager was deleted ahead of time.
135 if (manager) // Defensive programming.
136 manager->FetchDictionary(request_info_.url, sdch_dictionary_url_);
[email protected]7234e6c2009-02-11 21:37:04137 }
initial.commit586acc5fe2008-07-26 22:42:52138}
139
[email protected]175adac2008-07-30 17:28:04140void URLRequestHttpJob::SetUpload(net::UploadData* upload) {
[email protected]af4876d2008-10-21 23:10:57141 DCHECK(!transaction_.get()) << "cannot change once started";
initial.commit586acc5fe2008-07-26 22:42:52142 request_info_.upload_data = upload;
143}
144
[email protected]175adac2008-07-30 17:28:04145void URLRequestHttpJob::SetExtraRequestHeaders(
initial.commit586acc5fe2008-07-26 22:42:52146 const std::string& headers) {
[email protected]af4876d2008-10-21 23:10:57147 DCHECK(!transaction_.get()) << "cannot change once started";
initial.commit586acc5fe2008-07-26 22:42:52148 request_info_.extra_headers = headers;
149}
150
[email protected]175adac2008-07-30 17:28:04151void URLRequestHttpJob::Start() {
[email protected]af4876d2008-10-21 23:10:57152 DCHECK(!transaction_.get());
initial.commit586acc5fe2008-07-26 22:42:52153
initial.commit586acc5fe2008-07-26 22:42:52154 // Ensure that we do not send username and password fields in the referrer.
[email protected]e600c822009-08-31 16:57:08155 GURL referrer(request_->GetSanitizedReferrer());
initial.commit586acc5fe2008-07-26 22:42:52156
157 request_info_.url = request_->url();
158 request_info_.referrer = referrer;
159 request_info_.method = request_->method();
160 request_info_.load_flags = request_->load_flags();
[email protected]725355a2009-03-25 20:42:55161 request_info_.priority = request_->priority();
initial.commit586acc5fe2008-07-26 22:42:52162
[email protected]6f681a42009-01-27 22:28:54163 if (request_->context()) {
164 request_info_.user_agent =
165 request_->context()->GetUserAgent(request_->url());
166 }
initial.commit586acc5fe2008-07-26 22:42:52167
168 AddExtraHeaders();
169
170 StartTransaction();
171}
172
[email protected]175adac2008-07-30 17:28:04173void URLRequestHttpJob::Kill() {
[email protected]af4876d2008-10-21 23:10:57174 if (!transaction_.get())
initial.commit586acc5fe2008-07-26 22:42:52175 return;
176
177 DestroyTransaction();
178 URLRequestJob::Kill();
179}
180
[email protected]175adac2008-07-30 17:28:04181net::LoadState URLRequestHttpJob::GetLoadState() const {
[email protected]af4876d2008-10-21 23:10:57182 return transaction_.get() ?
183 transaction_->GetLoadState() : net::LOAD_STATE_IDLE;
initial.commit586acc5fe2008-07-26 22:42:52184}
185
[email protected]175adac2008-07-30 17:28:04186uint64 URLRequestHttpJob::GetUploadProgress() const {
[email protected]af4876d2008-10-21 23:10:57187 return transaction_.get() ? transaction_->GetUploadProgress() : 0;
initial.commit586acc5fe2008-07-26 22:42:52188}
189
[email protected]60c413c92009-03-09 16:53:31190bool URLRequestHttpJob::GetMimeType(std::string* mime_type) const {
[email protected]af4876d2008-10-21 23:10:57191 DCHECK(transaction_.get());
initial.commit586acc5fe2008-07-26 22:42:52192
193 if (!response_info_)
194 return false;
195
196 return response_info_->headers->GetMimeType(mime_type);
197}
198
[email protected]175adac2008-07-30 17:28:04199bool URLRequestHttpJob::GetCharset(std::string* charset) {
[email protected]af4876d2008-10-21 23:10:57200 DCHECK(transaction_.get());
initial.commit586acc5fe2008-07-26 22:42:52201
202 if (!response_info_)
203 return false;
204
205 return response_info_->headers->GetCharset(charset);
206}
207
[email protected]175adac2008-07-30 17:28:04208void URLRequestHttpJob::GetResponseInfo(net::HttpResponseInfo* info) {
initial.commit586acc5fe2008-07-26 22:42:52209 DCHECK(request_);
[email protected]af4876d2008-10-21 23:10:57210 DCHECK(transaction_.get());
initial.commit586acc5fe2008-07-26 22:42:52211
212 if (response_info_)
213 *info = *response_info_;
214}
215
[email protected]175adac2008-07-30 17:28:04216bool URLRequestHttpJob::GetResponseCookies(
initial.commit586acc5fe2008-07-26 22:42:52217 std::vector<std::string>* cookies) {
[email protected]af4876d2008-10-21 23:10:57218 DCHECK(transaction_.get());
initial.commit586acc5fe2008-07-26 22:42:52219
220 if (!response_info_)
221 return false;
222
223 if (response_cookies_.empty())
224 FetchResponseCookies();
225
226 cookies->clear();
227 cookies->swap(response_cookies_);
228 return true;
229}
230
[email protected]84973ad2009-03-30 18:05:43231int URLRequestHttpJob::GetResponseCode() const {
[email protected]af4876d2008-10-21 23:10:57232 DCHECK(transaction_.get());
initial.commit586acc5fe2008-07-26 22:42:52233
234 if (!response_info_)
235 return -1;
236
237 return response_info_->headers->response_code();
238}
239
[email protected]60889422008-09-23 01:18:16240bool URLRequestHttpJob::GetContentEncodings(
[email protected]423041b2008-10-27 17:39:28241 std::vector<Filter::FilterType>* encoding_types) {
[email protected]af4876d2008-10-21 23:10:57242 DCHECK(transaction_.get());
initial.commit586acc5fe2008-07-26 22:42:52243 if (!response_info_)
244 return false;
[email protected]423041b2008-10-27 17:39:28245 DCHECK(encoding_types->empty());
initial.commit586acc5fe2008-07-26 22:42:52246
[email protected]60889422008-09-23 01:18:16247 std::string encoding_type;
248 void* iter = NULL;
249 while (response_info_->headers->EnumerateHeader(&iter, "Content-Encoding",
250 &encoding_type)) {
[email protected]423041b2008-10-27 17:39:28251 encoding_types->push_back(Filter::ConvertEncodingToType(encoding_type));
[email protected]60889422008-09-23 01:18:16252 }
[email protected]c631b6aa2008-10-15 21:21:37253
[email protected]77e9fcf2009-03-28 01:45:58254 // Even if encoding types are empty, there is a chance that we need to add
255 // some decoding, as some proxies strip encoding completely. In such cases,
256 // we may need to add (for example) SDCH filtering (when the context suggests
257 // it is appropriate).
258 Filter::FixupEncodingTypes(*this, encoding_types);
259
[email protected]60889422008-09-23 01:18:16260 return !encoding_types->empty();
initial.commit586acc5fe2008-07-26 22:42:52261}
262
[email protected]c631b6aa2008-10-15 21:21:37263bool URLRequestHttpJob::IsSdchResponse() const {
[email protected]5b90b5d2009-04-30 23:06:01264 return sdch_dictionary_advertised_;
[email protected]c631b6aa2008-10-15 21:21:37265}
266
[email protected]175adac2008-07-30 17:28:04267bool URLRequestHttpJob::IsSafeRedirect(const GURL& location) {
initial.commit586acc5fe2008-07-26 22:42:52268 // We only allow redirects to certain "safe" protocols. This does not
269 // restrict redirects to externally handled protocols. Our consumer would
270 // need to take care of those.
271
272 if (!URLRequest::IsHandledURL(location))
273 return true;
274
275 static const char* kSafeSchemes[] = {
276 "http",
277 "https",
278 "ftp"
279 };
280
281 for (size_t i = 0; i < arraysize(kSafeSchemes); ++i) {
282 if (location.SchemeIs(kSafeSchemes[i]))
283 return true;
284 }
285
286 return false;
287}
288
[email protected]175adac2008-07-30 17:28:04289bool URLRequestHttpJob::NeedsAuth() {
initial.commit586acc5fe2008-07-26 22:42:52290 int code = GetResponseCode();
291 if (code == -1)
292 return false;
293
294 // Check if we need either Proxy or WWW Authentication. This could happen
295 // because we either provided no auth info, or provided incorrect info.
296 switch (code) {
297 case 407:
[email protected]a9bb6f692008-07-30 16:40:10298 if (proxy_auth_state_ == net::AUTH_STATE_CANCELED)
initial.commit586acc5fe2008-07-26 22:42:52299 return false;
[email protected]a9bb6f692008-07-30 16:40:10300 proxy_auth_state_ = net::AUTH_STATE_NEED_AUTH;
initial.commit586acc5fe2008-07-26 22:42:52301 return true;
302 case 401:
[email protected]a9bb6f692008-07-30 16:40:10303 if (server_auth_state_ == net::AUTH_STATE_CANCELED)
initial.commit586acc5fe2008-07-26 22:42:52304 return false;
[email protected]a9bb6f692008-07-30 16:40:10305 server_auth_state_ = net::AUTH_STATE_NEED_AUTH;
initial.commit586acc5fe2008-07-26 22:42:52306 return true;
307 }
308 return false;
309}
310
[email protected]175adac2008-07-30 17:28:04311void URLRequestHttpJob::GetAuthChallengeInfo(
[email protected]a9bb6f692008-07-30 16:40:10312 scoped_refptr<net::AuthChallengeInfo>* result) {
[email protected]af4876d2008-10-21 23:10:57313 DCHECK(transaction_.get());
initial.commit586acc5fe2008-07-26 22:42:52314 DCHECK(response_info_);
315
316 // sanity checks:
[email protected]a9bb6f692008-07-30 16:40:10317 DCHECK(proxy_auth_state_ == net::AUTH_STATE_NEED_AUTH ||
318 server_auth_state_ == net::AUTH_STATE_NEED_AUTH);
initial.commit586acc5fe2008-07-26 22:42:52319 DCHECK(response_info_->headers->response_code() == 401 ||
320 response_info_->headers->response_code() == 407);
321
322 *result = response_info_->auth_challenge;
323}
324
[email protected]175adac2008-07-30 17:28:04325void URLRequestHttpJob::SetAuth(const std::wstring& username,
326 const std::wstring& password) {
[email protected]af4876d2008-10-21 23:10:57327 DCHECK(transaction_.get());
initial.commit586acc5fe2008-07-26 22:42:52328
329 // Proxy gets set first, then WWW.
[email protected]a9bb6f692008-07-30 16:40:10330 if (proxy_auth_state_ == net::AUTH_STATE_NEED_AUTH) {
331 proxy_auth_state_ = net::AUTH_STATE_HAVE_AUTH;
initial.commit586acc5fe2008-07-26 22:42:52332 } else {
[email protected]a9bb6f692008-07-30 16:40:10333 DCHECK(server_auth_state_ == net::AUTH_STATE_NEED_AUTH);
334 server_auth_state_ = net::AUTH_STATE_HAVE_AUTH;
initial.commit586acc5fe2008-07-26 22:42:52335 }
336
[email protected]0757e7702009-03-27 04:00:22337 RestartTransactionWithAuth(username, password);
338}
339
340void URLRequestHttpJob::RestartTransactionWithAuth(
341 const std::wstring& username,
342 const std::wstring& password) {
343
initial.commit586acc5fe2008-07-26 22:42:52344 // These will be reset in OnStartCompleted.
345 response_info_ = NULL;
346 response_cookies_.clear();
347
[email protected]0757e7702009-03-27 04:00:22348 // Update the cookies, since the cookie store may have been updated from the
349 // headers in the 401/407. Since cookies were already appended to
350 // extra_headers by AddExtraHeaders(), we need to strip them out.
351 static const char* const cookie_name[] = { "cookie" };
352 request_info_.extra_headers = net::HttpUtil::StripHeaders(
353 request_info_.extra_headers, cookie_name, arraysize(cookie_name));
354 // TODO(eroman): this ordering is inconsistent with non-restarted request,
355 // where cookies header appears second from the bottom.
356 request_info_.extra_headers += AssembleRequestCookies();
357
initial.commit586acc5fe2008-07-26 22:42:52358 // No matter what, we want to report our status as IO pending since we will
359 // be notifying our consumer asynchronously via OnStartCompleted.
360 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
361
362 int rv = transaction_->RestartWithAuth(username, password,
363 &start_callback_);
364 if (rv == net::ERR_IO_PENDING)
365 return;
366
367 // The transaction started synchronously, but we need to notify the
368 // URLRequest delegate via the message loop.
369 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
[email protected]175adac2008-07-30 17:28:04370 this, &URLRequestHttpJob::OnStartCompleted, rv));
initial.commit586acc5fe2008-07-26 22:42:52371}
372
[email protected]bcb84f8b2009-08-31 16:20:14373// static
374bool URLRequestHttpJob::IsPortAllowedByOverride(int port) {
375 if (explicitly_allowed_ports().empty())
376 return false;
377
378 std::set<int>::const_iterator it =
379 std::find(explicitly_allowed_ports().begin(),
380 explicitly_allowed_ports().end(),
381 port);
382
383 return it != explicitly_allowed_ports().end();
384}
385
[email protected]175adac2008-07-30 17:28:04386void URLRequestHttpJob::CancelAuth() {
initial.commit586acc5fe2008-07-26 22:42:52387 // Proxy gets set first, then WWW.
[email protected]a9bb6f692008-07-30 16:40:10388 if (proxy_auth_state_ == net::AUTH_STATE_NEED_AUTH) {
389 proxy_auth_state_ = net::AUTH_STATE_CANCELED;
initial.commit586acc5fe2008-07-26 22:42:52390 } else {
[email protected]a9bb6f692008-07-30 16:40:10391 DCHECK(server_auth_state_ == net::AUTH_STATE_NEED_AUTH);
392 server_auth_state_ = net::AUTH_STATE_CANCELED;
initial.commit586acc5fe2008-07-26 22:42:52393 }
394
395 // These will be reset in OnStartCompleted.
396 response_info_ = NULL;
397 response_cookies_.clear();
398
399 // OK, let the consumer read the error page...
400 //
401 // Because we set the AUTH_STATE_CANCELED flag, NeedsAuth will return false,
402 // which will cause the consumer to receive OnResponseStarted instead of
403 // OnAuthRequired.
404 //
405 // We have to do this via InvokeLater to avoid "recursing" the consumer.
406 //
407 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
[email protected]175adac2008-07-30 17:28:04408 this, &URLRequestHttpJob::OnStartCompleted, net::OK));
initial.commit586acc5fe2008-07-26 22:42:52409}
410
[email protected]0b45559b2009-06-12 21:45:11411void URLRequestHttpJob::ContinueWithCertificate(
412 net::X509Certificate* client_cert) {
413 DCHECK(transaction_.get());
414
415 DCHECK(!response_info_) << "should not have a response yet";
416
417 // No matter what, we want to report our status as IO pending since we will
418 // be notifying our consumer asynchronously via OnStartCompleted.
419 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
420
421 int rv = transaction_->RestartWithCertificate(client_cert, &start_callback_);
422 if (rv == net::ERR_IO_PENDING)
423 return;
424
425 // The transaction started synchronously, but we need to notify the
426 // URLRequest delegate via the message loop.
427 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
428 this, &URLRequestHttpJob::OnStartCompleted, rv));
429}
430
[email protected]175adac2008-07-30 17:28:04431void URLRequestHttpJob::ContinueDespiteLastError() {
[email protected]9ec48752009-02-06 23:33:58432 // If the transaction was destroyed, then the job was cancelled.
433 if (!transaction_.get())
434 return;
435
initial.commit586acc5fe2008-07-26 22:42:52436 DCHECK(!response_info_) << "should not have a response yet";
437
438 // No matter what, we want to report our status as IO pending since we will
439 // be notifying our consumer asynchronously via OnStartCompleted.
440 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
441
442 int rv = transaction_->RestartIgnoringLastError(&start_callback_);
443 if (rv == net::ERR_IO_PENDING)
444 return;
445
446 // The transaction started synchronously, but we need to notify the
447 // URLRequest delegate via the message loop.
448 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
[email protected]175adac2008-07-30 17:28:04449 this, &URLRequestHttpJob::OnStartCompleted, rv));
initial.commit586acc5fe2008-07-26 22:42:52450}
451
[email protected]175adac2008-07-30 17:28:04452bool URLRequestHttpJob::GetMoreData() {
[email protected]af4876d2008-10-21 23:10:57453 return transaction_.get() && !read_in_progress_;
initial.commit586acc5fe2008-07-26 22:42:52454}
455
[email protected]9dea9e1f2009-01-29 00:30:47456bool URLRequestHttpJob::ReadRawData(net::IOBuffer* buf, int buf_size,
457 int *bytes_read) {
initial.commit586acc5fe2008-07-26 22:42:52458 DCHECK_NE(buf_size, 0);
459 DCHECK(bytes_read);
460 DCHECK(!read_in_progress_);
461
462 int rv = transaction_->Read(buf, buf_size, &read_callback_);
463 if (rv >= 0) {
464 *bytes_read = rv;
465 return true;
466 }
467
468 if (rv == net::ERR_IO_PENDING) {
469 read_in_progress_ = true;
470 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
471 } else {
472 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv));
473 }
474
475 return false;
476}
477
[email protected]175adac2008-07-30 17:28:04478void URLRequestHttpJob::OnStartCompleted(int result) {
initial.commit586acc5fe2008-07-26 22:42:52479 // If the request was destroyed, then there is no more work to do.
480 if (!request_ || !request_->delegate())
481 return;
482
483 // If the transaction was destroyed, then the job was cancelled, and
484 // we can just ignore this notification.
[email protected]af4876d2008-10-21 23:10:57485 if (!transaction_.get())
initial.commit586acc5fe2008-07-26 22:42:52486 return;
487
488 // Clear the IO_PENDING status
489 SetStatus(URLRequestStatus());
490
491 if (result == net::OK) {
492 NotifyHeadersComplete();
[email protected]a9cea7542009-05-20 04:30:23493 } else if (ShouldTreatAsCertificateError(result)) {
initial.commit586acc5fe2008-07-26 22:42:52494 // We encountered an SSL certificate error. Ask our delegate to decide
495 // what we should do.
496 // TODO(wtc): also pass ssl_info.cert_status, or just pass the whole
497 // ssl_info.
498 request_->delegate()->OnSSLCertificateError(
499 request_, result, transaction_->GetResponseInfo()->ssl_info.cert);
[email protected]0b45559b2009-06-12 21:45:11500 } else if (result == net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED) {
501 request_->delegate()->OnCertificateRequested(
502 request_, transaction_->GetResponseInfo()->cert_request_info);
initial.commit586acc5fe2008-07-26 22:42:52503 } else {
504 NotifyStartError(URLRequestStatus(URLRequestStatus::FAILED, result));
505 }
506}
507
[email protected]175adac2008-07-30 17:28:04508void URLRequestHttpJob::OnReadCompleted(int result) {
initial.commit586acc5fe2008-07-26 22:42:52509 read_in_progress_ = false;
510
511 if (result == 0) {
512 NotifyDone(URLRequestStatus());
513 } else if (result < 0) {
514 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result));
515 } else {
516 // Clear the IO_PENDING status
517 SetStatus(URLRequestStatus());
518 }
519
520 NotifyReadComplete(result);
521}
522
[email protected]a9cea7542009-05-20 04:30:23523bool URLRequestHttpJob::ShouldTreatAsCertificateError(int result) {
524 if (!net::IsCertificateError(result))
525 return false;
526
527 // Hide the fancy processing behind a command line switch.
528 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kForceHTTPS))
529 return true;
530
531 // Check whether our context is using ForceTLS.
532 if (!context_->force_tls_state())
533 return true;
534
535 return !context_->force_tls_state()->IsEnabledForHost(
536 request_info_.url.host());
537}
538
[email protected]175adac2008-07-30 17:28:04539void URLRequestHttpJob::NotifyHeadersComplete() {
initial.commit586acc5fe2008-07-26 22:42:52540 DCHECK(!response_info_);
541
542 response_info_ = transaction_->GetResponseInfo();
543
[email protected]d8fd5132009-05-15 01:06:53544 // Save boolean, as we'll need this info at destruction time, and filters may
545 // also need this info.
546 is_cached_content_ = response_info_->was_cached;
547
initial.commit586acc5fe2008-07-26 22:42:52548 // Get the Set-Cookie values, and send them to our cookie database.
[email protected]b8430722008-09-17 20:05:44549 if (!(request_info_.load_flags & net::LOAD_DO_NOT_SAVE_COOKIES)) {
550 URLRequestContext* ctx = request_->context();
551 if (ctx && ctx->cookie_store() &&
[email protected]cfd733212009-05-23 18:11:10552 ctx->cookie_policy()->CanSetCookie(
553 request_->url(), request_->first_party_for_cookies())) {
[email protected]b8430722008-09-17 20:05:44554 FetchResponseCookies();
[email protected]5f450e52009-07-28 13:28:11555 net::CookieOptions options;
[email protected]3a96c742008-11-19 19:46:27556 options.set_include_httponly();
557 ctx->cookie_store()->SetCookiesWithOptions(request_->url(),
558 response_cookies_,
559 options);
[email protected]b8430722008-09-17 20:05:44560 }
561 }
initial.commit586acc5fe2008-07-26 22:42:52562
[email protected]a9cea7542009-05-20 04:30:23563 ProcessForceTLSHeader();
564
[email protected]fe219872008-09-23 02:17:00565 if (SdchManager::Global() &&
566 SdchManager::Global()->IsInSupportedDomain(request_->url())) {
[email protected]60889422008-09-23 01:18:16567 static const std::string name = "Get-Dictionary";
568 std::string url_text;
569 void* iter = NULL;
570 // TODO(jar): We need to not fetch dictionaries the first time they are
571 // seen, but rather wait until we can justify their usefulness.
572 // For now, we will only fetch the first dictionary, which will at least
573 // require multiple suggestions before we get additional ones for this site.
574 // Eventually we should wait until a dictionary is requested several times
575 // before we even download it (so that we don't waste memory or bandwidth).
576 if (response_info_->headers->EnumerateHeader(&iter, name, &url_text)) {
[email protected]d55ad15d2009-02-17 19:40:50577 // request_->url() won't be valid in the destructor, so we use an
578 // alternate copy.
579 DCHECK(request_->url() == request_info_.url);
580 // Resolve suggested URL relative to request url.
581 sdch_dictionary_url_ = request_info_.url.Resolve(url_text);
[email protected]60889422008-09-23 01:18:16582 }
583 }
584
[email protected]0757e7702009-03-27 04:00:22585 // The HTTP transaction may be restarted several times for the purposes
586 // of sending authorization information. Each time it restarts, we get
587 // notified of the headers completion so that we can update the cookie store.
588 if (transaction_->IsReadyToRestartForAuth()) {
589 DCHECK(!response_info_->auth_challenge.get());
590 RestartTransactionWithAuth(std::wstring(), std::wstring());
591 return;
592 }
593
initial.commit586acc5fe2008-07-26 22:42:52594 URLRequestJob::NotifyHeadersComplete();
595}
596
[email protected]175adac2008-07-30 17:28:04597void URLRequestHttpJob::DestroyTransaction() {
[email protected]af4876d2008-10-21 23:10:57598 DCHECK(transaction_.get());
initial.commit586acc5fe2008-07-26 22:42:52599
[email protected]af4876d2008-10-21 23:10:57600 transaction_.reset();
initial.commit586acc5fe2008-07-26 22:42:52601 response_info_ = NULL;
602}
603
[email protected]175adac2008-07-30 17:28:04604void URLRequestHttpJob::StartTransaction() {
initial.commit586acc5fe2008-07-26 22:42:52605 // NOTE: This method assumes that request_info_ is already setup properly.
606
607 // Create a transaction.
[email protected]af4876d2008-10-21 23:10:57608 DCHECK(!transaction_.get());
initial.commit586acc5fe2008-07-26 22:42:52609
610 DCHECK(request_->context());
611 DCHECK(request_->context()->http_transaction_factory());
612
[email protected]af4876d2008-10-21 23:10:57613 transaction_.reset(
614 request_->context()->http_transaction_factory()->CreateTransaction());
initial.commit586acc5fe2008-07-26 22:42:52615
616 // No matter what, we want to report our status as IO pending since we will
617 // be notifying our consumer asynchronously via OnStartCompleted.
618 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
619
620 int rv;
[email protected]af4876d2008-10-21 23:10:57621 if (transaction_.get()) {
[email protected]ec08bb22009-08-12 00:25:12622 rv = transaction_->Start(
[email protected]684970b2009-08-14 04:54:46623 &request_info_, &start_callback_, request_->load_log());
initial.commit586acc5fe2008-07-26 22:42:52624 if (rv == net::ERR_IO_PENDING)
625 return;
626 } else {
627 rv = net::ERR_FAILED;
628 }
629
630 // The transaction started synchronously, but we need to notify the
631 // URLRequest delegate via the message loop.
632 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
[email protected]175adac2008-07-30 17:28:04633 this, &URLRequestHttpJob::OnStartCompleted, rv));
initial.commit586acc5fe2008-07-26 22:42:52634}
635
[email protected]175adac2008-07-30 17:28:04636void URLRequestHttpJob::AddExtraHeaders() {
[email protected]5b90b5d2009-04-30 23:06:01637 // TODO(jar): Consider optimizing away SDCH advertising bytes when the URL is
638 // probably an img or such (and SDCH encoding is not likely).
639 bool advertise_sdch = SdchManager::Global() &&
640 SdchManager::Global()->IsInSupportedDomain(request_->url());
641 std::string avail_dictionaries;
642 if (advertise_sdch) {
643 SdchManager::Global()->GetAvailDictionaryList(request_->url(),
644 &avail_dictionaries);
645
646 // The AllowLatencyExperiment() is only true if we've successfully done a
647 // full SDCH compression recently in this browser session for this host.
648 // Note that for this path, there might be no applicable dictionaries, and
649 // hence we can't participate in the experiment.
650 if (!avail_dictionaries.empty() &&
651 SdchManager::Global()->AllowLatencyExperiment(request_->url())) {
652 // We are participating in the test (or control), and hence we'll
653 // eventually record statistics via either SDCH_EXPERIMENT_DECODE or
654 // SDCH_EXPERIMENT_HOLDBACK, and we'll need some packet timing data.
655 EnablePacketCounting(kSdchPacketHistogramCount);
[email protected]a88af5232009-06-05 01:34:53656 if (base::RandDouble() < .01) {
657 sdch_test_control_ = true; // 1% probability.
[email protected]5b90b5d2009-04-30 23:06:01658 advertise_sdch = false;
659 } else {
660 sdch_test_activated_ = true;
661 }
662 }
663 }
664
[email protected]423041b2008-10-27 17:39:28665 // Supply Accept-Encoding headers first so that it is more likely that they
666 // will be in the first transmitted packet. This can sometimes make it easier
667 // to filter and analyze the streams to assure that a proxy has not damaged
668 // these headers. Some proxies deliberately corrupt Accept-Encoding headers.
[email protected]5b90b5d2009-04-30 23:06:01669 if (!advertise_sdch) {
[email protected]423041b2008-10-27 17:39:28670 // Tell the server what compression formats we support (other than SDCH).
[email protected]a86c97cc2009-06-24 21:26:27671 request_info_.extra_headers += "Accept-Encoding: gzip,deflate\r\n";
[email protected]423041b2008-10-27 17:39:28672 } else {
[email protected]5b90b5d2009-04-30 23:06:01673 // Include SDCH in acceptable list.
[email protected]423041b2008-10-27 17:39:28674 request_info_.extra_headers += "Accept-Encoding: "
[email protected]a86c97cc2009-06-24 21:26:27675 "gzip,deflate,sdch\r\n";
[email protected]423041b2008-10-27 17:39:28676 if (!avail_dictionaries.empty()) {
677 request_info_.extra_headers += "Avail-Dictionary: "
678 + avail_dictionaries + "\r\n";
[email protected]5b90b5d2009-04-30 23:06:01679 sdch_dictionary_advertised_ = true;
680 // Since we're tagging this transaction as advertising a dictionary, we'll
681 // definately employ an SDCH filter (or tentative sdch filter) when we get
682 // a response. When done, we'll record histograms via SDCH_DECODE or
683 // SDCH_PASSTHROUGH. Hence we need to record packet arrival times.
684 EnablePacketCounting(kSdchPacketHistogramCount);
[email protected]423041b2008-10-27 17:39:28685 }
[email protected]423041b2008-10-27 17:39:28686 }
687
initial.commit586acc5fe2008-07-26 22:42:52688 URLRequestContext* context = request_->context();
689 if (context) {
[email protected]5f450e52009-07-28 13:28:11690 if (context->AllowSendingCookies(request_))
[email protected]eaadd9052009-06-23 18:02:23691 request_info_.extra_headers += AssembleRequestCookies();
initial.commit586acc5fe2008-07-26 22:42:52692 if (!context->accept_language().empty())
693 request_info_.extra_headers += "Accept-Language: " +
694 context->accept_language() + "\r\n";
695 if (!context->accept_charset().empty())
696 request_info_.extra_headers += "Accept-Charset: " +
697 context->accept_charset() + "\r\n";
698 }
initial.commit586acc5fe2008-07-26 22:42:52699}
700
[email protected]0757e7702009-03-27 04:00:22701std::string URLRequestHttpJob::AssembleRequestCookies() {
[email protected]861fcd52009-08-26 02:33:46702 if (request_info_.load_flags & net::LOAD_DO_NOT_SEND_COOKIES)
703 return std::string();
704
[email protected]0757e7702009-03-27 04:00:22705 URLRequestContext* context = request_->context();
706 if (context) {
707 // Add in the cookie header. TODO might we need more than one header?
708 if (context->cookie_store() &&
[email protected]cfd733212009-05-23 18:11:10709 context->cookie_policy()->CanGetCookies(
710 request_->url(), request_->first_party_for_cookies())) {
[email protected]5f450e52009-07-28 13:28:11711 net::CookieOptions options;
[email protected]0757e7702009-03-27 04:00:22712 options.set_include_httponly();
713 std::string cookies = request_->context()->cookie_store()->
714 GetCookiesWithOptions(request_->url(), options);
715 if (!cookies.empty())
716 return "Cookie: " + cookies + "\r\n";
717 }
718 }
719 return std::string();
720}
721
[email protected]175adac2008-07-30 17:28:04722void URLRequestHttpJob::FetchResponseCookies() {
initial.commit586acc5fe2008-07-26 22:42:52723 DCHECK(response_info_);
724 DCHECK(response_cookies_.empty());
725
726 std::string name = "Set-Cookie";
727 std::string value;
728
729 void* iter = NULL;
730 while (response_info_->headers->EnumerateHeader(&iter, name, &value))
[email protected]5f450e52009-07-28 13:28:11731 if (request_->context()->InterceptCookie(request_, &value))
[email protected]eaadd9052009-06-23 18:02:23732 response_cookies_.push_back(value);
initial.commit586acc5fe2008-07-26 22:42:52733}
[email protected]a9cea7542009-05-20 04:30:23734
735
736void URLRequestHttpJob::ProcessForceTLSHeader() {
737 DCHECK(response_info_);
738
739 // Hide processing behind a command line flag.
740 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kForceHTTPS))
741 return;
742
743 // Only process X-Force-TLS from HTTPS responses.
744 if (request_info_.url.scheme() != "https")
745 return;
746
747 // Only process X-Force-TLS from responses with valid certificates.
748 if (response_info_->ssl_info.cert_status & net::CERT_STATUS_ALL_ERRORS)
749 return;
750
751 URLRequestContext* ctx = request_->context();
752 if (!ctx || !ctx->force_tls_state())
753 return;
754
755 std::string name = "X-Force-TLS";
756 std::string value;
757
758 void* iter = NULL;
759 while (response_info_->headers->EnumerateHeader(&iter, name, &value))
760 ctx->force_tls_state()->DidReceiveHeader(request_info_.url, value);
761}