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 }
     46 
     47 void DevToolsNetLogObserver::OnAddURLRequestEntry(
     48     const net::NetLog::Entry& entry) {
     49   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     50 
     51   bool is_begin = entry.phase() == net::NetLog::PHASE_BEGIN;
     52   bool is_end = entry.phase() == net::NetLog::PHASE_END;
     53 
     54   if (entry.type() == net::NetLog::TYPE_URL_REQUEST_START_JOB) {
     55     if (is_begin) {
     56       int load_flags;
     57       scoped_ptr<base::Value> event_param(entry.ParametersToValue());
     58       if (!net::StartEventLoadFlagsFromEventParams(event_param.get(),
     59                                                    &load_flags)) {
     60         return;
     61       }
     62 
     63       if (!(load_flags & net::LOAD_REPORT_RAW_HEADERS))
     64         return;
     65 
     66       if (request_to_info_.size() > kMaxNumEntries) {
     67         LOG(WARNING) << "The raw headers observer url request count has grown "
     68                         "larger than expected, resetting";
     69         request_to_info_.clear();
     70       }
     71 
     72       request_to_info_[entry.source().id] = new ResourceInfo();
     73     }
     74     return;
     75   } else if (entry.type() == net::NetLog::TYPE_REQUEST_ALIVE) {
     76     // Cleanup records based on the TYPE_REQUEST_ALIVE entry.
     77     if (is_end)
     78       request_to_info_.erase(entry.source().id);
     79     return;
     80   }
     81 
     82   ResourceInfo* info = GetResourceInfo(entry.source().id);
     83   if (!info)
     84     return;
     85 
     86   switch (entry.type()) {
     87     case net::NetLog::TYPE_HTTP_TRANSACTION_SEND_REQUEST_HEADERS: {
     88       scoped_ptr<base::Value> event_params(entry.ParametersToValue());
     89       std::string request_line;
     90       net::HttpRequestHeaders request_headers;
     91 
     92       if (!net::HttpRequestHeaders::FromNetLogParam(event_params.get(),
     93                                                     &request_headers,
     94                                                     &request_line)) {
     95         NOTREACHED();
     96       }
     97 
     98       // We need to clear headers in case the same url_request is reused for
     99       // several http requests (e.g. see http://crbug.com/80157).
    100       info->request_headers.clear();
    101 
    102       for (net::HttpRequestHeaders::Iterator it(request_headers);
    103            it.GetNext();) {
    104         info->request_headers.push_back(std::make_pair(it.name(), it.value()));
    105       }
    106       info->request_headers_text = request_line + request_headers.ToString();
    107       break;
    108     }
    109     case net::NetLog::TYPE_HTTP_TRANSACTION_SPDY_SEND_REQUEST_HEADERS: {
    110       scoped_ptr<base::Value> event_params(entry.ParametersToValue());
    111       net::SpdyHeaderBlock request_headers;
    112 
    113       if (!net::SpdyHeaderBlockFromNetLogParam(event_params.get(),
    114                                                &request_headers)) {
    115         NOTREACHED();
    116       }
    117 
    118       // We need to clear headers in case the same url_request is reused for
    119       // several http requests (e.g. see http://crbug.com/80157).
    120       info->request_headers.clear();
    121 
    122       for (net::SpdyHeaderBlock::const_iterator it = request_headers.begin();
    123            it != request_headers.end(); ++it) {
    124         info->request_headers.push_back(std::make_pair(it->first, it->second));
    125       }
    126       info->request_headers_text = "";
    127       break;
    128     }
    129     case net::NetLog::TYPE_HTTP_TRANSACTION_READ_RESPONSE_HEADERS: {
    130       scoped_ptr<base::Value> event_params(entry.ParametersToValue());
    131 
    132       scoped_refptr<net::HttpResponseHeaders> response_headers;
    133 
    134       if (!net::HttpResponseHeaders::FromNetLogParam(event_params.get(),
    135                                                      &response_headers)) {
    136         NOTREACHED();
    137       }
    138 
    139       info->http_status_code = response_headers->response_code();
    140       info->http_status_text = response_headers->GetStatusText();
    141       std::string name, value;
    142 
    143       // We need to clear headers in case the same url_request is reused for
    144       // several http requests (e.g. see http://crbug.com/80157).
    145       info->response_headers.clear();
    146 
    147       for (void* it = NULL;
    148            response_headers->EnumerateHeaderLines(&it, &name, &value); ) {
    149         info->response_headers.push_back(std::make_pair(name, value));
    150       }
    151 
    152       if (!info->request_headers_text.empty()) {
    153         info->response_headers_text =
    154             net::HttpUtil::ConvertHeadersBackToHTTPResponse(
    155                 response_headers->raw_headers());
    156       } else {
    157         // SPDY request.
    158         info->response_headers_text = "";
    159       }
    160       break;
    161     }
    162     default:
    163       break;
    164   }
    165 }
    166 
    167 void DevToolsNetLogObserver::Attach() {
    168   DCHECK(!instance_);
    169   net::NetLog* net_log = GetContentClient()->browser()->GetNetLog();
    170   if (net_log) {
    171     instance_ = new DevToolsNetLogObserver();
    172     net_log->AddThreadSafeObserver(instance_, net::NetLog::LOG_ALL_BUT_BYTES);
    173   }
    174 }
    175 
    176 void DevToolsNetLogObserver::Detach() {
    177   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    178 
    179   if (instance_) {
    180     // Safest not to do this in the destructor to maintain thread safety across
    181     // refactorings.
    182     instance_->net_log()->RemoveThreadSafeObserver(instance_);
    183     delete instance_;
    184     instance_ = NULL;
    185   }
    186 }
    187 
    188 DevToolsNetLogObserver* DevToolsNetLogObserver::GetInstance() {
    189   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    190 
    191   return instance_;
    192 }
    193 
    194 // static
    195 void DevToolsNetLogObserver::PopulateResponseInfo(
    196     net::URLRequest* request,
    197     ResourceResponse* response) {
    198   if (!(request->load_flags() & net::LOAD_REPORT_RAW_HEADERS))
    199     return;
    200 
    201   uint32 source_id = request->net_log().source().id;
    202   DevToolsNetLogObserver* dev_tools_net_log_observer =
    203       DevToolsNetLogObserver::GetInstance();
    204   if (dev_tools_net_log_observer == NULL)
    205     return;
    206   response->head.devtools_info =
    207       dev_tools_net_log_observer->GetResourceInfo(source_id);
    208 }
    209 
    210 }  // namespace content
    211