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
DevToolsNetLogObserver()24 DevToolsNetLogObserver::DevToolsNetLogObserver() {
25 }
26
~DevToolsNetLogObserver()27 DevToolsNetLogObserver::~DevToolsNetLogObserver() {
28 }
29
30 DevToolsNetLogObserver::ResourceInfo*
GetResourceInfo(uint32 id)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
OnAddEntry(const net::NetLog::Entry & entry)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
OnAddURLRequestEntry(const net::NetLog::Entry & entry)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
Attach()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
Detach()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
GetInstance()188 DevToolsNetLogObserver* DevToolsNetLogObserver::GetInstance() {
189 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
190
191 return instance_;
192 }
193
194 // static
PopulateResponseInfo(net::URLRequest * request,ResourceResponse * response)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