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.
5 #include "content/browser/devtools/devtools_netlog_observer.h"
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"
20 const size_t kMaxNumEntries = 1000;
22 DevToolsNetLogObserver* DevToolsNetLogObserver::instance_ = NULL;
24 DevToolsNetLogObserver::DevToolsNetLogObserver() {
27 DevToolsNetLogObserver::~DevToolsNetLogObserver() {
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();
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))
43 if (entry.source().type == net::NetLog::SOURCE_URL_REQUEST)
44 OnAddURLRequestEntry(entry);
47 void DevToolsNetLogObserver::OnAddURLRequestEntry(
48 const net::NetLog::Entry& entry) {
49 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
51 bool is_begin = entry.phase() == net::NetLog::PHASE_BEGIN;
52 bool is_end = entry.phase() == net::NetLog::PHASE_END;
54 if (entry.type() == net::NetLog::TYPE_URL_REQUEST_START_JOB) {
57 scoped_ptr<base::Value> event_param(entry.ParametersToValue());
58 if (!net::StartEventLoadFlagsFromEventParams(event_param.get(),
63 if (!(load_flags & net::LOAD_REPORT_RAW_HEADERS))
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();
72 request_to_info_[entry.source().id] = new ResourceInfo();
75 } else if (entry.type() == net::NetLog::TYPE_REQUEST_ALIVE) {
76 // Cleanup records based on the TYPE_REQUEST_ALIVE entry.
78 request_to_info_.erase(entry.source().id);
82 ResourceInfo* info = GetResourceInfo(entry.source().id);
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;
92 if (!net::HttpRequestHeaders::FromNetLogParam(event_params.get(),
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();
102 for (net::HttpRequestHeaders::Iterator it(request_headers);
104 info->request_headers.push_back(std::make_pair(it.name(), it.value()));
106 info->request_headers_text = request_line + request_headers.ToString();
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;
113 if (!net::SpdyHeaderBlockFromNetLogParam(event_params.get(),
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();
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));
126 info->request_headers_text = "";
129 case net::NetLog::TYPE_HTTP_TRANSACTION_READ_RESPONSE_HEADERS: {
130 scoped_ptr<base::Value> event_params(entry.ParametersToValue());
132 scoped_refptr<net::HttpResponseHeaders> response_headers;
134 if (!net::HttpResponseHeaders::FromNetLogParam(event_params.get(),
135 &response_headers)) {
139 info->http_status_code = response_headers->response_code();
140 info->http_status_text = response_headers->GetStatusText();
141 std::string name, value;
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();
147 for (void* it = NULL;
148 response_headers->EnumerateHeaderLines(&it, &name, &value); ) {
149 info->response_headers.push_back(std::make_pair(name, value));
151 info->response_headers_text =
152 net::HttpUtil::ConvertHeadersBackToHTTPResponse(
153 response_headers->raw_headers());
161 void DevToolsNetLogObserver::Attach() {
163 net::NetLog* net_log = GetContentClient()->browser()->GetNetLog();
165 instance_ = new DevToolsNetLogObserver();
166 net_log->AddThreadSafeObserver(instance_, net::NetLog::LOG_ALL_BUT_BYTES);
170 void DevToolsNetLogObserver::Detach() {
171 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
174 // Safest not to do this in the destructor to maintain thread safety across
176 instance_->net_log()->RemoveThreadSafeObserver(instance_);
182 DevToolsNetLogObserver* DevToolsNetLogObserver::GetInstance() {
183 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
189 void DevToolsNetLogObserver::PopulateResponseInfo(
190 net::URLRequest* request,
191 ResourceResponse* response) {
192 if (!(request->load_flags() & net::LOAD_REPORT_RAW_HEADERS))
195 uint32 source_id = request->net_log().source().id;
196 DevToolsNetLogObserver* dev_tools_net_log_observer =
197 DevToolsNetLogObserver::GetInstance();
198 if (dev_tools_net_log_observer == NULL)
200 response->head.devtools_info =
201 dev_tools_net_log_observer->GetResourceInfo(source_id);
204 } // namespace content