summaryrefslogtreecommitdiffstats
path: root/net/url_request/url_request_http_job.cc
diff options
context:
space:
mode:
authordarin@chromium.org <darin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-02-03 22:14:15 +0000
committerdarin@chromium.org <darin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-02-03 22:14:15 +0000
commit3460228438564690022243511825894c9c600608 (patch)
treebada6636f1a8656ffb8ac481cad1c1d29bddf9f5 /net/url_request/url_request_http_job.cc
parente56e96f80ce491b23454a039ae5eba1a1c6ee3f9 (diff)
downloadchromium_src-3460228438564690022243511825894c9c600608.zip
chromium_src-3460228438564690022243511825894c9c600608.tar.gz
chromium_src-3460228438564690022243511825894c9c600608.tar.bz2
Revert 38001 and 38002.
Modify CookiePolicy to work asynchronously This change will enable us to prompt the user before setting a cookie. While we only need to prompt before setting, we actually need to make both CanSetCookie and CanGetCookies asynchronous. This is necessary in order to preserve FIFO ordering since the value returned by GetCookies depends on the changes made to the cookie DB by SetCookie. This change also includes some simplification of CookieStore. Instead of N virtual functions, I distilled it down to only 4. The remaining functions are instead expressed in terms of those. While studying all the places where we currently use CookiePolicy, I found that some of them were not appropriate. After discussing with Amit, I decided to remove the policy checks in URLRequestAutomationJob. See the comments in the code regarding this. I changed the signature of CookieMonster::GetRawCookies to GetAllCookiesForURL to better match GetAllCookies. Related to this change webkit/glue/webcookie.h grows a constructor that takes a CanonicalCookie to help clean up some code. On the Chrome side, ChromeURLRequestContext now has a ChromeCookiePolicy object. That object is threadsafe ref counted because it is passed between the UI and IO threads. It is responsible for implementing the queuing logic described above. It will also in the future trigger the Chrome UI code to actually show the setcookie prompt. Please review the state machinery changes in URLRequestHttpJob carefully. R=eroman BUG=34331 TEST=no tests yet for prompting. Review URL: http://codereview.chromium.org/564045 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@38028 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/url_request/url_request_http_job.cc')
-rw-r--r--net/url_request/url_request_http_job.cc228
1 files changed, 144 insertions, 84 deletions
diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc
index 4b08c64..15b3373 100644
--- a/net/url_request/url_request_http_job.cc
+++ b/net/url_request/url_request_http_job.cc
@@ -75,12 +75,17 @@ URLRequestHttpJob::URLRequestHttpJob(URLRequest* request)
: URLRequestJob(request),
context_(request->context()),
response_info_(NULL),
+ response_cookies_save_index_(0),
proxy_auth_state_(net::AUTH_STATE_DONT_NEED_AUTH),
server_auth_state_(net::AUTH_STATE_DONT_NEED_AUTH),
- ALLOW_THIS_IN_INITIALIZER_LIST(
- start_callback_(this, &URLRequestHttpJob::OnStartCompleted)),
- ALLOW_THIS_IN_INITIALIZER_LIST(
- read_callback_(this, &URLRequestHttpJob::OnReadCompleted)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(can_get_cookies_callback_(
+ this, &URLRequestHttpJob::OnCanGetCookiesCompleted)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(can_set_cookie_callback_(
+ this, &URLRequestHttpJob::OnCanSetCookieCompleted)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(start_callback_(
+ this, &URLRequestHttpJob::OnStartCompleted)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(read_callback_(
+ this, &URLRequestHttpJob::OnReadCompleted)),
read_in_progress_(false),
transaction_(NULL),
sdch_dictionary_advertised_(false),
@@ -146,8 +151,7 @@ void URLRequestHttpJob::Start() {
}
AddExtraHeaders();
-
- StartTransaction();
+ AddCookieHeaderAndStart();
}
void URLRequestHttpJob::Kill() {
@@ -200,11 +204,11 @@ bool URLRequestHttpJob::GetResponseCookies(
if (!response_info_)
return false;
- if (response_cookies_.empty())
- FetchResponseCookies();
+ // TODO(darin): Why are we extracting response cookies again? Perhaps we
+ // should just leverage response_cookies_.
cookies->clear();
- cookies->swap(response_cookies_);
+ FetchResponseCookies(response_info_, cookies);
return true;
}
@@ -320,6 +324,8 @@ void URLRequestHttpJob::SetAuth(const std::wstring& username,
void URLRequestHttpJob::RestartTransactionWithAuth(
const std::wstring& username,
const std::wstring& password) {
+ username_ = username;
+ password_ = password;
// These will be reset in OnStartCompleted.
response_info_ = NULL;
@@ -327,27 +333,12 @@ void URLRequestHttpJob::RestartTransactionWithAuth(
// Update the cookies, since the cookie store may have been updated from the
// headers in the 401/407. Since cookies were already appended to
- // extra_headers by AddExtraHeaders(), we need to strip them out.
+ // extra_headers, we need to strip them out before adding them again.
static const char* const cookie_name[] = { "cookie" };
request_info_.extra_headers = net::HttpUtil::StripHeaders(
request_info_.extra_headers, cookie_name, arraysize(cookie_name));
- // TODO(eroman): this ordering is inconsistent with non-restarted request,
- // where cookies header appears second from the bottom.
- request_info_.extra_headers += AssembleRequestCookies();
-
- // No matter what, we want to report our status as IO pending since we will
- // be notifying our consumer asynchronously via OnStartCompleted.
- SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
- int rv = transaction_->RestartWithAuth(username, password,
- &start_callback_);
- if (rv == net::ERR_IO_PENDING)
- return;
-
- // The transaction started synchronously, but we need to notify the
- // URLRequest delegate via the message loop.
- MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
- this, &URLRequestHttpJob::OnStartCompleted, rv));
+ AddCookieHeaderAndStart();
}
void URLRequestHttpJob::CancelAuth() {
@@ -438,6 +429,41 @@ bool URLRequestHttpJob::ReadRawData(net::IOBuffer* buf, int buf_size,
return false;
}
+void URLRequestHttpJob::OnCanGetCookiesCompleted(int policy) {
+ // If the request was destroyed, then there is no more work to do.
+ if (request_ && request_->delegate()) {
+ if (policy == net::OK && request_->context()->cookie_store()) {
+ net::CookieOptions options;
+ options.set_include_httponly();
+ std::string cookies =
+ request_->context()->cookie_store()->GetCookiesWithOptions(
+ request_->url(), options);
+ if (request_->context()->InterceptRequestCookies(request_, cookies) &&
+ !cookies.empty())
+ request_info_.extra_headers += "Cookie: " + cookies + "\r\n";
+ }
+ StartTransaction();
+ }
+ Release(); // Balance AddRef taken in AddCookieHeaderAndStart
+}
+
+void URLRequestHttpJob::OnCanSetCookieCompleted(int policy) {
+ // If the request was destroyed, then there is no more work to do.
+ if (request_ && request_->delegate()) {
+ if (policy == net::OK && request_->context()->cookie_store()) {
+ // OK to save the current response cookie now.
+ net::CookieOptions options;
+ options.set_include_httponly();
+ request_->context()->cookie_store()->SetCookieWithOptions(
+ request_->url(), response_cookies_[response_cookies_save_index_],
+ options);
+ }
+ response_cookies_save_index_++;
+ SaveNextCookie();
+ }
+ Release(); // Balance AddRef taken in SaveNextCookie
+}
+
void URLRequestHttpJob::OnStartCompleted(int result) {
// If the request was destroyed, then there is no more work to do.
if (!request_ || !request_->delegate())
@@ -452,7 +478,7 @@ void URLRequestHttpJob::OnStartCompleted(int result) {
SetStatus(URLRequestStatus());
if (result == net::OK) {
- NotifyHeadersComplete();
+ SaveCookiesAndNotifyHeadersComplete();
} else if (ShouldTreatAsCertificateError(result)) {
// We encountered an SSL certificate error. Ask our delegate to decide
// what we should do.
@@ -509,21 +535,6 @@ void URLRequestHttpJob::NotifyHeadersComplete() {
// also need this info.
is_cached_content_ = response_info_->was_cached;
- // Get the Set-Cookie values, and send them to our cookie database.
- if (!(request_info_.load_flags & net::LOAD_DO_NOT_SAVE_COOKIES)) {
- URLRequestContext* ctx = request_->context();
- if (ctx && ctx->cookie_store() && (!ctx->cookie_policy() ||
- ctx->cookie_policy()->CanSetCookie(
- request_->url(), request_->first_party_for_cookies()))) {
- FetchResponseCookies();
- net::CookieOptions options;
- options.set_include_httponly();
- ctx->cookie_store()->SetCookiesWithOptions(request_->url(),
- response_cookies_,
- options);
- }
- }
-
ProcessStrictTransportSecurityHeader();
if (SdchManager::Global() &&
@@ -568,26 +579,29 @@ void URLRequestHttpJob::DestroyTransaction() {
void URLRequestHttpJob::StartTransaction() {
// NOTE: This method assumes that request_info_ is already setup properly.
- // Create a transaction.
- DCHECK(!transaction_.get());
-
- DCHECK(request_->context());
- DCHECK(request_->context()->http_transaction_factory());
-
- // No matter what, we want to report our status as IO pending since we will
- // be notifying our consumer asynchronously via OnStartCompleted.
- SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
+ // If we already have a transaction, then we should restart the transaction
+ // with auth provided by username_ and password_.
- int rv = request_->context()->http_transaction_factory()->CreateTransaction(
- &transaction_);
-
- if (rv == net::OK) {
- rv = transaction_->Start(
- &request_info_, &start_callback_, request_->load_log());
- if (rv == net::ERR_IO_PENDING)
- return;
+ int rv;
+ if (transaction_.get()) {
+ rv = transaction_->RestartWithAuth(username_, password_, &start_callback_);
+ username_.clear();
+ password_.clear();
+ } else {
+ DCHECK(request_->context());
+ DCHECK(request_->context()->http_transaction_factory());
+
+ rv = request_->context()->http_transaction_factory()->CreateTransaction(
+ &transaction_);
+ if (rv == net::OK) {
+ rv = transaction_->Start(
+ &request_info_, &start_callback_, request_->load_log());
+ }
}
+ if (rv == net::ERR_IO_PENDING)
+ return;
+
// The transaction started synchronously, but we need to notify the
// URLRequest delegate via the message loop.
MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
@@ -648,8 +662,6 @@ void URLRequestHttpJob::AddExtraHeaders() {
URLRequestContext* context = request_->context();
if (context) {
- request_info_.extra_headers += AssembleRequestCookies();
-
// Only add default Accept-Language and Accept-Charset if the request
// didn't have them specified.
net::HttpUtil::AppendHeaderIfMissing("Accept-Language",
@@ -661,39 +673,87 @@ void URLRequestHttpJob::AddExtraHeaders() {
}
}
-std::string URLRequestHttpJob::AssembleRequestCookies() {
- if (request_info_.load_flags & net::LOAD_DO_NOT_SEND_COOKIES)
- return std::string();
+void URLRequestHttpJob::AddCookieHeaderAndStart() {
+ // No matter what, we want to report our status as IO pending since we will
+ // be notifying our consumer asynchronously via OnStartCompleted.
+ SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
- URLRequestContext* context = request_->context();
- if (context) {
- // Add in the cookie header. TODO might we need more than one header?
- if (context->cookie_store() && (!context->cookie_policy() ||
- context->cookie_policy()->CanGetCookies(
- request_->url(), request_->first_party_for_cookies()))) {
- net::CookieOptions options;
- options.set_include_httponly();
- std::string cookies = request_->context()->cookie_store()->
- GetCookiesWithOptions(request_->url(), options);
- if (context->InterceptRequestCookies(request_, cookies) &&
- !cookies.empty())
- return "Cookie: " + cookies + "\r\n";
- }
+ AddRef(); // Balanced in OnCanGetCookiesCompleted
+
+ int policy = net::OK;
+
+ if (request_info_.load_flags & net::LOAD_DO_NOT_SEND_COOKIES) {
+ policy = net::ERR_ACCESS_DENIED;
+ } else if (request_->context()->cookie_policy()) {
+ policy = request_->context()->cookie_policy()->CanGetCookies(
+ request_->url(),
+ request_->first_party_for_cookies(),
+ &can_get_cookies_callback_);
+ if (policy == net::ERR_IO_PENDING)
+ return; // Wait for completion callback
}
- return std::string();
+
+ OnCanGetCookiesCompleted(policy);
}
-void URLRequestHttpJob::FetchResponseCookies() {
- DCHECK(response_info_);
- DCHECK(response_cookies_.empty());
+void URLRequestHttpJob::SaveCookiesAndNotifyHeadersComplete() {
+ DCHECK(transaction_.get());
+ const net::HttpResponseInfo* response_info = transaction_->GetResponseInfo();
+ DCHECK(response_info);
+
+ response_cookies_.clear();
+ response_cookies_save_index_ = 0;
+
+ FetchResponseCookies(response_info, &response_cookies_);
+
+ // Now, loop over the response cookies, and attempt to persist each.
+ SaveNextCookie();
+}
+
+void URLRequestHttpJob::SaveNextCookie() {
+ if (response_cookies_save_index_ == response_cookies_.size()) {
+ response_cookies_.clear();
+ response_cookies_save_index_ = 0;
+ SetStatus(URLRequestStatus()); // Clear the IO_PENDING status
+ NotifyHeadersComplete();
+ return;
+ }
+
+ // No matter what, we want to report our status as IO pending since we will
+ // be notifying our consumer asynchronously via OnStartCompleted.
+ SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
+
+ AddRef(); // Balanced in OnCanSetCookieCompleted
+
+ int policy = net::OK;
+
+ if (request_info_.load_flags & net::LOAD_DO_NOT_SAVE_COOKIES) {
+ policy = net::ERR_ACCESS_DENIED;
+ } else if (request_->context()->cookie_policy()) {
+ policy = request_->context()->cookie_policy()->CanSetCookie(
+ request_->url(),
+ request_->first_party_for_cookies(),
+ response_cookies_[response_cookies_save_index_],
+ &can_set_cookie_callback_);
+ if (policy == net::ERR_IO_PENDING)
+ return; // Wait for completion callback
+ }
+
+ OnCanSetCookieCompleted(policy);
+}
+
+void URLRequestHttpJob::FetchResponseCookies(
+ const net::HttpResponseInfo* response_info,
+ std::vector<std::string>* cookies) {
std::string name = "Set-Cookie";
std::string value;
void* iter = NULL;
- while (response_info_->headers->EnumerateHeader(&iter, name, &value))
+ while (response_info->headers->EnumerateHeader(&iter, name, &value)) {
if (request_->context()->InterceptResponseCookie(request_, value))
- response_cookies_.push_back(value);
+ cookies->push_back(value);
+ }
}
class HTTPSProberDelegate : public net::HTTPSProberDelegate {