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
));
152 if (!info
->request_headers_text
.empty()) {
153 info
->response_headers_text
=
154 net::HttpUtil::ConvertHeadersBackToHTTPResponse(
155 response_headers
->raw_headers());
158 info
->response_headers_text
= "";
167 void DevToolsNetLogObserver::Attach() {
169 net::NetLog
* net_log
= GetContentClient()->browser()->GetNetLog();
171 instance_
= new DevToolsNetLogObserver();
172 net_log
->AddThreadSafeObserver(instance_
, net::NetLog::LOG_ALL_BUT_BYTES
);
176 void DevToolsNetLogObserver::Detach() {
177 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
180 // Safest not to do this in the destructor to maintain thread safety across
182 instance_
->net_log()->RemoveThreadSafeObserver(instance_
);
188 DevToolsNetLogObserver
* DevToolsNetLogObserver::GetInstance() {
189 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
195 void DevToolsNetLogObserver::PopulateResponseInfo(
196 net::URLRequest
* request
,
197 ResourceResponse
* response
) {
198 if (!(request
->load_flags() & net::LOAD_REPORT_RAW_HEADERS
))
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
)
206 response
->head
.devtools_info
=
207 dev_tools_net_log_observer
->GetResourceInfo(source_id
);
210 } // namespace content