Home | History | Annotate | Download | only in devtools
      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/devtools/devtools_netlog_observer.h"
      6 
      7 #include "base/strings/string_util.h"
      8 #include "base/values.h"
      9 #include "content/public/browser/browser_thread.h"
     10 #include "content/public/browser/content_browser_client.h"
     11 #include "content/public/common/resource_response.h"
     12 #include "net/base/load_flags.h"
     13 #include "net/http/http_response_headers.h"
     14 #include "net/http/http_util.h"
     15 #include "net/spdy/spdy_header_block.h"
     16 #include "net/url_request/url_request.h"
     17 #include "net/url_request/url_request_netlog_params.h"
     18 
     19 namespace content {
     20 const size_t kMaxNumEntries = 1000;
     21 
     22 DevToolsNetLogObserver* DevToolsNetLogObserver::instance_ = NULL;
     23 
     24 DevToolsNetLogObserver::DevToolsNetLogObserver() {
     25 }
     26 
     27 DevToolsNetLogObserver::~DevToolsNetLogObserver() {
     28 }
     29 
     30 DevToolsNetLogObserver::ResourceInfo*
     31 DevToolsNetLogObserver::GetResourceInfo(uint32 id) {
     32   RequestToInfoMap::iterator it = request_to_info_.find(id);
     33   if (it != request_to_info_.end())
     34     return it->second.get();
     35   return NULL;
     36 }
     37 
     38 void DevToolsNetLogObserver::OnAddEntry(const net::NetLog::Entry& entry) {
     39   // The events that the Observer is interested in only occur on the IO thread.
     40   if (!BrowserThread::CurrentlyOn(BrowserThread::IO))
     41     return;
     42 
     43   if (entry.source().type == net::NetLog::SOURCE_URL_REQUEST)
     44     OnAddURLRequestEntry(entry);
     45   else if (entry.source().type == net::NetLog::SOURCE_HTTP_STREAM_JOB)
     46     OnAddHTTPStreamJobEntry(entry);
     47   else if (entry.source().type == net::NetLog::SOURCE_SOCKET)
     48     OnAddSocketEntry(entry);
     49 }
     50 
     51 void DevToolsNetLogObserver::OnAddURLRequestEntry(
     52     const net::NetLog::Entry& entry) {
     53   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     54 
     55   bool is_begin = entry.phase() == net::NetLog::PHASE_BEGIN;
     56   bool is_end = entry.phase() == net::NetLog::PHASE_END;
     57 
     58   if (entry.type() == net::NetLog::TYPE_URL_REQUEST_START_JOB) {
     59     if (is_begin) {
     60       int load_flags;
     61       scoped_ptr<base::Value> event_param(entry.ParametersToValue());
     62       if (!net::StartEventLoadFlagsFromEventParams(event_param.get(),
     63                                                    &load_flags)) {
     64         return;
     65       }
     66 
     67       if (!(load_flags & net::LOAD_REPORT_RAW_HEADERS))
     68         return;
     69 
     70       if (request_to_info_.size() > kMaxNumEntries) {
     71         LOG(WARNING) << "The raw headers observer url request count has grown "
     72                         "larger than expected, resetting";
     73         request_to_info_.clear();
     74       }
     75 
     76       request_to_info_[entry.source().id] = new ResourceInfo();
     77 
     78       if (request_to_encoded_data_length_.size() > kMaxNumEntries) {
     79         LOG(WARNING) << "The encoded data length observer url request count "
     80                         "has grown larger than expected, resetting";
     81         request_to_encoded_data_length_.clear();
     82       }
     83 
     84       request_to_encoded_data_length_[entry.source().id] = 0;
     85     }
     86     return;
     87   } else if (entry.type() == net::NetLog::TYPE_REQUEST_ALIVE) {
     88     // Cleanup records based on the TYPE_REQUEST_ALIVE entry.
     89     if (is_end) {
     90       request_to_info_.erase(entry.source().id);
     91       request_to_encoded_data_length_.erase(entry.source().id);
     92     }
     93     return;
     94   }
     95 
     96   ResourceInfo* info = GetResourceInfo(entry.source().id);
     97   if (!info)
     98     return;
     99 
    100   switch (entry.type()) {
    101     case net::NetLog::TYPE_HTTP_TRANSACTION_SEND_REQUEST_HEADERS: {
    102       scoped_ptr<base::Value> event_params(entry.ParametersToValue());
    103       std::string request_line;
    104       net::HttpRequestHeaders request_headers;
    105 
    106       if (!net::HttpRequestHeaders::FromNetLogParam(event_params.get(),
    107                                                     &request_headers,
    108                                                     &request_line)) {
    109         NOTREACHED();
    110       }
    111 
    112       // We need to clear headers in case the same url_request is reused for
    113       // several http requests (e.g. see http://crbug.com/80157).
    114       info->request_headers.clear();
    115 
    116       for (net::HttpRequestHeaders::Iterator it(request_headers);
    117            it.GetNext();) {
    118         info->request_headers.push_back(std::make_pair(it.name(), it.value()));
    119       }
    120       info->request_headers_text = request_line + request_headers.ToString();
    121       break;
    122     }
    123     case net::NetLog::TYPE_HTTP_TRANSACTION_SPDY_SEND_REQUEST_HEADERS: {
    124       scoped_ptr<base::Value> event_params(entry.ParametersToValue());
    125       net::SpdyHeaderBlock request_headers;
    126 
    127       if (!net::SpdyHeaderBlockFromNetLogParam(event_params.get(),
    128                                                &request_headers)) {
    129         NOTREACHED();
    130       }
    131 
    132       // We need to clear headers in case the same url_request is reused for
    133       // several http requests (e.g. see http://crbug.com/80157).
    134       info->request_headers.clear();
    135 
    136       for (net::SpdyHeaderBlock::const_iterator it = request_headers.begin();
    137            it != request_headers.end(); ++it) {
    138         info->request_headers.push_back(std::make_pair(it->first, it->second));
    139       }
    140       info->request_headers_text = "";
    141       break;
    142     }
    143     case net::NetLog::TYPE_HTTP_TRANSACTION_READ_RESPONSE_HEADERS: {
    144       scoped_ptr<base::Value> event_params(entry.ParametersToValue());
    145 
    146       scoped_refptr<net::HttpResponseHeaders> response_headers;
    147 
    148       if (!net::HttpResponseHeaders::FromNetLogParam(event_params.get(),
    149                                                      &response_headers)) {
    150         NOTREACHED();
    151       }
    152 
    153       info->http_status_code = response_headers->response_code();
    154       info->http_status_text = response_headers->GetStatusText();
    155       std::string name, value;
    156 
    157       // We need to clear headers in case the same url_request is reused for
    158       // several http requests (e.g. see http://crbug.com/80157).
    159       info->response_headers.clear();
    160 
    161       for (void* it = NULL;
    162            response_headers->EnumerateHeaderLines(&it, &name, &value); ) {
    163         info->response_headers.push_back(std::make_pair(name, value));
    164       }
    165       info->response_headers_text =
    166           net::HttpUtil::ConvertHeadersBackToHTTPResponse(
    167               response_headers->raw_headers());
    168       break;
    169     }
    170     case net::NetLog::TYPE_HTTP_STREAM_REQUEST_BOUND_TO_JOB: {
    171       scoped_ptr<base::Value> event_params(entry.ParametersToValue());
    172       net::NetLog::Source http_stream_job_source;
    173       if (!net::NetLog::Source::FromEventParameters(event_params.get(),
    174                                                     &http_stream_job_source)) {
    175         NOTREACHED();
    176         break;
    177       }
    178 
    179       uint32 http_stream_job_id = http_stream_job_source.id;
    180       HTTPStreamJobToSocketMap::iterator it =
    181           http_stream_job_to_socket_.find(http_stream_job_id);
    182       if (it == http_stream_job_to_socket_.end())
    183         return;
    184       uint32 socket_id = it->second;
    185 
    186       if (socket_to_request_.size() > kMaxNumEntries) {
    187         LOG(WARNING) << "The url request observer socket count has grown "
    188                         "larger than expected, resetting";
    189         socket_to_request_.clear();
    190       }
    191 
    192       socket_to_request_[socket_id] = entry.source().id;
    193       http_stream_job_to_socket_.erase(http_stream_job_id);
    194       break;
    195     }
    196     default:
    197       break;
    198   }
    199 }
    200 
    201 void DevToolsNetLogObserver::OnAddHTTPStreamJobEntry(
    202     const net::NetLog::Entry& entry) {
    203   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    204 
    205   if (entry.type() == net::NetLog::TYPE_SOCKET_POOL_BOUND_TO_SOCKET) {
    206     scoped_ptr<base::Value> event_params(entry.ParametersToValue());
    207     net::NetLog::Source socket_source;
    208     if (!net::NetLog::Source::FromEventParameters(event_params.get(),
    209                                                   &socket_source)) {
    210       NOTREACHED();
    211       return;
    212     }
    213 
    214     // Prevents us from passively growing the memory unbounded in
    215     // case something went wrong. Should not happen.
    216     if (http_stream_job_to_socket_.size() > kMaxNumEntries) {
    217       LOG(WARNING) << "The load timing observer http stream job count "
    218                       "has grown larger than expected, resetting";
    219       http_stream_job_to_socket_.clear();
    220     }
    221     http_stream_job_to_socket_[entry.source().id] = socket_source.id;
    222   }
    223 }
    224 
    225 void DevToolsNetLogObserver::OnAddSocketEntry(
    226     const net::NetLog::Entry& entry) {
    227   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    228 
    229   bool is_end = entry.phase() == net::NetLog::PHASE_END;
    230 
    231   SocketToRequestMap::iterator it =
    232       socket_to_request_.find(entry.source().id);
    233   if (it == socket_to_request_.end())
    234     return;
    235   uint32 request_id = it->second;
    236 
    237   if (entry.type() == net::NetLog::TYPE_SOCKET_IN_USE) {
    238     if (is_end)
    239       socket_to_request_.erase(entry.source().id);
    240     return;
    241   }
    242 
    243   RequestToEncodedDataLengthMap::iterator encoded_data_length_it =
    244       request_to_encoded_data_length_.find(request_id);
    245   if (encoded_data_length_it == request_to_encoded_data_length_.end())
    246     return;
    247 
    248   if (net::NetLog::TYPE_SOCKET_BYTES_RECEIVED == entry.type()) {
    249     int byte_count = 0;
    250     scoped_ptr<base::Value> value(entry.ParametersToValue());
    251     if (!value->IsType(base::Value::TYPE_DICTIONARY))
    252       return;
    253 
    254     base::DictionaryValue* dValue =
    255         static_cast<base::DictionaryValue*>(value.get());
    256     if (!dValue->GetInteger("byte_count", &byte_count))
    257       return;
    258 
    259     encoded_data_length_it->second += byte_count;
    260   }
    261 }
    262 
    263 void DevToolsNetLogObserver::Attach() {
    264   DCHECK(!instance_);
    265   net::NetLog* net_log = GetContentClient()->browser()->GetNetLog();
    266   if (net_log) {
    267     instance_ = new DevToolsNetLogObserver();
    268     net_log->AddThreadSafeObserver(instance_, net::NetLog::LOG_ALL_BUT_BYTES);
    269   }
    270 }
    271 
    272 void DevToolsNetLogObserver::Detach() {
    273   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    274 
    275   if (instance_) {
    276     // Safest not to do this in the destructor to maintain thread safety across
    277     // refactorings.
    278     instance_->net_log()->RemoveThreadSafeObserver(instance_);
    279     delete instance_;
    280     instance_ = NULL;
    281   }
    282 }
    283 
    284 DevToolsNetLogObserver* DevToolsNetLogObserver::GetInstance() {
    285   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    286 
    287   return instance_;
    288 }
    289 
    290 // static
    291 void DevToolsNetLogObserver::PopulateResponseInfo(
    292     net::URLRequest* request,
    293     ResourceResponse* response) {
    294   if (!(request->load_flags() & net::LOAD_REPORT_RAW_HEADERS))
    295     return;
    296 
    297   uint32 source_id = request->net_log().source().id;
    298   DevToolsNetLogObserver* dev_tools_net_log_observer =
    299       DevToolsNetLogObserver::GetInstance();
    300   if (dev_tools_net_log_observer == NULL)
    301     return;
    302   response->head.devtools_info =
    303       dev_tools_net_log_observer->GetResourceInfo(source_id);
    304 }
    305 
    306 // static
    307 int DevToolsNetLogObserver::GetAndResetEncodedDataLength(
    308     net::URLRequest* request) {
    309   if (!(request->load_flags() & net::LOAD_REPORT_RAW_HEADERS))
    310     return -1;
    311 
    312   uint32 source_id = request->net_log().source().id;
    313   DevToolsNetLogObserver* dev_tools_net_log_observer =
    314       DevToolsNetLogObserver::GetInstance();
    315   if (dev_tools_net_log_observer == NULL)
    316     return -1;
    317 
    318   RequestToEncodedDataLengthMap::iterator it =
    319       dev_tools_net_log_observer->request_to_encoded_data_length_.find(
    320           source_id);
    321   if (it == dev_tools_net_log_observer->request_to_encoded_data_length_.end())
    322     return -1;
    323   int encoded_data_length = it->second;
    324   it->second = 0;
    325   return encoded_data_length;
    326 }
    327 
    328 }  // namespace content
    329