| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "net/url_request/url_fetcher_core.h" | |
| 6 | |
| 7 #include <stdint.h> | |
| 8 | |
| 9 #include "base/bind.h" | |
| 10 #include "base/logging.h" | |
| 11 #include "base/metrics/histogram.h" | |
| 12 #include "base/profiler/scoped_tracker.h" | |
| 13 #include "base/sequenced_task_runner.h" | |
| 14 #include "base/single_thread_task_runner.h" | |
| 15 #include "base/stl_util.h" | |
| 16 #include "base/thread_task_runner_handle.h" | |
| 17 #include "base/tracked_objects.h" | |
| 18 #include "net/base/elements_upload_data_stream.h" | |
| 19 #include "net/base/io_buffer.h" | |
| 20 #include "net/base/load_flags.h" | |
| 21 #include "net/base/net_errors.h" | |
| 22 #include "net/base/request_priority.h" | |
| 23 #include "net/base/upload_bytes_element_reader.h" | |
| 24 #include "net/base/upload_data_stream.h" | |
| 25 #include "net/base/upload_file_element_reader.h" | |
| 26 #include "net/http/http_response_headers.h" | |
| 27 #include "net/url_request/redirect_info.h" | |
| 28 #include "net/url_request/url_fetcher_delegate.h" | |
| 29 #include "net/url_request/url_fetcher_response_writer.h" | |
| 30 #include "net/url_request/url_request_context.h" | |
| 31 #include "net/url_request/url_request_context_getter.h" | |
| 32 #include "net/url_request/url_request_throttler_manager.h" | |
| 33 | |
| 34 namespace { | |
| 35 | |
| 36 const int kBufferSize = 4096; | |
| 37 const int kUploadProgressTimerInterval = 100; | |
| 38 bool g_ignore_certificate_requests = false; | |
| 39 | |
| 40 void EmptyCompletionCallback(int result) {} | |
| 41 | |
| 42 } // namespace | |
| 43 | |
| 44 namespace net { | |
| 45 | |
| 46 // URLFetcherCore::Registry --------------------------------------------------- | |
| 47 | |
| 48 URLFetcherCore::Registry::Registry() {} | |
| 49 URLFetcherCore::Registry::~Registry() {} | |
| 50 | |
| 51 void URLFetcherCore::Registry::AddURLFetcherCore(URLFetcherCore* core) { | |
| 52 DCHECK(!ContainsKey(fetchers_, core)); | |
| 53 fetchers_.insert(core); | |
| 54 } | |
| 55 | |
| 56 void URLFetcherCore::Registry::RemoveURLFetcherCore(URLFetcherCore* core) { | |
| 57 DCHECK(ContainsKey(fetchers_, core)); | |
| 58 fetchers_.erase(core); | |
| 59 } | |
| 60 | |
| 61 void URLFetcherCore::Registry::CancelAll() { | |
| 62 while (!fetchers_.empty()) | |
| 63 (*fetchers_.begin())->CancelURLRequest(ERR_ABORTED); | |
| 64 } | |
| 65 | |
| 66 // URLFetcherCore ------------------------------------------------------------- | |
| 67 | |
| 68 // static | |
| 69 base::LazyInstance<URLFetcherCore::Registry> | |
| 70 URLFetcherCore::g_registry = LAZY_INSTANCE_INITIALIZER; | |
| 71 | |
| 72 URLFetcherCore::URLFetcherCore(URLFetcher* fetcher, | |
| 73 const GURL& original_url, | |
| 74 URLFetcher::RequestType request_type, | |
| 75 URLFetcherDelegate* d) | |
| 76 : fetcher_(fetcher), | |
| 77 original_url_(original_url), | |
| 78 request_type_(request_type), | |
| 79 delegate_(d), | |
| 80 delegate_task_runner_(base::ThreadTaskRunnerHandle::Get()), | |
| 81 load_flags_(LOAD_NORMAL), | |
| 82 response_code_(URLFetcher::RESPONSE_CODE_INVALID), | |
| 83 buffer_(new IOBuffer(kBufferSize)), | |
| 84 url_request_data_key_(NULL), | |
| 85 was_fetched_via_proxy_(false), | |
| 86 upload_content_set_(false), | |
| 87 upload_range_offset_(0), | |
| 88 upload_range_length_(0), | |
| 89 referrer_policy_( | |
| 90 URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE), | |
| 91 is_chunked_upload_(false), | |
| 92 was_cancelled_(false), | |
| 93 stop_on_redirect_(false), | |
| 94 stopped_on_redirect_(false), | |
| 95 automatically_retry_on_5xx_(true), | |
| 96 num_retries_on_5xx_(0), | |
| 97 max_retries_on_5xx_(0), | |
| 98 num_retries_on_network_changes_(0), | |
| 99 max_retries_on_network_changes_(0), | |
| 100 current_upload_bytes_(-1), | |
| 101 current_response_bytes_(0), | |
| 102 total_response_bytes_(-1) { | |
| 103 CHECK(original_url_.is_valid()); | |
| 104 } | |
| 105 | |
| 106 void URLFetcherCore::Start() { | |
| 107 DCHECK(delegate_task_runner_.get()); | |
| 108 DCHECK(request_context_getter_.get()) << "We need an URLRequestContext!"; | |
| 109 if (network_task_runner_.get()) { | |
| 110 DCHECK_EQ(network_task_runner_, | |
| 111 request_context_getter_->GetNetworkTaskRunner()); | |
| 112 } else { | |
| 113 network_task_runner_ = request_context_getter_->GetNetworkTaskRunner(); | |
| 114 } | |
| 115 DCHECK(network_task_runner_.get()) << "We need an IO task runner"; | |
| 116 | |
| 117 network_task_runner_->PostTask( | |
| 118 FROM_HERE, base::Bind(&URLFetcherCore::StartOnIOThread, this)); | |
| 119 } | |
| 120 | |
| 121 void URLFetcherCore::Stop() { | |
| 122 if (delegate_task_runner_.get()) // May be NULL in tests. | |
| 123 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); | |
| 124 | |
| 125 delegate_ = NULL; | |
| 126 fetcher_ = NULL; | |
| 127 if (!network_task_runner_.get()) | |
| 128 return; | |
| 129 if (network_task_runner_->RunsTasksOnCurrentThread()) { | |
| 130 CancelURLRequest(ERR_ABORTED); | |
| 131 } else { | |
| 132 network_task_runner_->PostTask( | |
| 133 FROM_HERE, | |
| 134 base::Bind(&URLFetcherCore::CancelURLRequest, this, ERR_ABORTED)); | |
| 135 } | |
| 136 } | |
| 137 | |
| 138 void URLFetcherCore::SetUploadData(const std::string& upload_content_type, | |
| 139 const std::string& upload_content) { | |
| 140 AssertHasNoUploadData(); | |
| 141 DCHECK(!is_chunked_upload_); | |
| 142 DCHECK(upload_content_type_.empty()); | |
| 143 | |
| 144 // Empty |upload_content_type| is allowed iff the |upload_content| is empty. | |
| 145 DCHECK(upload_content.empty() || !upload_content_type.empty()); | |
| 146 | |
| 147 upload_content_type_ = upload_content_type; | |
| 148 upload_content_ = upload_content; | |
| 149 upload_content_set_ = true; | |
| 150 } | |
| 151 | |
| 152 void URLFetcherCore::SetUploadFilePath( | |
| 153 const std::string& upload_content_type, | |
| 154 const base::FilePath& file_path, | |
| 155 uint64 range_offset, | |
| 156 uint64 range_length, | |
| 157 scoped_refptr<base::TaskRunner> file_task_runner) { | |
| 158 AssertHasNoUploadData(); | |
| 159 DCHECK(!is_chunked_upload_); | |
| 160 DCHECK_EQ(upload_range_offset_, 0ULL); | |
| 161 DCHECK_EQ(upload_range_length_, 0ULL); | |
| 162 DCHECK(upload_content_type_.empty()); | |
| 163 DCHECK(!upload_content_type.empty()); | |
| 164 | |
| 165 upload_content_type_ = upload_content_type; | |
| 166 upload_file_path_ = file_path; | |
| 167 upload_range_offset_ = range_offset; | |
| 168 upload_range_length_ = range_length; | |
| 169 upload_file_task_runner_ = file_task_runner; | |
| 170 upload_content_set_ = true; | |
| 171 } | |
| 172 | |
| 173 void URLFetcherCore::SetUploadStreamFactory( | |
| 174 const std::string& upload_content_type, | |
| 175 const URLFetcher::CreateUploadStreamCallback& factory) { | |
| 176 AssertHasNoUploadData(); | |
| 177 DCHECK(!is_chunked_upload_); | |
| 178 DCHECK(upload_content_type_.empty()); | |
| 179 | |
| 180 upload_content_type_ = upload_content_type; | |
| 181 upload_stream_factory_ = factory; | |
| 182 upload_content_set_ = true; | |
| 183 } | |
| 184 | |
| 185 void URLFetcherCore::SetChunkedUpload(const std::string& content_type) { | |
| 186 if (!is_chunked_upload_) { | |
| 187 AssertHasNoUploadData(); | |
| 188 DCHECK(upload_content_type_.empty()); | |
| 189 } | |
| 190 | |
| 191 // Empty |content_type| is not allowed here, because it is impossible | |
| 192 // to ensure non-empty upload content as it is not yet supplied. | |
| 193 DCHECK(!content_type.empty()); | |
| 194 | |
| 195 upload_content_type_ = content_type; | |
| 196 upload_content_.clear(); | |
| 197 is_chunked_upload_ = true; | |
| 198 } | |
| 199 | |
| 200 void URLFetcherCore::AppendChunkToUpload(const std::string& content, | |
| 201 bool is_last_chunk) { | |
| 202 DCHECK(delegate_task_runner_.get()); | |
| 203 DCHECK(network_task_runner_.get()); | |
| 204 network_task_runner_->PostTask( | |
| 205 FROM_HERE, | |
| 206 base::Bind(&URLFetcherCore::CompleteAddingUploadDataChunk, this, content, | |
| 207 is_last_chunk)); | |
| 208 } | |
| 209 | |
| 210 void URLFetcherCore::SetLoadFlags(int load_flags) { | |
| 211 load_flags_ = load_flags; | |
| 212 } | |
| 213 | |
| 214 int URLFetcherCore::GetLoadFlags() const { | |
| 215 return load_flags_; | |
| 216 } | |
| 217 | |
| 218 void URLFetcherCore::SetReferrer(const std::string& referrer) { | |
| 219 referrer_ = referrer; | |
| 220 } | |
| 221 | |
| 222 void URLFetcherCore::SetReferrerPolicy( | |
| 223 URLRequest::ReferrerPolicy referrer_policy) { | |
| 224 referrer_policy_ = referrer_policy; | |
| 225 } | |
| 226 | |
| 227 void URLFetcherCore::SetExtraRequestHeaders( | |
| 228 const std::string& extra_request_headers) { | |
| 229 extra_request_headers_.Clear(); | |
| 230 extra_request_headers_.AddHeadersFromString(extra_request_headers); | |
| 231 } | |
| 232 | |
| 233 void URLFetcherCore::AddExtraRequestHeader(const std::string& header_line) { | |
| 234 extra_request_headers_.AddHeaderFromString(header_line); | |
| 235 } | |
| 236 | |
| 237 void URLFetcherCore::SetRequestContext( | |
| 238 URLRequestContextGetter* request_context_getter) { | |
| 239 DCHECK(!request_context_getter_.get()); | |
| 240 DCHECK(request_context_getter); | |
| 241 request_context_getter_ = request_context_getter; | |
| 242 } | |
| 243 | |
| 244 void URLFetcherCore::SetFirstPartyForCookies( | |
| 245 const GURL& first_party_for_cookies) { | |
| 246 DCHECK(first_party_for_cookies_.is_empty()); | |
| 247 first_party_for_cookies_ = first_party_for_cookies; | |
| 248 } | |
| 249 | |
| 250 void URLFetcherCore::SetURLRequestUserData( | |
| 251 const void* key, | |
| 252 const URLFetcher::CreateDataCallback& create_data_callback) { | |
| 253 DCHECK(key); | |
| 254 DCHECK(!create_data_callback.is_null()); | |
| 255 url_request_data_key_ = key; | |
| 256 url_request_create_data_callback_ = create_data_callback; | |
| 257 } | |
| 258 | |
| 259 void URLFetcherCore::SetStopOnRedirect(bool stop_on_redirect) { | |
| 260 stop_on_redirect_ = stop_on_redirect; | |
| 261 } | |
| 262 | |
| 263 void URLFetcherCore::SetAutomaticallyRetryOn5xx(bool retry) { | |
| 264 automatically_retry_on_5xx_ = retry; | |
| 265 } | |
| 266 | |
| 267 void URLFetcherCore::SetMaxRetriesOn5xx(int max_retries) { | |
| 268 max_retries_on_5xx_ = max_retries; | |
| 269 } | |
| 270 | |
| 271 int URLFetcherCore::GetMaxRetriesOn5xx() const { | |
| 272 return max_retries_on_5xx_; | |
| 273 } | |
| 274 | |
| 275 base::TimeDelta URLFetcherCore::GetBackoffDelay() const { | |
| 276 return backoff_delay_; | |
| 277 } | |
| 278 | |
| 279 void URLFetcherCore::SetAutomaticallyRetryOnNetworkChanges(int max_retries) { | |
| 280 max_retries_on_network_changes_ = max_retries; | |
| 281 } | |
| 282 | |
| 283 void URLFetcherCore::SaveResponseToFileAtPath( | |
| 284 const base::FilePath& file_path, | |
| 285 scoped_refptr<base::SequencedTaskRunner> file_task_runner) { | |
| 286 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); | |
| 287 SaveResponseWithWriter(scoped_ptr<URLFetcherResponseWriter>( | |
| 288 new URLFetcherFileWriter(file_task_runner, file_path))); | |
| 289 } | |
| 290 | |
| 291 void URLFetcherCore::SaveResponseToTemporaryFile( | |
| 292 scoped_refptr<base::SequencedTaskRunner> file_task_runner) { | |
| 293 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); | |
| 294 SaveResponseWithWriter(scoped_ptr<URLFetcherResponseWriter>( | |
| 295 new URLFetcherFileWriter(file_task_runner, base::FilePath()))); | |
| 296 } | |
| 297 | |
| 298 void URLFetcherCore::SaveResponseWithWriter( | |
| 299 scoped_ptr<URLFetcherResponseWriter> response_writer) { | |
| 300 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); | |
| 301 response_writer_ = response_writer.Pass(); | |
| 302 } | |
| 303 | |
| 304 HttpResponseHeaders* URLFetcherCore::GetResponseHeaders() const { | |
| 305 return response_headers_.get(); | |
| 306 } | |
| 307 | |
| 308 // TODO(panayiotis): socket_address_ is written in the IO thread, | |
| 309 // if this is accessed in the UI thread, this could result in a race. | |
| 310 // Same for response_headers_ above and was_fetched_via_proxy_ below. | |
| 311 HostPortPair URLFetcherCore::GetSocketAddress() const { | |
| 312 return socket_address_; | |
| 313 } | |
| 314 | |
| 315 bool URLFetcherCore::WasFetchedViaProxy() const { | |
| 316 return was_fetched_via_proxy_; | |
| 317 } | |
| 318 | |
| 319 const GURL& URLFetcherCore::GetOriginalURL() const { | |
| 320 return original_url_; | |
| 321 } | |
| 322 | |
| 323 const GURL& URLFetcherCore::GetURL() const { | |
| 324 return url_; | |
| 325 } | |
| 326 | |
| 327 const URLRequestStatus& URLFetcherCore::GetStatus() const { | |
| 328 return status_; | |
| 329 } | |
| 330 | |
| 331 int URLFetcherCore::GetResponseCode() const { | |
| 332 return response_code_; | |
| 333 } | |
| 334 | |
| 335 const ResponseCookies& URLFetcherCore::GetCookies() const { | |
| 336 return cookies_; | |
| 337 } | |
| 338 | |
| 339 void URLFetcherCore::ReceivedContentWasMalformed() { | |
| 340 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); | |
| 341 if (network_task_runner_.get()) { | |
| 342 network_task_runner_->PostTask( | |
| 343 FROM_HERE, base::Bind(&URLFetcherCore::NotifyMalformedContent, this)); | |
| 344 } | |
| 345 } | |
| 346 | |
| 347 bool URLFetcherCore::GetResponseAsString( | |
| 348 std::string* out_response_string) const { | |
| 349 URLFetcherStringWriter* string_writer = | |
| 350 response_writer_ ? response_writer_->AsStringWriter() : NULL; | |
| 351 if (!string_writer) | |
| 352 return false; | |
| 353 | |
| 354 *out_response_string = string_writer->data(); | |
| 355 UMA_HISTOGRAM_MEMORY_KB("UrlFetcher.StringResponseSize", | |
| 356 (string_writer->data().length() / 1024)); | |
| 357 return true; | |
| 358 } | |
| 359 | |
| 360 bool URLFetcherCore::GetResponseAsFilePath(bool take_ownership, | |
| 361 base::FilePath* out_response_path) { | |
| 362 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); | |
| 363 | |
| 364 URLFetcherFileWriter* file_writer = | |
| 365 response_writer_ ? response_writer_->AsFileWriter() : NULL; | |
| 366 if (!file_writer) | |
| 367 return false; | |
| 368 | |
| 369 *out_response_path = file_writer->file_path(); | |
| 370 | |
| 371 if (take_ownership) { | |
| 372 // Intentionally calling a file_writer_ method directly without posting | |
| 373 // the task to network_task_runner_. | |
| 374 // | |
| 375 // This is for correctly handling the case when file_writer_->DisownFile() | |
| 376 // is soon followed by URLFetcherCore::Stop(). We have to make sure that | |
| 377 // DisownFile takes effect before Stop deletes file_writer_. | |
| 378 // | |
| 379 // This direct call should be thread-safe, since DisownFile itself does no | |
| 380 // file operation. It just flips the state to be referred in destruction. | |
| 381 file_writer->DisownFile(); | |
| 382 } | |
| 383 return true; | |
| 384 } | |
| 385 | |
| 386 void URLFetcherCore::OnReceivedRedirect(URLRequest* request, | |
| 387 const RedirectInfo& redirect_info, | |
| 388 bool* defer_redirect) { | |
| 389 DCHECK_EQ(request, request_.get()); | |
| 390 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
| 391 if (stop_on_redirect_) { | |
| 392 stopped_on_redirect_ = true; | |
| 393 url_ = redirect_info.new_url; | |
| 394 response_code_ = request_->GetResponseCode(); | |
| 395 was_fetched_via_proxy_ = request_->was_fetched_via_proxy(); | |
| 396 request->Cancel(); | |
| 397 OnReadCompleted(request, 0); | |
| 398 } | |
| 399 } | |
| 400 | |
| 401 void URLFetcherCore::OnResponseStarted(URLRequest* request) { | |
| 402 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
| 403 tracked_objects::ScopedTracker tracking_profile( | |
| 404 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 405 "423948 URLFetcherCore::OnResponseStarted")); | |
| 406 | |
| 407 DCHECK_EQ(request, request_.get()); | |
| 408 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
| 409 if (request_->status().is_success()) { | |
| 410 response_code_ = request_->GetResponseCode(); | |
| 411 response_headers_ = request_->response_headers(); | |
| 412 socket_address_ = request_->GetSocketAddress(); | |
| 413 was_fetched_via_proxy_ = request_->was_fetched_via_proxy(); | |
| 414 total_response_bytes_ = request_->GetExpectedContentSize(); | |
| 415 } | |
| 416 | |
| 417 ReadResponse(); | |
| 418 } | |
| 419 | |
| 420 void URLFetcherCore::OnCertificateRequested( | |
| 421 URLRequest* request, | |
| 422 SSLCertRequestInfo* cert_request_info) { | |
| 423 DCHECK_EQ(request, request_.get()); | |
| 424 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
| 425 | |
| 426 if (g_ignore_certificate_requests) { | |
| 427 request->ContinueWithCertificate(NULL); | |
| 428 } else { | |
| 429 request->Cancel(); | |
| 430 } | |
| 431 } | |
| 432 | |
| 433 void URLFetcherCore::OnReadCompleted(URLRequest* request, | |
| 434 int bytes_read) { | |
| 435 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
| 436 tracked_objects::ScopedTracker tracking_profile( | |
| 437 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 438 "423948 URLFetcherCore::OnReadCompleted")); | |
| 439 | |
| 440 DCHECK(request == request_); | |
| 441 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
| 442 | |
| 443 if (!stopped_on_redirect_) | |
| 444 url_ = request->url(); | |
| 445 URLRequestThrottlerManager* throttler_manager = | |
| 446 request->context()->throttler_manager(); | |
| 447 if (throttler_manager) { | |
| 448 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
| 449 tracked_objects::ScopedTracker tracking_profile1( | |
| 450 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 451 "423948 URLFetcherCore::OnReadCompleted1")); | |
| 452 | |
| 453 url_throttler_entry_ = throttler_manager->RegisterRequestUrl(url_); | |
| 454 } | |
| 455 | |
| 456 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
| 457 tracked_objects::ScopedTracker tracking_profile2( | |
| 458 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 459 "423948 URLFetcherCore::OnReadCompleted2")); | |
| 460 | |
| 461 do { | |
| 462 if (!request_->status().is_success() || bytes_read <= 0) | |
| 463 break; | |
| 464 | |
| 465 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
| 466 tracked_objects::ScopedTracker tracking_profile3( | |
| 467 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 468 "423948 URLFetcherCore::OnReadCompleted3")); | |
| 469 | |
| 470 current_response_bytes_ += bytes_read; | |
| 471 InformDelegateDownloadProgress(); | |
| 472 | |
| 473 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
| 474 tracked_objects::ScopedTracker tracking_profile4( | |
| 475 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 476 "423948 URLFetcherCore::OnReadCompleted4")); | |
| 477 | |
| 478 const int result = | |
| 479 WriteBuffer(new DrainableIOBuffer(buffer_.get(), bytes_read)); | |
| 480 if (result < 0) { | |
| 481 // Write failed or waiting for write completion. | |
| 482 return; | |
| 483 } | |
| 484 } while (request_->Read(buffer_.get(), kBufferSize, &bytes_read)); | |
| 485 | |
| 486 const URLRequestStatus status = request_->status(); | |
| 487 | |
| 488 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
| 489 tracked_objects::ScopedTracker tracking_profile5( | |
| 490 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 491 "423948 URLFetcherCore::OnReadCompleted5")); | |
| 492 | |
| 493 if (status.is_success()) | |
| 494 request_->GetResponseCookies(&cookies_); | |
| 495 | |
| 496 // See comments re: HEAD requests in ReadResponse(). | |
| 497 if (!status.is_io_pending() || request_type_ == URLFetcher::HEAD) { | |
| 498 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
| 499 tracked_objects::ScopedTracker tracking_profile6( | |
| 500 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 501 "423948 URLFetcherCore::OnReadCompleted6")); | |
| 502 | |
| 503 status_ = status; | |
| 504 ReleaseRequest(); | |
| 505 | |
| 506 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
| 507 tracked_objects::ScopedTracker tracking_profile7( | |
| 508 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 509 "423948 URLFetcherCore::OnReadCompleted7")); | |
| 510 | |
| 511 // No more data to write. | |
| 512 const int result = response_writer_->Finish( | |
| 513 base::Bind(&URLFetcherCore::DidFinishWriting, this)); | |
| 514 | |
| 515 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
| 516 tracked_objects::ScopedTracker tracking_profile8( | |
| 517 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 518 "423948 URLFetcherCore::OnReadCompleted8")); | |
| 519 | |
| 520 if (result != ERR_IO_PENDING) | |
| 521 DidFinishWriting(result); | |
| 522 } | |
| 523 } | |
| 524 | |
| 525 void URLFetcherCore::CancelAll() { | |
| 526 g_registry.Get().CancelAll(); | |
| 527 } | |
| 528 | |
| 529 int URLFetcherCore::GetNumFetcherCores() { | |
| 530 return g_registry.Get().size(); | |
| 531 } | |
| 532 | |
| 533 void URLFetcherCore::SetIgnoreCertificateRequests(bool ignored) { | |
| 534 g_ignore_certificate_requests = ignored; | |
| 535 } | |
| 536 | |
| 537 URLFetcherCore::~URLFetcherCore() { | |
| 538 // |request_| should be NULL. If not, it's unsafe to delete it here since we | |
| 539 // may not be on the IO thread. | |
| 540 DCHECK(!request_.get()); | |
| 541 } | |
| 542 | |
| 543 void URLFetcherCore::StartOnIOThread() { | |
| 544 // TODO(pkasting): Remove ScopedTracker below once crbug.com/456327 is fixed. | |
| 545 tracked_objects::ScopedTracker tracking_profile( | |
| 546 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 547 "456327 URLFetcherCore::StartOnIOThread")); | |
| 548 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
| 549 | |
| 550 if (!response_writer_) | |
| 551 response_writer_.reset(new URLFetcherStringWriter); | |
| 552 | |
| 553 const int result = response_writer_->Initialize( | |
| 554 base::Bind(&URLFetcherCore::DidInitializeWriter, this)); | |
| 555 if (result != ERR_IO_PENDING) | |
| 556 DidInitializeWriter(result); | |
| 557 } | |
| 558 | |
| 559 void URLFetcherCore::StartURLRequest() { | |
| 560 // TODO(pkasting): Remove ScopedTracker below once crbug.com/456327 is fixed. | |
| 561 tracked_objects::ScopedTracker tracking_profile( | |
| 562 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 563 "456327 URLFetcherCore::StartURLRequest")); | |
| 564 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
| 565 | |
| 566 if (was_cancelled_) { | |
| 567 // Since StartURLRequest() is posted as a *delayed* task, it may | |
| 568 // run after the URLFetcher was already stopped. | |
| 569 return; | |
| 570 } | |
| 571 | |
| 572 DCHECK(request_context_getter_.get()); | |
| 573 DCHECK(!request_.get()); | |
| 574 | |
| 575 g_registry.Get().AddURLFetcherCore(this); | |
| 576 current_response_bytes_ = 0; | |
| 577 request_ = request_context_getter_->GetURLRequestContext()->CreateRequest( | |
| 578 original_url_, DEFAULT_PRIORITY, this, NULL); | |
| 579 request_->set_stack_trace(stack_trace_); | |
| 580 int flags = request_->load_flags() | load_flags_; | |
| 581 | |
| 582 if (is_chunked_upload_) | |
| 583 request_->EnableChunkedUpload(); | |
| 584 request_->SetLoadFlags(flags); | |
| 585 request_->SetReferrer(referrer_); | |
| 586 request_->set_referrer_policy(referrer_policy_); | |
| 587 request_->set_first_party_for_cookies(first_party_for_cookies_.is_empty() ? | |
| 588 original_url_ : first_party_for_cookies_); | |
| 589 if (url_request_data_key_ && !url_request_create_data_callback_.is_null()) { | |
| 590 request_->SetUserData(url_request_data_key_, | |
| 591 url_request_create_data_callback_.Run()); | |
| 592 } | |
| 593 | |
| 594 switch (request_type_) { | |
| 595 case URLFetcher::GET: | |
| 596 break; | |
| 597 | |
| 598 case URLFetcher::POST: | |
| 599 case URLFetcher::PUT: | |
| 600 case URLFetcher::PATCH: | |
| 601 // Upload content must be set. | |
| 602 DCHECK(is_chunked_upload_ || upload_content_set_); | |
| 603 | |
| 604 request_->set_method( | |
| 605 request_type_ == URLFetcher::POST ? "POST" : | |
| 606 request_type_ == URLFetcher::PUT ? "PUT" : "PATCH"); | |
| 607 if (!upload_content_type_.empty()) { | |
| 608 extra_request_headers_.SetHeader(HttpRequestHeaders::kContentType, | |
| 609 upload_content_type_); | |
| 610 } | |
| 611 if (!upload_content_.empty()) { | |
| 612 scoped_ptr<UploadElementReader> reader(new UploadBytesElementReader( | |
| 613 upload_content_.data(), upload_content_.size())); | |
| 614 request_->set_upload( | |
| 615 ElementsUploadDataStream::CreateWithReader(reader.Pass(), 0)); | |
| 616 } else if (!upload_file_path_.empty()) { | |
| 617 scoped_ptr<UploadElementReader> reader( | |
| 618 new UploadFileElementReader(upload_file_task_runner_.get(), | |
| 619 upload_file_path_, | |
| 620 upload_range_offset_, | |
| 621 upload_range_length_, | |
| 622 base::Time())); | |
| 623 request_->set_upload( | |
| 624 ElementsUploadDataStream::CreateWithReader(reader.Pass(), 0)); | |
| 625 } else if (!upload_stream_factory_.is_null()) { | |
| 626 scoped_ptr<UploadDataStream> stream = upload_stream_factory_.Run(); | |
| 627 DCHECK(stream); | |
| 628 request_->set_upload(stream.Pass()); | |
| 629 } | |
| 630 | |
| 631 current_upload_bytes_ = -1; | |
| 632 // TODO(kinaba): http://crbug.com/118103. Implement upload callback in the | |
| 633 // layer and avoid using timer here. | |
| 634 upload_progress_checker_timer_.reset( | |
| 635 new base::RepeatingTimer<URLFetcherCore>()); | |
| 636 upload_progress_checker_timer_->Start( | |
| 637 FROM_HERE, | |
| 638 base::TimeDelta::FromMilliseconds(kUploadProgressTimerInterval), | |
| 639 this, | |
| 640 &URLFetcherCore::InformDelegateUploadProgress); | |
| 641 break; | |
| 642 | |
| 643 case URLFetcher::HEAD: | |
| 644 request_->set_method("HEAD"); | |
| 645 break; | |
| 646 | |
| 647 case URLFetcher::DELETE_REQUEST: | |
| 648 request_->set_method("DELETE"); | |
| 649 break; | |
| 650 | |
| 651 default: | |
| 652 NOTREACHED(); | |
| 653 } | |
| 654 | |
| 655 if (!extra_request_headers_.IsEmpty()) | |
| 656 request_->SetExtraRequestHeaders(extra_request_headers_); | |
| 657 | |
| 658 request_->Start(); | |
| 659 } | |
| 660 | |
| 661 void URLFetcherCore::DidInitializeWriter(int result) { | |
| 662 if (result != OK) { | |
| 663 CancelURLRequest(result); | |
| 664 delegate_task_runner_->PostTask( | |
| 665 FROM_HERE, | |
| 666 base::Bind(&URLFetcherCore::InformDelegateFetchIsComplete, this)); | |
| 667 return; | |
| 668 } | |
| 669 StartURLRequestWhenAppropriate(); | |
| 670 } | |
| 671 | |
| 672 void URLFetcherCore::StartURLRequestWhenAppropriate() { | |
| 673 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
| 674 | |
| 675 if (was_cancelled_) | |
| 676 return; | |
| 677 | |
| 678 DCHECK(request_context_getter_.get()); | |
| 679 | |
| 680 int64 delay = 0; | |
| 681 if (!original_url_throttler_entry_.get()) { | |
| 682 // TODO(pkasting): Remove ScopedTracker below once crbug.com/456327 is | |
| 683 // fixed. | |
| 684 tracked_objects::ScopedTracker tracking_profile1( | |
| 685 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 686 "456327 URLFetcherCore::StartURLRequestWhenAppropriate1")); | |
| 687 URLRequestThrottlerManager* manager = | |
| 688 request_context_getter_->GetURLRequestContext()->throttler_manager(); | |
| 689 if (manager) { | |
| 690 original_url_throttler_entry_ = | |
| 691 manager->RegisterRequestUrl(original_url_); | |
| 692 } | |
| 693 } | |
| 694 if (original_url_throttler_entry_.get()) { | |
| 695 // TODO(pkasting): Remove ScopedTracker below once crbug.com/456327 is | |
| 696 // fixed. | |
| 697 tracked_objects::ScopedTracker tracking_profile2( | |
| 698 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 699 "456327 URLFetcherCore::StartURLRequestWhenAppropriate2")); | |
| 700 delay = original_url_throttler_entry_->ReserveSendingTimeForNextRequest( | |
| 701 GetBackoffReleaseTime()); | |
| 702 } | |
| 703 | |
| 704 if (delay == 0) { | |
| 705 StartURLRequest(); | |
| 706 } else { | |
| 707 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( | |
| 708 FROM_HERE, base::Bind(&URLFetcherCore::StartURLRequest, this), | |
| 709 base::TimeDelta::FromMilliseconds(delay)); | |
| 710 } | |
| 711 } | |
| 712 | |
| 713 void URLFetcherCore::CancelURLRequest(int error) { | |
| 714 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
| 715 | |
| 716 if (request_.get()) { | |
| 717 request_->CancelWithError(error); | |
| 718 ReleaseRequest(); | |
| 719 } | |
| 720 | |
| 721 // Set the error manually. | |
| 722 // Normally, calling URLRequest::CancelWithError() results in calling | |
| 723 // OnReadCompleted() with bytes_read = -1 via an asynchronous task posted by | |
| 724 // URLRequestJob::NotifyDone(). But, because the request was released | |
| 725 // immediately after being canceled, the request could not call | |
| 726 // OnReadCompleted() which overwrites |status_| with the error status. | |
| 727 status_.set_status(URLRequestStatus::CANCELED); | |
| 728 status_.set_error(error); | |
| 729 | |
| 730 // Release the reference to the request context. There could be multiple | |
| 731 // references to URLFetcher::Core at this point so it may take a while to | |
| 732 // delete the object, but we cannot delay the destruction of the request | |
| 733 // context. | |
| 734 request_context_getter_ = NULL; | |
| 735 first_party_for_cookies_ = GURL(); | |
| 736 url_request_data_key_ = NULL; | |
| 737 url_request_create_data_callback_.Reset(); | |
| 738 was_cancelled_ = true; | |
| 739 } | |
| 740 | |
| 741 void URLFetcherCore::OnCompletedURLRequest( | |
| 742 base::TimeDelta backoff_delay) { | |
| 743 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); | |
| 744 | |
| 745 // Save the status and backoff_delay so that delegates can read it. | |
| 746 if (delegate_) { | |
| 747 backoff_delay_ = backoff_delay; | |
| 748 InformDelegateFetchIsComplete(); | |
| 749 } | |
| 750 } | |
| 751 | |
| 752 void URLFetcherCore::InformDelegateFetchIsComplete() { | |
| 753 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); | |
| 754 if (delegate_) | |
| 755 delegate_->OnURLFetchComplete(fetcher_); | |
| 756 } | |
| 757 | |
| 758 void URLFetcherCore::NotifyMalformedContent() { | |
| 759 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
| 760 if (url_throttler_entry_.get()) { | |
| 761 int status_code = response_code_; | |
| 762 if (status_code == URLFetcher::RESPONSE_CODE_INVALID) { | |
| 763 // The status code will generally be known by the time clients | |
| 764 // call the |ReceivedContentWasMalformed()| function (which ends up | |
| 765 // calling the current function) but if it's not, we need to assume | |
| 766 // the response was successful so that the total failure count | |
| 767 // used to calculate exponential back-off goes up. | |
| 768 status_code = 200; | |
| 769 } | |
| 770 url_throttler_entry_->ReceivedContentWasMalformed(status_code); | |
| 771 } | |
| 772 } | |
| 773 | |
| 774 void URLFetcherCore::DidFinishWriting(int result) { | |
| 775 if (result != OK) { | |
| 776 CancelURLRequest(result); | |
| 777 delegate_task_runner_->PostTask( | |
| 778 FROM_HERE, | |
| 779 base::Bind(&URLFetcherCore::InformDelegateFetchIsComplete, this)); | |
| 780 return; | |
| 781 } | |
| 782 // If the file was successfully closed, then the URL request is complete. | |
| 783 RetryOrCompleteUrlFetch(); | |
| 784 } | |
| 785 | |
| 786 void URLFetcherCore::RetryOrCompleteUrlFetch() { | |
| 787 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
| 788 base::TimeDelta backoff_delay; | |
| 789 | |
| 790 // Checks the response from server. | |
| 791 if (response_code_ >= 500 || | |
| 792 status_.error() == ERR_TEMPORARILY_THROTTLED) { | |
| 793 // When encountering a server error, we will send the request again | |
| 794 // after backoff time. | |
| 795 ++num_retries_on_5xx_; | |
| 796 | |
| 797 // Note that backoff_delay may be 0 because (a) the | |
| 798 // URLRequestThrottlerManager and related code does not | |
| 799 // necessarily back off on the first error, (b) it only backs off | |
| 800 // on some of the 5xx status codes, (c) not all URLRequestContexts | |
| 801 // have a throttler manager. | |
| 802 base::TimeTicks backoff_release_time = GetBackoffReleaseTime(); | |
| 803 backoff_delay = backoff_release_time - base::TimeTicks::Now(); | |
| 804 if (backoff_delay < base::TimeDelta()) | |
| 805 backoff_delay = base::TimeDelta(); | |
| 806 | |
| 807 if (automatically_retry_on_5xx_ && | |
| 808 num_retries_on_5xx_ <= max_retries_on_5xx_) { | |
| 809 StartOnIOThread(); | |
| 810 return; | |
| 811 } | |
| 812 } else { | |
| 813 backoff_delay = base::TimeDelta(); | |
| 814 } | |
| 815 | |
| 816 // Retry if the request failed due to network changes. | |
| 817 if (status_.error() == ERR_NETWORK_CHANGED && | |
| 818 num_retries_on_network_changes_ < max_retries_on_network_changes_) { | |
| 819 ++num_retries_on_network_changes_; | |
| 820 | |
| 821 // Retry soon, after flushing all the current tasks which may include | |
| 822 // further network change observers. | |
| 823 network_task_runner_->PostTask( | |
| 824 FROM_HERE, base::Bind(&URLFetcherCore::StartOnIOThread, this)); | |
| 825 return; | |
| 826 } | |
| 827 | |
| 828 request_context_getter_ = NULL; | |
| 829 first_party_for_cookies_ = GURL(); | |
| 830 url_request_data_key_ = NULL; | |
| 831 url_request_create_data_callback_.Reset(); | |
| 832 bool posted = delegate_task_runner_->PostTask( | |
| 833 FROM_HERE, | |
| 834 base::Bind(&URLFetcherCore::OnCompletedURLRequest, this, backoff_delay)); | |
| 835 | |
| 836 // If the delegate message loop does not exist any more, then the delegate | |
| 837 // should be gone too. | |
| 838 DCHECK(posted || !delegate_); | |
| 839 } | |
| 840 | |
| 841 void URLFetcherCore::ReleaseRequest() { | |
| 842 upload_progress_checker_timer_.reset(); | |
| 843 request_.reset(); | |
| 844 g_registry.Get().RemoveURLFetcherCore(this); | |
| 845 } | |
| 846 | |
| 847 base::TimeTicks URLFetcherCore::GetBackoffReleaseTime() { | |
| 848 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
| 849 | |
| 850 if (!original_url_throttler_entry_.get()) | |
| 851 return base::TimeTicks(); | |
| 852 | |
| 853 base::TimeTicks original_url_backoff = | |
| 854 original_url_throttler_entry_->GetExponentialBackoffReleaseTime(); | |
| 855 base::TimeTicks destination_url_backoff; | |
| 856 if (url_throttler_entry_.get() && | |
| 857 original_url_throttler_entry_.get() != url_throttler_entry_.get()) { | |
| 858 destination_url_backoff = | |
| 859 url_throttler_entry_->GetExponentialBackoffReleaseTime(); | |
| 860 } | |
| 861 | |
| 862 return original_url_backoff > destination_url_backoff ? | |
| 863 original_url_backoff : destination_url_backoff; | |
| 864 } | |
| 865 | |
| 866 void URLFetcherCore::CompleteAddingUploadDataChunk( | |
| 867 const std::string& content, bool is_last_chunk) { | |
| 868 if (was_cancelled_) { | |
| 869 // Since CompleteAddingUploadDataChunk() is posted as a *delayed* task, it | |
| 870 // may run after the URLFetcher was already stopped. | |
| 871 return; | |
| 872 } | |
| 873 DCHECK(is_chunked_upload_); | |
| 874 DCHECK(request_.get()); | |
| 875 DCHECK(!content.empty()); | |
| 876 request_->AppendChunkToUpload(content.data(), | |
| 877 static_cast<int>(content.length()), | |
| 878 is_last_chunk); | |
| 879 } | |
| 880 | |
| 881 int URLFetcherCore::WriteBuffer(scoped_refptr<DrainableIOBuffer> data) { | |
| 882 while (data->BytesRemaining() > 0) { | |
| 883 const int result = response_writer_->Write( | |
| 884 data.get(), | |
| 885 data->BytesRemaining(), | |
| 886 base::Bind(&URLFetcherCore::DidWriteBuffer, this, data)); | |
| 887 if (result < 0) { | |
| 888 if (result != ERR_IO_PENDING) | |
| 889 DidWriteBuffer(data, result); | |
| 890 return result; | |
| 891 } | |
| 892 data->DidConsume(result); | |
| 893 } | |
| 894 return OK; | |
| 895 } | |
| 896 | |
| 897 void URLFetcherCore::DidWriteBuffer(scoped_refptr<DrainableIOBuffer> data, | |
| 898 int result) { | |
| 899 if (result < 0) { // Handle errors. | |
| 900 CancelURLRequest(result); | |
| 901 response_writer_->Finish(base::Bind(&EmptyCompletionCallback)); | |
| 902 delegate_task_runner_->PostTask( | |
| 903 FROM_HERE, | |
| 904 base::Bind(&URLFetcherCore::InformDelegateFetchIsComplete, this)); | |
| 905 return; | |
| 906 } | |
| 907 | |
| 908 // Continue writing. | |
| 909 data->DidConsume(result); | |
| 910 if (WriteBuffer(data) < 0) | |
| 911 return; | |
| 912 | |
| 913 // Finished writing buffer_. Read some more, unless the request has been | |
| 914 // cancelled and deleted. | |
| 915 DCHECK_EQ(0, data->BytesRemaining()); | |
| 916 if (request_.get()) | |
| 917 ReadResponse(); | |
| 918 } | |
| 919 | |
| 920 void URLFetcherCore::ReadResponse() { | |
| 921 // Some servers may treat HEAD requests as GET requests. To free up the | |
| 922 // network connection as soon as possible, signal that the request has | |
| 923 // completed immediately, without trying to read any data back (all we care | |
| 924 // about is the response code and headers, which we already have). | |
| 925 int bytes_read = 0; | |
| 926 if (request_->status().is_success() && | |
| 927 (request_type_ != URLFetcher::HEAD)) { | |
| 928 if (!request_->Read(buffer_.get(), kBufferSize, &bytes_read)) | |
| 929 bytes_read = -1; // Match OnReadCompleted() interface contract. | |
| 930 } | |
| 931 OnReadCompleted(request_.get(), bytes_read); | |
| 932 } | |
| 933 | |
| 934 void URLFetcherCore::InformDelegateUploadProgress() { | |
| 935 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
| 936 if (request_.get()) { | |
| 937 int64 current = request_->GetUploadProgress().position(); | |
| 938 if (current_upload_bytes_ != current) { | |
| 939 current_upload_bytes_ = current; | |
| 940 int64 total = -1; | |
| 941 if (!is_chunked_upload_) { | |
| 942 total = static_cast<int64>(request_->GetUploadProgress().size()); | |
| 943 // Total may be zero if the UploadDataStream::Init has not been called | |
| 944 // yet. Don't send the upload progress until the size is initialized. | |
| 945 if (!total) | |
| 946 return; | |
| 947 } | |
| 948 delegate_task_runner_->PostTask( | |
| 949 FROM_HERE, | |
| 950 base::Bind( | |
| 951 &URLFetcherCore::InformDelegateUploadProgressInDelegateThread, | |
| 952 this, current, total)); | |
| 953 } | |
| 954 } | |
| 955 } | |
| 956 | |
| 957 void URLFetcherCore::InformDelegateUploadProgressInDelegateThread( | |
| 958 int64 current, int64 total) { | |
| 959 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); | |
| 960 if (delegate_) | |
| 961 delegate_->OnURLFetchUploadProgress(fetcher_, current, total); | |
| 962 } | |
| 963 | |
| 964 void URLFetcherCore::InformDelegateDownloadProgress() { | |
| 965 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
| 966 tracked_objects::ScopedTracker tracking_profile1( | |
| 967 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 968 "423948 URLFetcherCore::InformDelegateDownloadProgress1")); | |
| 969 | |
| 970 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
| 971 | |
| 972 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
| 973 tracked_objects::ScopedTracker tracking_profile2( | |
| 974 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 975 "423948 URLFetcherCore::InformDelegateDownloadProgress2")); | |
| 976 | |
| 977 delegate_task_runner_->PostTask( | |
| 978 FROM_HERE, | |
| 979 base::Bind( | |
| 980 &URLFetcherCore::InformDelegateDownloadProgressInDelegateThread, | |
| 981 this, current_response_bytes_, total_response_bytes_)); | |
| 982 } | |
| 983 | |
| 984 void URLFetcherCore::InformDelegateDownloadProgressInDelegateThread( | |
| 985 int64 current, int64 total) { | |
| 986 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); | |
| 987 if (delegate_) | |
| 988 delegate_->OnURLFetchDownloadProgress(fetcher_, current, total); | |
| 989 } | |
| 990 | |
| 991 void URLFetcherCore::AssertHasNoUploadData() const { | |
| 992 DCHECK(!upload_content_set_); | |
| 993 DCHECK(upload_content_.empty()); | |
| 994 DCHECK(upload_file_path_.empty()); | |
| 995 DCHECK(upload_stream_factory_.is_null()); | |
| 996 } | |
| 997 | |
| 998 } // namespace net | |
| OLD | NEW |