Home | History | Annotate | Download | only in loader
      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 "content/browser/loader/async_resource_handler.h"
      6 
      7 #include <algorithm>
      8 #include <vector>
      9 
     10 #include "base/command_line.h"
     11 #include "base/containers/hash_tables.h"
     12 #include "base/debug/alias.h"
     13 #include "base/logging.h"
     14 #include "base/memory/shared_memory.h"
     15 #include "base/metrics/histogram.h"
     16 #include "base/strings/string_number_conversions.h"
     17 #include "content/browser/devtools/devtools_netlog_observer.h"
     18 #include "content/browser/host_zoom_map_impl.h"
     19 #include "content/browser/loader/resource_buffer.h"
     20 #include "content/browser/loader/resource_dispatcher_host_impl.h"
     21 #include "content/browser/loader/resource_message_filter.h"
     22 #include "content/browser/loader/resource_request_info_impl.h"
     23 #include "content/browser/resource_context_impl.h"
     24 #include "content/common/resource_messages.h"
     25 #include "content/common/view_messages.h"
     26 #include "content/public/browser/global_request_id.h"
     27 #include "content/public/browser/resource_dispatcher_host_delegate.h"
     28 #include "content/public/common/resource_response.h"
     29 #include "net/base/io_buffer.h"
     30 #include "net/base/load_flags.h"
     31 #include "net/base/net_log.h"
     32 #include "net/base/net_util.h"
     33 
     34 using base::TimeTicks;
     35 
     36 namespace content {
     37 namespace {
     38 
     39 static int kBufferSize = 1024 * 512;
     40 static int kMinAllocationSize = 1024 * 4;
     41 static int kMaxAllocationSize = 1024 * 32;
     42 
     43 void GetNumericArg(const std::string& name, int* result) {
     44   const std::string& value =
     45       CommandLine::ForCurrentProcess()->GetSwitchValueASCII(name);
     46   if (!value.empty())
     47     base::StringToInt(value, result);
     48 }
     49 
     50 void InitializeResourceBufferConstants() {
     51   static bool did_init = false;
     52   if (did_init)
     53     return;
     54   did_init = true;
     55 
     56   GetNumericArg("resource-buffer-size", &kBufferSize);
     57   GetNumericArg("resource-buffer-min-allocation-size", &kMinAllocationSize);
     58   GetNumericArg("resource-buffer-max-allocation-size", &kMaxAllocationSize);
     59 }
     60 
     61 int CalcUsedPercentage(int bytes_read, int buffer_size) {
     62   double ratio = static_cast<double>(bytes_read) / buffer_size;
     63   return static_cast<int>(ratio * 100.0 + 0.5);  // Round to nearest integer.
     64 }
     65 
     66 }  // namespace
     67 
     68 class DependentIOBuffer : public net::WrappedIOBuffer {
     69  public:
     70   DependentIOBuffer(ResourceBuffer* backing, char* memory)
     71       : net::WrappedIOBuffer(memory),
     72         backing_(backing) {
     73   }
     74  private:
     75   virtual ~DependentIOBuffer() {}
     76   scoped_refptr<ResourceBuffer> backing_;
     77 };
     78 
     79 AsyncResourceHandler::AsyncResourceHandler(
     80     net::URLRequest* request,
     81     ResourceDispatcherHostImpl* rdh)
     82     : ResourceHandler(request),
     83       ResourceMessageDelegate(request),
     84       rdh_(rdh),
     85       pending_data_count_(0),
     86       allocation_size_(0),
     87       did_defer_(false),
     88       has_checked_for_sufficient_resources_(false),
     89       sent_received_response_msg_(false),
     90       sent_first_data_msg_(false) {
     91   InitializeResourceBufferConstants();
     92 }
     93 
     94 AsyncResourceHandler::~AsyncResourceHandler() {
     95   if (has_checked_for_sufficient_resources_)
     96     rdh_->FinishedWithResourcesForRequest(request());
     97 }
     98 
     99 bool AsyncResourceHandler::OnMessageReceived(const IPC::Message& message,
    100                                              bool* message_was_ok) {
    101   bool handled = true;
    102   IPC_BEGIN_MESSAGE_MAP_EX(AsyncResourceHandler, message, *message_was_ok)
    103     IPC_MESSAGE_HANDLER(ResourceHostMsg_FollowRedirect, OnFollowRedirect)
    104     IPC_MESSAGE_HANDLER(ResourceHostMsg_DataReceived_ACK, OnDataReceivedACK)
    105     IPC_MESSAGE_UNHANDLED(handled = false)
    106   IPC_END_MESSAGE_MAP_EX()
    107   return handled;
    108 }
    109 
    110 void AsyncResourceHandler::OnFollowRedirect(
    111     int request_id,
    112     bool has_new_first_party_for_cookies,
    113     const GURL& new_first_party_for_cookies) {
    114   if (!request()->status().is_success()) {
    115     DVLOG(1) << "OnFollowRedirect for invalid request";
    116     return;
    117   }
    118 
    119   if (has_new_first_party_for_cookies)
    120     request()->set_first_party_for_cookies(new_first_party_for_cookies);
    121 
    122   ResumeIfDeferred();
    123 }
    124 
    125 void AsyncResourceHandler::OnDataReceivedACK(int request_id) {
    126   if (pending_data_count_) {
    127     --pending_data_count_;
    128 
    129     buffer_->RecycleLeastRecentlyAllocated();
    130     if (buffer_->CanAllocate())
    131       ResumeIfDeferred();
    132   }
    133 }
    134 
    135 bool AsyncResourceHandler::OnUploadProgress(int request_id,
    136                                             uint64 position,
    137                                             uint64 size) {
    138   ResourceMessageFilter* filter = GetFilter();
    139   if (!filter)
    140     return false;
    141   return filter->Send(
    142       new ResourceMsg_UploadProgress(request_id, position, size));
    143 }
    144 
    145 bool AsyncResourceHandler::OnRequestRedirected(int request_id,
    146                                                const GURL& new_url,
    147                                                ResourceResponse* response,
    148                                                bool* defer) {
    149   const ResourceRequestInfoImpl* info = GetRequestInfo();
    150   if (!info->filter())
    151     return false;
    152 
    153   *defer = did_defer_ = true;
    154   OnDefer();
    155 
    156   if (rdh_->delegate()) {
    157     rdh_->delegate()->OnRequestRedirected(
    158         new_url, request(), info->GetContext(), response);
    159   }
    160 
    161   DevToolsNetLogObserver::PopulateResponseInfo(request(), response);
    162   response->head.request_start = request()->creation_time();
    163   response->head.response_start = TimeTicks::Now();
    164   return info->filter()->Send(new ResourceMsg_ReceivedRedirect(
    165       request_id, new_url, response->head));
    166 }
    167 
    168 bool AsyncResourceHandler::OnResponseStarted(int request_id,
    169                                              ResourceResponse* response,
    170                                              bool* defer) {
    171   // For changes to the main frame, inform the renderer of the new URL's
    172   // per-host settings before the request actually commits.  This way the
    173   // renderer will be able to set these precisely at the time the
    174   // request commits, avoiding the possibility of e.g. zooming the old content
    175   // or of having to layout the new content twice.
    176 
    177   const ResourceRequestInfoImpl* info = GetRequestInfo();
    178   if (!info->filter())
    179     return false;
    180 
    181   if (rdh_->delegate()) {
    182     rdh_->delegate()->OnResponseStarted(
    183         request(), info->GetContext(), response, info->filter());
    184   }
    185 
    186   DevToolsNetLogObserver::PopulateResponseInfo(request(), response);
    187 
    188   HostZoomMap* host_zoom_map =
    189       GetHostZoomMapForResourceContext(info->GetContext());
    190 
    191   if (info->GetResourceType() == ResourceType::MAIN_FRAME && host_zoom_map) {
    192     const GURL& request_url = request()->url();
    193     info->filter()->Send(new ViewMsg_SetZoomLevelForLoadingURL(
    194         info->GetRouteID(),
    195         request_url, host_zoom_map->GetZoomLevelForHostAndScheme(
    196             request_url.scheme(),
    197             net::GetHostOrSpecFromURL(request_url))));
    198   }
    199 
    200   response->head.request_start = request()->creation_time();
    201   response->head.response_start = TimeTicks::Now();
    202   info->filter()->Send(new ResourceMsg_ReceivedResponse(request_id,
    203                                                         response->head));
    204   sent_received_response_msg_ = true;
    205 
    206   if (request()->response_info().metadata.get()) {
    207     std::vector<char> copy(request()->response_info().metadata->data(),
    208                            request()->response_info().metadata->data() +
    209                                request()->response_info().metadata->size());
    210     info->filter()->Send(new ResourceMsg_ReceivedCachedMetadata(request_id,
    211                                                                 copy));
    212   }
    213 
    214   return true;
    215 }
    216 
    217 bool AsyncResourceHandler::OnWillStart(int request_id,
    218                                        const GURL& url,
    219                                        bool* defer) {
    220   return true;
    221 }
    222 
    223 bool AsyncResourceHandler::OnWillRead(int request_id,
    224                                       scoped_refptr<net::IOBuffer>* buf,
    225                                       int* buf_size,
    226                                       int min_size) {
    227   DCHECK_EQ(-1, min_size);
    228 
    229   if (!EnsureResourceBufferIsInitialized())
    230     return false;
    231 
    232   DCHECK(buffer_->CanAllocate());
    233   char* memory = buffer_->Allocate(&allocation_size_);
    234   CHECK(memory);
    235 
    236   *buf = new DependentIOBuffer(buffer_.get(), memory);
    237   *buf_size = allocation_size_;
    238 
    239   UMA_HISTOGRAM_CUSTOM_COUNTS(
    240       "Net.AsyncResourceHandler_SharedIOBuffer_Alloc",
    241       *buf_size, 0, kMaxAllocationSize, 100);
    242   return true;
    243 }
    244 
    245 bool AsyncResourceHandler::OnReadCompleted(int request_id, int bytes_read,
    246                                            bool* defer) {
    247   DCHECK_GE(bytes_read, 0);
    248 
    249   if (!bytes_read)
    250     return true;
    251 
    252   ResourceMessageFilter* filter = GetFilter();
    253   if (!filter)
    254     return false;
    255 
    256   buffer_->ShrinkLastAllocation(bytes_read);
    257 
    258   UMA_HISTOGRAM_CUSTOM_COUNTS(
    259       "Net.AsyncResourceHandler_SharedIOBuffer_Used",
    260       bytes_read, 0, kMaxAllocationSize, 100);
    261   UMA_HISTOGRAM_PERCENTAGE(
    262       "Net.AsyncResourceHandler_SharedIOBuffer_UsedPercentage",
    263       CalcUsedPercentage(bytes_read, allocation_size_));
    264 
    265   if (!sent_first_data_msg_) {
    266     base::SharedMemoryHandle handle;
    267     int size;
    268     if (!buffer_->ShareToProcess(filter->PeerHandle(), &handle, &size))
    269       return false;
    270     filter->Send(new ResourceMsg_SetDataBuffer(
    271         request_id, handle, size, filter->peer_pid()));
    272     sent_first_data_msg_ = true;
    273   }
    274 
    275   int data_offset = buffer_->GetLastAllocationOffset();
    276   int encoded_data_length =
    277       DevToolsNetLogObserver::GetAndResetEncodedDataLength(request());
    278 
    279   filter->Send(new ResourceMsg_DataReceived(
    280       request_id, data_offset, bytes_read, encoded_data_length));
    281   ++pending_data_count_;
    282   UMA_HISTOGRAM_CUSTOM_COUNTS(
    283       "Net.AsyncResourceHandler_PendingDataCount",
    284       pending_data_count_, 0, 100, 100);
    285 
    286   if (!buffer_->CanAllocate()) {
    287     UMA_HISTOGRAM_CUSTOM_COUNTS(
    288         "Net.AsyncResourceHandler_PendingDataCount_WhenFull",
    289         pending_data_count_, 0, 100, 100);
    290     *defer = did_defer_ = true;
    291     OnDefer();
    292   }
    293 
    294   return true;
    295 }
    296 
    297 void AsyncResourceHandler::OnDataDownloaded(
    298     int request_id, int bytes_downloaded) {
    299   int encoded_data_length =
    300       DevToolsNetLogObserver::GetAndResetEncodedDataLength(request());
    301 
    302   ResourceMessageFilter* filter = GetFilter();
    303   if (filter) {
    304     filter->Send(new ResourceMsg_DataDownloaded(
    305         request_id, bytes_downloaded, encoded_data_length));
    306   }
    307 }
    308 
    309 void AsyncResourceHandler::OnResponseCompleted(
    310     int request_id,
    311     const net::URLRequestStatus& status,
    312     const std::string& security_info,
    313     bool* defer) {
    314   const ResourceRequestInfoImpl* info = GetRequestInfo();
    315   if (!info->filter())
    316     return;
    317 
    318   // If we crash here, figure out what URL the renderer was requesting.
    319   // http://crbug.com/107692
    320   char url_buf[128];
    321   base::strlcpy(url_buf, request()->url().spec().c_str(), arraysize(url_buf));
    322   base::debug::Alias(url_buf);
    323 
    324   // TODO(gavinp): Remove this CHECK when we figure out the cause of
    325   // http://crbug.com/124680 . This check mirrors closely check in
    326   // WebURLLoaderImpl::OnCompletedRequest that routes this message to a WebCore
    327   // ResourceHandleInternal which asserts on its state and crashes. By crashing
    328   // when the message is sent, we should get better crash reports.
    329   CHECK(status.status() != net::URLRequestStatus::SUCCESS ||
    330         sent_received_response_msg_);
    331 
    332   TimeTicks completion_time = TimeTicks::Now();
    333 
    334   int error_code = status.error();
    335   bool was_ignored_by_handler = info->WasIgnoredByHandler();
    336 
    337   DCHECK(status.status() != net::URLRequestStatus::IO_PENDING);
    338   // If this check fails, then we're in an inconsistent state because all
    339   // requests ignored by the handler should be canceled (which should result in
    340   // the ERR_ABORTED error code).
    341   DCHECK(!was_ignored_by_handler || error_code == net::ERR_ABORTED);
    342 
    343   // TODO(mkosiba): Fix up cases where we create a URLRequestStatus
    344   // with a status() != SUCCESS and an error_code() == net::OK.
    345   if (status.status() == net::URLRequestStatus::CANCELED &&
    346       error_code == net::OK) {
    347     error_code = net::ERR_ABORTED;
    348   } else if (status.status() == net::URLRequestStatus::FAILED &&
    349              error_code == net::OK) {
    350     error_code = net::ERR_FAILED;
    351   }
    352 
    353   info->filter()->Send(
    354       new ResourceMsg_RequestComplete(request_id,
    355                                       error_code,
    356                                       was_ignored_by_handler,
    357                                       security_info,
    358                                       completion_time));
    359 }
    360 
    361 bool AsyncResourceHandler::EnsureResourceBufferIsInitialized() {
    362   if (buffer_.get() && buffer_->IsInitialized())
    363     return true;
    364 
    365   if (!has_checked_for_sufficient_resources_) {
    366     has_checked_for_sufficient_resources_ = true;
    367     if (!rdh_->HasSufficientResourcesForRequest(request())) {
    368       controller()->CancelWithError(net::ERR_INSUFFICIENT_RESOURCES);
    369       return false;
    370     }
    371   }
    372 
    373   buffer_ = new ResourceBuffer();
    374   return buffer_->Initialize(kBufferSize,
    375                              kMinAllocationSize,
    376                              kMaxAllocationSize);
    377 }
    378 
    379 void AsyncResourceHandler::ResumeIfDeferred() {
    380   if (did_defer_) {
    381     did_defer_ = false;
    382     request()->LogUnblocked();
    383     controller()->Resume();
    384   }
    385 }
    386 
    387 void AsyncResourceHandler::OnDefer() {
    388   request()->LogBlockedBy("AsyncResourceHandler");
    389 }
    390 
    391 }  // namespace content
    392