1 // Copyright (c) 2011 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 "chrome/browser/debugger/devtools_netlog_observer.h"
6
7 #include "base/string_util.h"
8 #include "base/values.h"
9 #include "chrome/browser/io_thread.h"
10 #include "content/common/resource_response.h"
11 #include "net/base/load_flags.h"
12 #include "net/http/http_net_log_params.h"
13 #include "net/http/http_response_headers.h"
14 #include "net/url_request/url_request.h"
15 #include "net/url_request/url_request_netlog_params.h"
16 #include "webkit/glue/resource_loader_bridge.h"
17
18 const size_t kMaxNumEntries = 1000;
19
20 DevToolsNetLogObserver* DevToolsNetLogObserver::instance_ = NULL;
21
DevToolsNetLogObserver(ChromeNetLog * chrome_net_log)22 DevToolsNetLogObserver::DevToolsNetLogObserver(ChromeNetLog* chrome_net_log)
23 : ChromeNetLog::ThreadSafeObserver(net::NetLog::LOG_ALL_BUT_BYTES),
24 chrome_net_log_(chrome_net_log) {
25 chrome_net_log_->AddObserver(this);
26 }
27
~DevToolsNetLogObserver()28 DevToolsNetLogObserver::~DevToolsNetLogObserver() {
29 chrome_net_log_->RemoveObserver(this);
30 }
31
32 DevToolsNetLogObserver::ResourceInfo*
GetResourceInfo(uint32 id)33 DevToolsNetLogObserver::GetResourceInfo(uint32 id) {
34 RequestToInfoMap::iterator it = request_to_info_.find(id);
35 if (it != request_to_info_.end())
36 return it->second;
37 return NULL;
38 }
39
OnAddEntry(net::NetLog::EventType type,const base::TimeTicks & time,const net::NetLog::Source & source,net::NetLog::EventPhase phase,net::NetLog::EventParameters * params)40 void DevToolsNetLogObserver::OnAddEntry(net::NetLog::EventType type,
41 const base::TimeTicks& time,
42 const net::NetLog::Source& source,
43 net::NetLog::EventPhase phase,
44 net::NetLog::EventParameters* params) {
45 // The events that the Observer is interested in only occur on the IO thread.
46 if (!BrowserThread::CurrentlyOn(BrowserThread::IO))
47 return;
48
49 // The events that the Observer is interested in only occur on the IO thread.
50 if (!BrowserThread::CurrentlyOn(BrowserThread::IO))
51 return;
52 if (source.type == net::NetLog::SOURCE_URL_REQUEST)
53 OnAddURLRequestEntry(type, time, source, phase, params);
54 else if (source.type == net::NetLog::SOURCE_HTTP_STREAM_JOB)
55 OnAddHTTPStreamJobEntry(type, time, source, phase, params);
56 else if (source.type == net::NetLog::SOURCE_SOCKET)
57 OnAddSocketEntry(type, time, source, phase, params);
58 }
59
OnAddURLRequestEntry(net::NetLog::EventType type,const base::TimeTicks & time,const net::NetLog::Source & source,net::NetLog::EventPhase phase,net::NetLog::EventParameters * params)60 void DevToolsNetLogObserver::OnAddURLRequestEntry(
61 net::NetLog::EventType type,
62 const base::TimeTicks& time,
63 const net::NetLog::Source& source,
64 net::NetLog::EventPhase phase,
65 net::NetLog::EventParameters* params) {
66 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
67
68 bool is_begin = phase == net::NetLog::PHASE_BEGIN;
69 bool is_end = phase == net::NetLog::PHASE_END;
70
71 if (type == net::NetLog::TYPE_URL_REQUEST_START_JOB) {
72 if (is_begin) {
73 int load_flags = static_cast<
74 net::URLRequestStartEventParameters*>(params)->load_flags();
75 if (!(load_flags & net::LOAD_REPORT_RAW_HEADERS))
76 return;
77
78 if (request_to_info_.size() > kMaxNumEntries) {
79 LOG(WARNING) << "The raw headers observer url request count has grown "
80 "larger than expected, resetting";
81 request_to_info_.clear();
82 }
83
84 request_to_info_[source.id] = new ResourceInfo();
85
86 if (request_to_encoded_data_length_.size() > kMaxNumEntries) {
87 LOG(WARNING) << "The encoded data length observer url request count "
88 "has grown larger than expected, resetting";
89 request_to_encoded_data_length_.clear();
90 }
91
92 request_to_encoded_data_length_[source.id] = 0;
93 }
94 return;
95 } else if (type == net::NetLog::TYPE_REQUEST_ALIVE) {
96 // Cleanup records based on the TYPE_REQUEST_ALIVE entry.
97 if (is_end) {
98 request_to_info_.erase(source.id);
99 request_to_encoded_data_length_.erase(source.id);
100 }
101 return;
102 }
103
104 ResourceInfo* info = GetResourceInfo(source.id);
105 if (!info)
106 return;
107
108 switch (type) {
109 case net::NetLog::TYPE_HTTP_TRANSACTION_SEND_REQUEST_HEADERS: {
110 const net::HttpRequestHeaders &request_headers =
111 static_cast<net::NetLogHttpRequestParameter*>(params)->GetHeaders();
112 for (net::HttpRequestHeaders::Iterator it(request_headers);
113 it.GetNext();) {
114 info->request_headers.push_back(std::make_pair(it.name(),
115 it.value()));
116 }
117 break;
118 }
119 case net::NetLog::TYPE_HTTP_TRANSACTION_READ_RESPONSE_HEADERS: {
120 const net::HttpResponseHeaders& response_headers =
121 static_cast<net::NetLogHttpResponseParameter*>(params)->GetHeaders();
122 info->http_status_code = response_headers.response_code();
123 info->http_status_text = response_headers.GetStatusText();
124 std::string name, value;
125 for (void* it = NULL;
126 response_headers.EnumerateHeaderLines(&it, &name, &value); ) {
127 info->response_headers.push_back(std::make_pair(name, value));
128 }
129 break;
130 }
131 case net::NetLog::TYPE_HTTP_STREAM_REQUEST_BOUND_TO_JOB: {
132 uint32 http_stream_job_id = static_cast<net::NetLogSourceParameter*>(
133 params)->value().id;
134 HTTPStreamJobToSocketMap::iterator it =
135 http_stream_job_to_socket_.find(http_stream_job_id);
136 if (it == http_stream_job_to_socket_.end())
137 return;
138 uint32 socket_id = it->second;
139
140 if (socket_to_request_.size() > kMaxNumEntries) {
141 LOG(WARNING) << "The url request observer socket count has grown "
142 "larger than expected, resetting";
143 socket_to_request_.clear();
144 }
145
146 socket_to_request_[socket_id] = source.id;
147 http_stream_job_to_socket_.erase(http_stream_job_id);
148 break;
149 }
150 default:
151 break;
152 }
153 }
154
OnAddHTTPStreamJobEntry(net::NetLog::EventType type,const base::TimeTicks & time,const net::NetLog::Source & source,net::NetLog::EventPhase phase,net::NetLog::EventParameters * params)155 void DevToolsNetLogObserver::OnAddHTTPStreamJobEntry(
156 net::NetLog::EventType type,
157 const base::TimeTicks& time,
158 const net::NetLog::Source& source,
159 net::NetLog::EventPhase phase,
160 net::NetLog::EventParameters* params) {
161 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
162
163 if (type == net::NetLog::TYPE_SOCKET_POOL_BOUND_TO_SOCKET) {
164 uint32 socket_id = static_cast<net::NetLogSourceParameter*>(
165 params)->value().id;
166
167 // Prevents us from passively growing the memory unbounded in
168 // case something went wrong. Should not happen.
169 if (http_stream_job_to_socket_.size() > kMaxNumEntries) {
170 LOG(WARNING) << "The load timing observer http stream job count "
171 "has grown larger than expected, resetting";
172 http_stream_job_to_socket_.clear();
173 }
174 http_stream_job_to_socket_[source.id] = socket_id;
175 }
176 }
177
OnAddSocketEntry(net::NetLog::EventType type,const base::TimeTicks & time,const net::NetLog::Source & source,net::NetLog::EventPhase phase,net::NetLog::EventParameters * params)178 void DevToolsNetLogObserver::OnAddSocketEntry(
179 net::NetLog::EventType type,
180 const base::TimeTicks& time,
181 const net::NetLog::Source& source,
182 net::NetLog::EventPhase phase,
183 net::NetLog::EventParameters* params) {
184 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
185
186 bool is_end = phase == net::NetLog::PHASE_END;
187
188 SocketToRequestMap::iterator it = socket_to_request_.find(source.id);
189 if (it == socket_to_request_.end())
190 return;
191 uint32 request_id = it->second;
192
193 if (type == net::NetLog::TYPE_SOCKET_IN_USE) {
194 if (is_end)
195 socket_to_request_.erase(source.id);
196 return;
197 }
198
199 RequestToEncodedDataLengthMap::iterator encoded_data_length_it =
200 request_to_encoded_data_length_.find(request_id);
201 if (encoded_data_length_it == request_to_encoded_data_length_.end())
202 return;
203
204 if (net::NetLog::TYPE_SOCKET_BYTES_RECEIVED == type) {
205 int byte_count = 0;
206 Value* value = params->ToValue();
207 if (!value->IsType(Value::TYPE_DICTIONARY))
208 return;
209
210 DictionaryValue* dValue = static_cast<DictionaryValue*>(value);
211 if (!dValue->GetInteger("byte_count", &byte_count))
212 return;
213
214 encoded_data_length_it->second += byte_count;
215 }
216 }
217
Attach(IOThread * io_thread)218 void DevToolsNetLogObserver::Attach(IOThread* io_thread) {
219 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
220 DCHECK(!instance_);
221
222 instance_ = new DevToolsNetLogObserver(io_thread->net_log());
223 }
224
Detach()225 void DevToolsNetLogObserver::Detach() {
226 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
227 DCHECK(instance_);
228
229 delete instance_;
230 instance_ = NULL;
231 }
232
GetInstance()233 DevToolsNetLogObserver* DevToolsNetLogObserver::GetInstance() {
234 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
235
236 return instance_;
237 }
238
239 // static
PopulateResponseInfo(net::URLRequest * request,ResourceResponse * response)240 void DevToolsNetLogObserver::PopulateResponseInfo(net::URLRequest* request,
241 ResourceResponse* response) {
242 if (!(request->load_flags() & net::LOAD_REPORT_RAW_HEADERS))
243 return;
244
245 uint32 source_id = request->net_log().source().id;
246 DevToolsNetLogObserver* dev_tools_net_log_observer =
247 DevToolsNetLogObserver::GetInstance();
248 if (dev_tools_net_log_observer == NULL)
249 return;
250 response->response_head.devtools_info =
251 dev_tools_net_log_observer->GetResourceInfo(source_id);
252 }
253
254 // static
GetAndResetEncodedDataLength(net::URLRequest * request)255 int DevToolsNetLogObserver::GetAndResetEncodedDataLength(
256 net::URLRequest* request) {
257 if (!(request->load_flags() & net::LOAD_REPORT_RAW_HEADERS))
258 return -1;
259
260 uint32 source_id = request->net_log().source().id;
261 DevToolsNetLogObserver* dev_tools_net_log_observer =
262 DevToolsNetLogObserver::GetInstance();
263 if (dev_tools_net_log_observer == NULL)
264 return -1;
265
266 RequestToEncodedDataLengthMap::iterator it =
267 dev_tools_net_log_observer->request_to_encoded_data_length_.find(
268 source_id);
269 if (it == dev_tools_net_log_observer->request_to_encoded_data_length_.end())
270 return -1;
271 int encoded_data_length = it->second;
272 it->second = 0;
273 return encoded_data_length;
274 }
275