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     ResourceMessageFilter* filter,
     81     int routing_id,
     82     net::URLRequest* request,
     83     ResourceDispatcherHostImpl* rdh)
     84     : ResourceMessageDelegate(request),
     85       filter_(filter),
     86       routing_id_(routing_id),
     87       request_(request),
     88       rdh_(rdh),
     89       pending_data_count_(0),
     90       allocation_size_(0),
     91       did_defer_(false),
     92       has_checked_for_sufficient_resources_(false),
     93       sent_received_response_msg_(false),
     94       sent_first_data_msg_(false) {
     95   InitializeResourceBufferConstants();
     96 }
     97 
     98 AsyncResourceHandler::~AsyncResourceHandler() {
     99   if (has_checked_for_sufficient_resources_)
    100     rdh_->FinishedWithResourcesForRequest(request_);
    101 }
    102 
    103 bool AsyncResourceHandler::OnMessageReceived(const IPC::Message& message,
    104                                              bool* message_was_ok) {
    105   bool handled = true;
    106   IPC_BEGIN_MESSAGE_MAP_EX(AsyncResourceHandler, message, *message_was_ok)
    107     IPC_MESSAGE_HANDLER(ResourceHostMsg_FollowRedirect, OnFollowRedirect)
    108     IPC_MESSAGE_HANDLER(ResourceHostMsg_DataReceived_ACK, OnDataReceivedACK)
    109     IPC_MESSAGE_UNHANDLED(handled = false)
    110   IPC_END_MESSAGE_MAP_EX()
    111   return handled;
    112 }
    113 
    114 void AsyncResourceHandler::OnFollowRedirect(
    115     int request_id,
    116     bool has_new_first_party_for_cookies,
    117     const GURL& new_first_party_for_cookies) {
    118   if (!request_->status().is_success()) {
    119     DVLOG(1) << "OnFollowRedirect for invalid request";
    120     return;
    121   }
    122 
    123   if (has_new_first_party_for_cookies)
    124     request_->set_first_party_for_cookies(new_first_party_for_cookies);
    125 
    126   ResumeIfDeferred();
    127 }
    128 
    129 void AsyncResourceHandler::OnDataReceivedACK(int request_id) {
    130   if (pending_data_count_) {
    131     --pending_data_count_;
    132 
    133     buffer_->RecycleLeastRecentlyAllocated();
    134     if (buffer_->CanAllocate())
    135       ResumeIfDeferred();
    136   }
    137 }
    138 
    139 bool AsyncResourceHandler::OnUploadProgress(int request_id,
    140                                             uint64 position,
    141                                             uint64 size) {
    142   return filter_->Send(new ResourceMsg_UploadProgress(routing_id_, request_id,
    143                                                       position, size));
    144 }
    145 
    146 bool AsyncResourceHandler::OnRequestRedirected(int request_id,
    147                                                const GURL& new_url,
    148                                                ResourceResponse* response,
    149                                                bool* defer) {
    150   *defer = did_defer_ = true;
    151 
    152   if (rdh_->delegate()) {
    153     rdh_->delegate()->OnRequestRedirected(new_url, request_,
    154                                           filter_->resource_context(),
    155                                           response);
    156   }
    157 
    158   DevToolsNetLogObserver::PopulateResponseInfo(request_, response);
    159   response->head.request_start = request_->creation_time();
    160   response->head.response_start = TimeTicks::Now();
    161   return filter_->Send(new ResourceMsg_ReceivedRedirect(
    162       routing_id_, request_id, new_url, response->head));
    163 }
    164 
    165 bool AsyncResourceHandler::OnResponseStarted(int request_id,
    166                                              ResourceResponse* response,
    167                                              bool* defer) {
    168   // For changes to the main frame, inform the renderer of the new URL's
    169   // per-host settings before the request actually commits.  This way the
    170   // renderer will be able to set these precisely at the time the
    171   // request commits, avoiding the possibility of e.g. zooming the old content
    172   // or of having to layout the new content twice.
    173 
    174   ResourceContext* resource_context = filter_->resource_context();
    175   if (rdh_->delegate()) {
    176     rdh_->delegate()->OnResponseStarted(
    177         request_, resource_context, response, filter_.get());
    178   }
    179 
    180   DevToolsNetLogObserver::PopulateResponseInfo(request_, response);
    181 
    182   HostZoomMap* host_zoom_map =
    183       GetHostZoomMapForResourceContext(resource_context);
    184 
    185   const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request_);
    186   if (info->GetResourceType() == ResourceType::MAIN_FRAME && host_zoom_map) {
    187     const GURL& request_url = request_->url();
    188     filter_->Send(new ViewMsg_SetZoomLevelForLoadingURL(
    189         info->GetRouteID(),
    190         request_url, host_zoom_map->GetZoomLevelForHostAndScheme(
    191             request_url.scheme(),
    192             net::GetHostOrSpecFromURL(request_url))));
    193   }
    194 
    195   response->head.request_start = request_->creation_time();
    196   response->head.response_start = TimeTicks::Now();
    197   filter_->Send(new ResourceMsg_ReceivedResponse(
    198       routing_id_, request_id, response->head));
    199   sent_received_response_msg_ = true;
    200 
    201   if (request_->response_info().metadata.get()) {
    202     std::vector<char> copy(request_->response_info().metadata->data(),
    203                            request_->response_info().metadata->data() +
    204                                request_->response_info().metadata->size());
    205     filter_->Send(new ResourceMsg_ReceivedCachedMetadata(
    206         routing_id_, request_id, copy));
    207   }
    208 
    209   return true;
    210 }
    211 
    212 bool AsyncResourceHandler::OnWillStart(int request_id,
    213                                        const GURL& url,
    214                                        bool* defer) {
    215   return true;
    216 }
    217 
    218 bool AsyncResourceHandler::OnWillRead(int request_id, net::IOBuffer** buf,
    219                                       int* buf_size, int min_size) {
    220   DCHECK_EQ(-1, min_size);
    221 
    222   if (!EnsureResourceBufferIsInitialized())
    223     return false;
    224 
    225   DCHECK(buffer_->CanAllocate());
    226   char* memory = buffer_->Allocate(&allocation_size_);
    227   CHECK(memory);
    228 
    229   *buf = new DependentIOBuffer(buffer_.get(), memory);
    230   *buf_size = allocation_size_;
    231 
    232   UMA_HISTOGRAM_CUSTOM_COUNTS(
    233       "Net.AsyncResourceHandler_SharedIOBuffer_Alloc",
    234       *buf_size, 0, kMaxAllocationSize, 100);
    235   return true;
    236 }
    237 
    238 bool AsyncResourceHandler::OnReadCompleted(int request_id, int bytes_read,
    239                                            bool* defer) {
    240   if (!bytes_read)
    241     return true;
    242 
    243   buffer_->ShrinkLastAllocation(bytes_read);
    244 
    245   UMA_HISTOGRAM_CUSTOM_COUNTS(
    246       "Net.AsyncResourceHandler_SharedIOBuffer_Used",
    247       bytes_read, 0, kMaxAllocationSize, 100);
    248   UMA_HISTOGRAM_PERCENTAGE(
    249       "Net.AsyncResourceHandler_SharedIOBuffer_UsedPercentage",
    250       CalcUsedPercentage(bytes_read, allocation_size_));
    251 
    252   if (!sent_first_data_msg_) {
    253     base::SharedMemoryHandle handle;
    254     int size;
    255     if (!buffer_->ShareToProcess(filter_->PeerHandle(), &handle, &size))
    256       return false;
    257     filter_->Send(
    258         new ResourceMsg_SetDataBuffer(routing_id_, request_id, handle, size,
    259                                       filter_->peer_pid()));
    260     sent_first_data_msg_ = true;
    261   }
    262 
    263   int data_offset = buffer_->GetLastAllocationOffset();
    264   int encoded_data_length =
    265       DevToolsNetLogObserver::GetAndResetEncodedDataLength(request_);
    266 
    267   filter_->Send(
    268       new ResourceMsg_DataReceived(routing_id_, request_id, data_offset,
    269                                    bytes_read, encoded_data_length));
    270   ++pending_data_count_;
    271   UMA_HISTOGRAM_CUSTOM_COUNTS(
    272       "Net.AsyncResourceHandler_PendingDataCount",
    273       pending_data_count_, 0, 100, 100);
    274 
    275   if (!buffer_->CanAllocate()) {
    276     UMA_HISTOGRAM_CUSTOM_COUNTS(
    277         "Net.AsyncResourceHandler_PendingDataCount_WhenFull",
    278         pending_data_count_, 0, 100, 100);
    279     *defer = did_defer_ = true;
    280   }
    281 
    282   return true;
    283 }
    284 
    285 void AsyncResourceHandler::OnDataDownloaded(
    286     int request_id, int bytes_downloaded) {
    287   filter_->Send(new ResourceMsg_DataDownloaded(
    288       routing_id_, request_id, bytes_downloaded));
    289 }
    290 
    291 bool AsyncResourceHandler::OnResponseCompleted(
    292     int request_id,
    293     const net::URLRequestStatus& status,
    294     const std::string& security_info) {
    295   // If we crash here, figure out what URL the renderer was requesting.
    296   // http://crbug.com/107692
    297   char url_buf[128];
    298   base::strlcpy(url_buf, request_->url().spec().c_str(), arraysize(url_buf));
    299   base::debug::Alias(url_buf);
    300 
    301   // TODO(gavinp): Remove this CHECK when we figure out the cause of
    302   // http://crbug.com/124680 . This check mirrors closely check in
    303   // WebURLLoaderImpl::OnCompletedRequest that routes this message to a WebCore
    304   // ResourceHandleInternal which asserts on its state and crashes. By crashing
    305   // when the message is sent, we should get better crash reports.
    306   CHECK(status.status() != net::URLRequestStatus::SUCCESS ||
    307         sent_received_response_msg_);
    308 
    309   TimeTicks completion_time = TimeTicks::Now();
    310 
    311   int error_code = status.error();
    312   bool was_ignored_by_handler =
    313       ResourceRequestInfoImpl::ForRequest(request_)->WasIgnoredByHandler();
    314 
    315   DCHECK(status.status() != net::URLRequestStatus::IO_PENDING);
    316   // If this check fails, then we're in an inconsistent state because all
    317   // requests ignored by the handler should be canceled (which should result in
    318   // the ERR_ABORTED error code).
    319   DCHECK(!was_ignored_by_handler || error_code == net::ERR_ABORTED);
    320 
    321   // TODO(mkosiba): Fix up cases where we create a URLRequestStatus
    322   // with a status() != SUCCESS and an error_code() == net::OK.
    323   if (status.status() == net::URLRequestStatus::CANCELED &&
    324       error_code == net::OK) {
    325     error_code = net::ERR_ABORTED;
    326   } else if (status.status() == net::URLRequestStatus::FAILED &&
    327              error_code == net::OK) {
    328     error_code = net::ERR_FAILED;
    329   }
    330 
    331   filter_->Send(new ResourceMsg_RequestComplete(routing_id_,
    332                                                 request_id,
    333                                                 error_code,
    334                                                 was_ignored_by_handler,
    335                                                 security_info,
    336                                                 completion_time));
    337   return true;
    338 }
    339 
    340 bool AsyncResourceHandler::EnsureResourceBufferIsInitialized() {
    341   if (buffer_.get() && buffer_->IsInitialized())
    342     return true;
    343 
    344   if (!has_checked_for_sufficient_resources_) {
    345     has_checked_for_sufficient_resources_ = true;
    346     if (!rdh_->HasSufficientResourcesForRequest(request_)) {
    347       controller()->CancelWithError(net::ERR_INSUFFICIENT_RESOURCES);
    348       return false;
    349     }
    350   }
    351 
    352   buffer_ = new ResourceBuffer();
    353   return buffer_->Initialize(kBufferSize,
    354                              kMinAllocationSize,
    355                              kMaxAllocationSize);
    356 }
    357 
    358 void AsyncResourceHandler::ResumeIfDeferred() {
    359   if (did_defer_) {
    360     did_defer_ = false;
    361     controller()->Resume();
    362   }
    363 }
    364 
    365 }  // namespace content
    366