initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 1 | // Copyright 2008, Google Inc. |
| 2 | // All rights reserved. |
| 3 | // |
| 4 | // Redistribution and use in source and binary forms, with or without |
| 5 | // modification, are permitted provided that the following conditions are |
| 6 | // met: |
| 7 | // |
| 8 | // * Redistributions of source code must retain the above copyright |
| 9 | // notice, this list of conditions and the following disclaimer. |
| 10 | // * Redistributions in binary form must reproduce the above |
| 11 | // copyright notice, this list of conditions and the following disclaimer |
| 12 | // in the documentation and/or other materials provided with the |
| 13 | // distribution. |
| 14 | // * Neither the name of Google Inc. nor the names of its |
| 15 | // contributors may be used to endorse or promote products derived from |
| 16 | // this software without specific prior written permission. |
| 17 | // |
| 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 29 | |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 30 | #include "net/url_request/url_request_http_job.h" |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 31 | |
[email protected] | 39ce5c0 | 2008-08-22 04:03:44 | [diff] [blame^] | 32 | #include "base/compiler_specific.h" |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 33 | #include "base/message_loop.h" |
| 34 | #include "base/string_util.h" |
| 35 | #include "net/base/cookie_monster.h" |
| 36 | #include "net/base/net_errors.h" |
| 37 | #include "net/base/net_util.h" |
| 38 | #include "net/http/http_response_info.h" |
| 39 | #include "net/http/http_transaction.h" |
| 40 | #include "net/http/http_transaction_factory.h" |
| 41 | #include "net/url_request/url_request.h" |
| 42 | #include "net/url_request/url_request_error_job.h" |
| 43 | |
| 44 | // TODO(darin): make sure the port blocking code is not lost |
| 45 | |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 46 | // static |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 47 | URLRequestJob* URLRequestHttpJob::Factory(URLRequest* request, |
| 48 | const std::string& scheme) { |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 49 | DCHECK(scheme == "http" || scheme == "https"); |
| 50 | |
[email protected] | 8ac1a75 | 2008-07-31 19:40:37 | [diff] [blame] | 51 | if (!net::IsPortAllowedByDefault(request->url().IntPort())) |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 52 | return new URLRequestErrorJob(request, net::ERR_UNSAFE_PORT); |
| 53 | |
| 54 | if (!request->context() || |
| 55 | !request->context()->http_transaction_factory()) { |
| 56 | NOTREACHED() << "requires a valid context"; |
| 57 | return new URLRequestErrorJob(request, net::ERR_INVALID_ARGUMENT); |
| 58 | } |
| 59 | |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 60 | return new URLRequestHttpJob(request); |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 61 | } |
| 62 | |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 63 | URLRequestHttpJob::URLRequestHttpJob(URLRequest* request) |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 64 | : URLRequestJob(request), |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 65 | transaction_(NULL), |
| 66 | response_info_(NULL), |
[email protected] | a9bb6f69 | 2008-07-30 16:40:10 | [diff] [blame] | 67 | proxy_auth_state_(net::AUTH_STATE_DONT_NEED_AUTH), |
| 68 | server_auth_state_(net::AUTH_STATE_DONT_NEED_AUTH), |
[email protected] | 39ce5c0 | 2008-08-22 04:03:44 | [diff] [blame^] | 69 | ALLOW_THIS_IN_INITIALIZER_LIST( |
| 70 | start_callback_(this, &URLRequestHttpJob::OnStartCompleted)), |
| 71 | ALLOW_THIS_IN_INITIALIZER_LIST( |
| 72 | read_callback_(this, &URLRequestHttpJob::OnReadCompleted)), |
[email protected] | 3589e55 | 2008-08-20 23:11:34 | [diff] [blame] | 73 | read_in_progress_(false), |
| 74 | context_(request->context()) { |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 75 | } |
| 76 | |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 77 | URLRequestHttpJob::~URLRequestHttpJob() { |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 78 | if (transaction_) |
| 79 | DestroyTransaction(); |
| 80 | } |
| 81 | |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 82 | void URLRequestHttpJob::SetUpload(net::UploadData* upload) { |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 83 | DCHECK(!transaction_) << "cannot change once started"; |
| 84 | request_info_.upload_data = upload; |
| 85 | } |
| 86 | |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 87 | void URLRequestHttpJob::SetExtraRequestHeaders( |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 88 | const std::string& headers) { |
| 89 | DCHECK(!transaction_) << "cannot change once started"; |
| 90 | request_info_.extra_headers = headers; |
| 91 | } |
| 92 | |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 93 | void URLRequestHttpJob::Start() { |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 94 | DCHECK(!transaction_); |
| 95 | |
| 96 | // TODO(darin): URLRequest::referrer() should return a GURL |
| 97 | GURL referrer(request_->referrer()); |
| 98 | |
| 99 | // Ensure that we do not send username and password fields in the referrer. |
| 100 | if (referrer.has_username() || referrer.has_password()) { |
| 101 | GURL::Replacements referrer_mods; |
| 102 | referrer_mods.ClearUsername(); |
| 103 | referrer_mods.ClearPassword(); |
| 104 | referrer = referrer.ReplaceComponents(referrer_mods); |
| 105 | } |
| 106 | |
| 107 | request_info_.url = request_->url(); |
| 108 | request_info_.referrer = referrer; |
| 109 | request_info_.method = request_->method(); |
| 110 | request_info_.load_flags = request_->load_flags(); |
| 111 | |
| 112 | if (request_->context()) |
| 113 | request_info_.user_agent = request_->context()->user_agent(); |
| 114 | |
| 115 | AddExtraHeaders(); |
| 116 | |
| 117 | StartTransaction(); |
| 118 | } |
| 119 | |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 120 | void URLRequestHttpJob::Kill() { |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 121 | if (!transaction_) |
| 122 | return; |
| 123 | |
| 124 | DestroyTransaction(); |
| 125 | URLRequestJob::Kill(); |
| 126 | } |
| 127 | |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 128 | net::LoadState URLRequestHttpJob::GetLoadState() const { |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 129 | return transaction_ ? transaction_->GetLoadState() : net::LOAD_STATE_IDLE; |
| 130 | } |
| 131 | |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 132 | uint64 URLRequestHttpJob::GetUploadProgress() const { |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 133 | return transaction_ ? transaction_->GetUploadProgress() : 0; |
| 134 | } |
| 135 | |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 136 | bool URLRequestHttpJob::GetMimeType(std::string* mime_type) { |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 137 | DCHECK(transaction_); |
| 138 | |
| 139 | if (!response_info_) |
| 140 | return false; |
| 141 | |
| 142 | return response_info_->headers->GetMimeType(mime_type); |
| 143 | } |
| 144 | |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 145 | bool URLRequestHttpJob::GetCharset(std::string* charset) { |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 146 | DCHECK(transaction_); |
| 147 | |
| 148 | if (!response_info_) |
| 149 | return false; |
| 150 | |
| 151 | return response_info_->headers->GetCharset(charset); |
| 152 | } |
| 153 | |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 154 | void URLRequestHttpJob::GetResponseInfo(net::HttpResponseInfo* info) { |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 155 | DCHECK(request_); |
| 156 | DCHECK(transaction_); |
| 157 | |
| 158 | if (response_info_) |
| 159 | *info = *response_info_; |
| 160 | } |
| 161 | |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 162 | bool URLRequestHttpJob::GetResponseCookies( |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 163 | std::vector<std::string>* cookies) { |
| 164 | DCHECK(transaction_); |
| 165 | |
| 166 | if (!response_info_) |
| 167 | return false; |
| 168 | |
| 169 | if (response_cookies_.empty()) |
| 170 | FetchResponseCookies(); |
| 171 | |
| 172 | cookies->clear(); |
| 173 | cookies->swap(response_cookies_); |
| 174 | return true; |
| 175 | } |
| 176 | |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 177 | int URLRequestHttpJob::GetResponseCode() { |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 178 | DCHECK(transaction_); |
| 179 | |
| 180 | if (!response_info_) |
| 181 | return -1; |
| 182 | |
| 183 | return response_info_->headers->response_code(); |
| 184 | } |
| 185 | |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 186 | bool URLRequestHttpJob::GetContentEncoding(std::string* encoding_type) { |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 187 | DCHECK(transaction_); |
| 188 | |
| 189 | if (!response_info_) |
| 190 | return false; |
| 191 | |
| 192 | // TODO(darin): what if there are multiple content encodings? |
| 193 | return response_info_->headers->EnumerateHeader(NULL, "Content-Encoding", |
| 194 | encoding_type); |
| 195 | } |
| 196 | |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 197 | bool URLRequestHttpJob::IsRedirectResponse(GURL* location, |
| 198 | int* http_status_code) { |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 199 | if (!response_info_) |
| 200 | return false; |
| 201 | |
| 202 | std::string value; |
| 203 | if (!response_info_->headers->IsRedirect(&value)) |
| 204 | return false; |
| 205 | |
| 206 | *location = request_->url().Resolve(value); |
| 207 | *http_status_code = response_info_->headers->response_code(); |
| 208 | return true; |
| 209 | } |
| 210 | |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 211 | bool URLRequestHttpJob::IsSafeRedirect(const GURL& location) { |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 212 | // We only allow redirects to certain "safe" protocols. This does not |
| 213 | // restrict redirects to externally handled protocols. Our consumer would |
| 214 | // need to take care of those. |
| 215 | |
| 216 | if (!URLRequest::IsHandledURL(location)) |
| 217 | return true; |
| 218 | |
| 219 | static const char* kSafeSchemes[] = { |
| 220 | "http", |
| 221 | "https", |
| 222 | "ftp" |
| 223 | }; |
| 224 | |
| 225 | for (size_t i = 0; i < arraysize(kSafeSchemes); ++i) { |
| 226 | if (location.SchemeIs(kSafeSchemes[i])) |
| 227 | return true; |
| 228 | } |
| 229 | |
| 230 | return false; |
| 231 | } |
| 232 | |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 233 | bool URLRequestHttpJob::NeedsAuth() { |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 234 | int code = GetResponseCode(); |
| 235 | if (code == -1) |
| 236 | return false; |
| 237 | |
| 238 | // Check if we need either Proxy or WWW Authentication. This could happen |
| 239 | // because we either provided no auth info, or provided incorrect info. |
| 240 | switch (code) { |
| 241 | case 407: |
[email protected] | a9bb6f69 | 2008-07-30 16:40:10 | [diff] [blame] | 242 | if (proxy_auth_state_ == net::AUTH_STATE_CANCELED) |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 243 | return false; |
[email protected] | a9bb6f69 | 2008-07-30 16:40:10 | [diff] [blame] | 244 | proxy_auth_state_ = net::AUTH_STATE_NEED_AUTH; |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 245 | return true; |
| 246 | case 401: |
[email protected] | a9bb6f69 | 2008-07-30 16:40:10 | [diff] [blame] | 247 | if (server_auth_state_ == net::AUTH_STATE_CANCELED) |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 248 | return false; |
[email protected] | a9bb6f69 | 2008-07-30 16:40:10 | [diff] [blame] | 249 | server_auth_state_ = net::AUTH_STATE_NEED_AUTH; |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 250 | return true; |
| 251 | } |
| 252 | return false; |
| 253 | } |
| 254 | |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 255 | void URLRequestHttpJob::GetAuthChallengeInfo( |
[email protected] | a9bb6f69 | 2008-07-30 16:40:10 | [diff] [blame] | 256 | scoped_refptr<net::AuthChallengeInfo>* result) { |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 257 | DCHECK(transaction_); |
| 258 | DCHECK(response_info_); |
| 259 | |
| 260 | // sanity checks: |
[email protected] | a9bb6f69 | 2008-07-30 16:40:10 | [diff] [blame] | 261 | DCHECK(proxy_auth_state_ == net::AUTH_STATE_NEED_AUTH || |
| 262 | server_auth_state_ == net::AUTH_STATE_NEED_AUTH); |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 263 | DCHECK(response_info_->headers->response_code() == 401 || |
| 264 | response_info_->headers->response_code() == 407); |
| 265 | |
| 266 | *result = response_info_->auth_challenge; |
| 267 | } |
| 268 | |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 269 | void URLRequestHttpJob::GetCachedAuthData( |
[email protected] | a9bb6f69 | 2008-07-30 16:40:10 | [diff] [blame] | 270 | const net::AuthChallengeInfo& auth_info, |
| 271 | scoped_refptr<net::AuthData>* auth_data) { |
| 272 | net::AuthCache* auth_cache = |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 273 | request_->context()->http_transaction_factory()->GetAuthCache(); |
| 274 | if (!auth_cache) { |
| 275 | *auth_data = NULL; |
| 276 | return; |
| 277 | } |
[email protected] | a9bb6f69 | 2008-07-30 16:40:10 | [diff] [blame] | 278 | std::string auth_cache_key = |
| 279 | net::AuthCache::HttpKey(request_->url(), auth_info); |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 280 | *auth_data = auth_cache->Lookup(auth_cache_key); |
| 281 | } |
| 282 | |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 283 | void URLRequestHttpJob::SetAuth(const std::wstring& username, |
| 284 | const std::wstring& password) { |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 285 | DCHECK(transaction_); |
| 286 | |
| 287 | // Proxy gets set first, then WWW. |
[email protected] | a9bb6f69 | 2008-07-30 16:40:10 | [diff] [blame] | 288 | if (proxy_auth_state_ == net::AUTH_STATE_NEED_AUTH) { |
| 289 | proxy_auth_state_ = net::AUTH_STATE_HAVE_AUTH; |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 290 | } else { |
[email protected] | a9bb6f69 | 2008-07-30 16:40:10 | [diff] [blame] | 291 | DCHECK(server_auth_state_ == net::AUTH_STATE_NEED_AUTH); |
| 292 | server_auth_state_ = net::AUTH_STATE_HAVE_AUTH; |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 293 | } |
| 294 | |
| 295 | // These will be reset in OnStartCompleted. |
| 296 | response_info_ = NULL; |
| 297 | response_cookies_.clear(); |
| 298 | |
| 299 | // No matter what, we want to report our status as IO pending since we will |
| 300 | // be notifying our consumer asynchronously via OnStartCompleted. |
| 301 | SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); |
| 302 | |
| 303 | int rv = transaction_->RestartWithAuth(username, password, |
| 304 | &start_callback_); |
| 305 | if (rv == net::ERR_IO_PENDING) |
| 306 | return; |
| 307 | |
| 308 | // The transaction started synchronously, but we need to notify the |
| 309 | // URLRequest delegate via the message loop. |
| 310 | MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 311 | this, &URLRequestHttpJob::OnStartCompleted, rv)); |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 312 | } |
| 313 | |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 314 | void URLRequestHttpJob::CancelAuth() { |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 315 | // Proxy gets set first, then WWW. |
[email protected] | a9bb6f69 | 2008-07-30 16:40:10 | [diff] [blame] | 316 | if (proxy_auth_state_ == net::AUTH_STATE_NEED_AUTH) { |
| 317 | proxy_auth_state_ = net::AUTH_STATE_CANCELED; |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 318 | } else { |
[email protected] | a9bb6f69 | 2008-07-30 16:40:10 | [diff] [blame] | 319 | DCHECK(server_auth_state_ == net::AUTH_STATE_NEED_AUTH); |
| 320 | server_auth_state_ = net::AUTH_STATE_CANCELED; |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 321 | } |
| 322 | |
| 323 | // These will be reset in OnStartCompleted. |
| 324 | response_info_ = NULL; |
| 325 | response_cookies_.clear(); |
| 326 | |
| 327 | // OK, let the consumer read the error page... |
| 328 | // |
| 329 | // Because we set the AUTH_STATE_CANCELED flag, NeedsAuth will return false, |
| 330 | // which will cause the consumer to receive OnResponseStarted instead of |
| 331 | // OnAuthRequired. |
| 332 | // |
| 333 | // We have to do this via InvokeLater to avoid "recursing" the consumer. |
| 334 | // |
| 335 | MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 336 | this, &URLRequestHttpJob::OnStartCompleted, net::OK)); |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 337 | } |
| 338 | |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 339 | void URLRequestHttpJob::ContinueDespiteLastError() { |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 340 | DCHECK(transaction_); |
| 341 | DCHECK(!response_info_) << "should not have a response yet"; |
| 342 | |
| 343 | // No matter what, we want to report our status as IO pending since we will |
| 344 | // be notifying our consumer asynchronously via OnStartCompleted. |
| 345 | SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); |
| 346 | |
| 347 | int rv = transaction_->RestartIgnoringLastError(&start_callback_); |
| 348 | if (rv == net::ERR_IO_PENDING) |
| 349 | return; |
| 350 | |
| 351 | // The transaction started synchronously, but we need to notify the |
| 352 | // URLRequest delegate via the message loop. |
| 353 | MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 354 | this, &URLRequestHttpJob::OnStartCompleted, rv)); |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 355 | } |
| 356 | |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 357 | bool URLRequestHttpJob::GetMoreData() { |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 358 | return transaction_ && !read_in_progress_; |
| 359 | } |
| 360 | |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 361 | bool URLRequestHttpJob::ReadRawData(char* buf, int buf_size, int *bytes_read) { |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 362 | DCHECK_NE(buf_size, 0); |
| 363 | DCHECK(bytes_read); |
| 364 | DCHECK(!read_in_progress_); |
| 365 | |
| 366 | int rv = transaction_->Read(buf, buf_size, &read_callback_); |
| 367 | if (rv >= 0) { |
| 368 | *bytes_read = rv; |
| 369 | return true; |
| 370 | } |
| 371 | |
| 372 | if (rv == net::ERR_IO_PENDING) { |
| 373 | read_in_progress_ = true; |
| 374 | SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); |
| 375 | } else { |
| 376 | NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv)); |
| 377 | } |
| 378 | |
| 379 | return false; |
| 380 | } |
| 381 | |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 382 | void URLRequestHttpJob::OnStartCompleted(int result) { |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 383 | // If the request was destroyed, then there is no more work to do. |
| 384 | if (!request_ || !request_->delegate()) |
| 385 | return; |
| 386 | |
| 387 | // If the transaction was destroyed, then the job was cancelled, and |
| 388 | // we can just ignore this notification. |
| 389 | if (!transaction_) |
| 390 | return; |
| 391 | |
| 392 | // Clear the IO_PENDING status |
| 393 | SetStatus(URLRequestStatus()); |
| 394 | |
| 395 | if (result == net::OK) { |
| 396 | NotifyHeadersComplete(); |
| 397 | } else if (net::IsCertificateError(result)) { |
| 398 | // We encountered an SSL certificate error. Ask our delegate to decide |
| 399 | // what we should do. |
| 400 | // TODO(wtc): also pass ssl_info.cert_status, or just pass the whole |
| 401 | // ssl_info. |
| 402 | request_->delegate()->OnSSLCertificateError( |
| 403 | request_, result, transaction_->GetResponseInfo()->ssl_info.cert); |
| 404 | } else { |
| 405 | NotifyStartError(URLRequestStatus(URLRequestStatus::FAILED, result)); |
| 406 | } |
| 407 | } |
| 408 | |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 409 | void URLRequestHttpJob::OnReadCompleted(int result) { |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 410 | read_in_progress_ = false; |
| 411 | |
| 412 | if (result == 0) { |
| 413 | NotifyDone(URLRequestStatus()); |
| 414 | } else if (result < 0) { |
| 415 | NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result)); |
| 416 | } else { |
| 417 | // Clear the IO_PENDING status |
| 418 | SetStatus(URLRequestStatus()); |
| 419 | } |
| 420 | |
| 421 | NotifyReadComplete(result); |
| 422 | } |
| 423 | |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 424 | void URLRequestHttpJob::NotifyHeadersComplete() { |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 425 | DCHECK(!response_info_); |
| 426 | |
| 427 | response_info_ = transaction_->GetResponseInfo(); |
| 428 | |
| 429 | // Get the Set-Cookie values, and send them to our cookie database. |
| 430 | |
| 431 | FetchResponseCookies(); |
| 432 | |
| 433 | URLRequestContext* ctx = request_->context(); |
| 434 | if (ctx && ctx->cookie_store() && |
| 435 | ctx->cookie_policy()->CanSetCookie(request_->url(), |
| 436 | request_->policy_url())) |
| 437 | ctx->cookie_store()->SetCookies(request_->url(), response_cookies_); |
| 438 | |
| 439 | URLRequestJob::NotifyHeadersComplete(); |
| 440 | } |
| 441 | |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 442 | void URLRequestHttpJob::DestroyTransaction() { |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 443 | DCHECK(transaction_); |
| 444 | |
| 445 | transaction_->Destroy(); |
| 446 | transaction_ = NULL; |
| 447 | response_info_ = NULL; |
| 448 | } |
| 449 | |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 450 | void URLRequestHttpJob::StartTransaction() { |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 451 | // NOTE: This method assumes that request_info_ is already setup properly. |
| 452 | |
| 453 | // Create a transaction. |
| 454 | DCHECK(!transaction_); |
| 455 | |
| 456 | DCHECK(request_->context()); |
| 457 | DCHECK(request_->context()->http_transaction_factory()); |
| 458 | |
| 459 | transaction_ = |
| 460 | request_->context()->http_transaction_factory()->CreateTransaction(); |
| 461 | |
| 462 | // No matter what, we want to report our status as IO pending since we will |
| 463 | // be notifying our consumer asynchronously via OnStartCompleted. |
| 464 | SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); |
| 465 | |
| 466 | int rv; |
| 467 | if (transaction_) { |
| 468 | rv = transaction_->Start(&request_info_, &start_callback_); |
| 469 | if (rv == net::ERR_IO_PENDING) |
| 470 | return; |
| 471 | } else { |
| 472 | rv = net::ERR_FAILED; |
| 473 | } |
| 474 | |
| 475 | // The transaction started synchronously, but we need to notify the |
| 476 | // URLRequest delegate via the message loop. |
| 477 | MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 478 | this, &URLRequestHttpJob::OnStartCompleted, rv)); |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 479 | } |
| 480 | |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 481 | void URLRequestHttpJob::AddExtraHeaders() { |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 482 | URLRequestContext* context = request_->context(); |
| 483 | if (context) { |
| 484 | // Add in the cookie header. TODO might we need more than one header? |
| 485 | if (context->cookie_store() && |
| 486 | context->cookie_policy()->CanGetCookies(request_->url(), |
| 487 | request_->policy_url())) { |
| 488 | std::string cookies = request_->context()->cookie_store()-> |
| 489 | GetCookiesWithOptions(request_->url(), |
[email protected] | 8ac1a75 | 2008-07-31 19:40:37 | [diff] [blame] | 490 | net::CookieMonster::INCLUDE_HTTPONLY); |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 491 | if (!cookies.empty()) |
| 492 | request_info_.extra_headers += "Cookie: " + cookies + "\r\n"; |
| 493 | } |
| 494 | if (!context->accept_language().empty()) |
| 495 | request_info_.extra_headers += "Accept-Language: " + |
| 496 | context->accept_language() + "\r\n"; |
| 497 | if (!context->accept_charset().empty()) |
| 498 | request_info_.extra_headers += "Accept-Charset: " + |
| 499 | context->accept_charset() + "\r\n"; |
| 500 | } |
| 501 | |
| 502 | #ifdef CHROME_LAST_MINUTE |
| 503 | // Tell the server what compression formats we support. |
| 504 | request_info_.extra_headers += "Accept-Encoding: gzip,deflate,bzip2\r\n"; |
| 505 | #else |
| 506 | // Tell the server that we support gzip/deflate encoding. |
| 507 | request_info_.extra_headers += "Accept-Encoding: gzip,deflate"; |
| 508 | |
| 509 | // const string point to google domain |
| 510 | static const char kGoogleDomain[] = "google.com"; |
| 511 | static const unsigned int kGoogleDomainLen = arraysize(kGoogleDomain) - 1; |
| 512 | static const char kLocalHostName[] = "localhost"; |
| 513 | |
| 514 | // At now, only support bzip2 feature for those requests which are |
| 515 | // sent to google domain or localhost. |
| 516 | // TODO(jnd) : we will remove the "google.com" domain check before launch. |
| 517 | // See bug : 861940 |
| 518 | const std::string &host = request_->url().host(); |
| 519 | |
| 520 | if (host == kLocalHostName || |
| 521 | request_->url().DomainIs(kGoogleDomain, kGoogleDomainLen)) { |
| 522 | request_info_.extra_headers += ",bzip2\r\n"; |
| 523 | } else { |
| 524 | request_info_.extra_headers += "\r\n"; |
| 525 | } |
| 526 | #endif |
| 527 | } |
| 528 | |
[email protected] | 175adac | 2008-07-30 17:28:04 | [diff] [blame] | 529 | void URLRequestHttpJob::FetchResponseCookies() { |
initial.commit | 586acc5fe | 2008-07-26 22:42:52 | [diff] [blame] | 530 | DCHECK(response_info_); |
| 531 | DCHECK(response_cookies_.empty()); |
| 532 | |
| 533 | std::string name = "Set-Cookie"; |
| 534 | std::string value; |
| 535 | |
| 536 | void* iter = NULL; |
| 537 | while (response_info_->headers->EnumerateHeader(&iter, name, &value)) |
| 538 | response_cookies_.push_back(value); |
| 539 | } |