OLD | NEW |
| (Empty) |
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/debugger/devtools_netlog_observer.h" | |
6 | |
7 #include "base/string_tokenizer.h" | |
8 #include "base/string_util.h" | |
9 #include "base/values.h" | |
10 #include "content/public/browser/browser_thread.h" | |
11 #include "content/public/browser/content_browser_client.h" | |
12 #include "content/public/common/resource_response.h" | |
13 #include "net/base/load_flags.h" | |
14 #include "net/http/http_response_headers.h" | |
15 #include "net/http/http_util.h" | |
16 #include "net/spdy/spdy_header_block.h" | |
17 #include "net/url_request/url_request.h" | |
18 #include "net/url_request/url_request_netlog_params.h" | |
19 #include "webkit/glue/resource_loader_bridge.h" | |
20 | |
21 namespace content { | |
22 const size_t kMaxNumEntries = 1000; | |
23 | |
24 DevToolsNetLogObserver* DevToolsNetLogObserver::instance_ = NULL; | |
25 | |
26 DevToolsNetLogObserver::DevToolsNetLogObserver() { | |
27 } | |
28 | |
29 DevToolsNetLogObserver::~DevToolsNetLogObserver() { | |
30 } | |
31 | |
32 DevToolsNetLogObserver::ResourceInfo* | |
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 | |
40 void DevToolsNetLogObserver::OnAddEntry(const net::NetLog::Entry& entry) { | |
41 // The events that the Observer is interested in only occur on the IO thread. | |
42 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) | |
43 return; | |
44 | |
45 if (entry.source().type == net::NetLog::SOURCE_URL_REQUEST) | |
46 OnAddURLRequestEntry(entry); | |
47 else if (entry.source().type == net::NetLog::SOURCE_HTTP_STREAM_JOB) | |
48 OnAddHTTPStreamJobEntry(entry); | |
49 else if (entry.source().type == net::NetLog::SOURCE_SOCKET) | |
50 OnAddSocketEntry(entry); | |
51 } | |
52 | |
53 void DevToolsNetLogObserver::OnAddURLRequestEntry( | |
54 const net::NetLog::Entry& entry) { | |
55 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
56 | |
57 bool is_begin = entry.phase() == net::NetLog::PHASE_BEGIN; | |
58 bool is_end = entry.phase() == net::NetLog::PHASE_END; | |
59 | |
60 if (entry.type() == net::NetLog::TYPE_URL_REQUEST_START_JOB) { | |
61 if (is_begin) { | |
62 int load_flags; | |
63 scoped_ptr<Value> event_param(entry.ParametersToValue()); | |
64 if (!net::StartEventLoadFlagsFromEventParams(event_param.get(), | |
65 &load_flags)) { | |
66 return; | |
67 } | |
68 | |
69 if (!(load_flags & net::LOAD_REPORT_RAW_HEADERS)) | |
70 return; | |
71 | |
72 if (request_to_info_.size() > kMaxNumEntries) { | |
73 LOG(WARNING) << "The raw headers observer url request count has grown " | |
74 "larger than expected, resetting"; | |
75 request_to_info_.clear(); | |
76 } | |
77 | |
78 request_to_info_[entry.source().id] = new ResourceInfo(); | |
79 | |
80 if (request_to_encoded_data_length_.size() > kMaxNumEntries) { | |
81 LOG(WARNING) << "The encoded data length observer url request count " | |
82 "has grown larger than expected, resetting"; | |
83 request_to_encoded_data_length_.clear(); | |
84 } | |
85 | |
86 request_to_encoded_data_length_[entry.source().id] = 0; | |
87 } | |
88 return; | |
89 } else if (entry.type() == net::NetLog::TYPE_REQUEST_ALIVE) { | |
90 // Cleanup records based on the TYPE_REQUEST_ALIVE entry. | |
91 if (is_end) { | |
92 request_to_info_.erase(entry.source().id); | |
93 request_to_encoded_data_length_.erase(entry.source().id); | |
94 } | |
95 return; | |
96 } | |
97 | |
98 ResourceInfo* info = GetResourceInfo(entry.source().id); | |
99 if (!info) | |
100 return; | |
101 | |
102 switch (entry.type()) { | |
103 case net::NetLog::TYPE_HTTP_TRANSACTION_SEND_REQUEST_HEADERS: { | |
104 scoped_ptr<Value> event_params(entry.ParametersToValue()); | |
105 std::string request_line; | |
106 net::HttpRequestHeaders request_headers; | |
107 | |
108 if (!net::HttpRequestHeaders::FromNetLogParam(event_params.get(), | |
109 &request_headers, | |
110 &request_line)) { | |
111 NOTREACHED(); | |
112 } | |
113 | |
114 // We need to clear headers in case the same url_request is reused for | |
115 // several http requests (e.g. see http://crbug.com/80157). | |
116 info->request_headers.clear(); | |
117 | |
118 for (net::HttpRequestHeaders::Iterator it(request_headers); | |
119 it.GetNext();) { | |
120 info->request_headers.push_back(std::make_pair(it.name(), it.value())); | |
121 } | |
122 info->request_headers_text = request_line + request_headers.ToString(); | |
123 break; | |
124 } | |
125 case net::NetLog::TYPE_HTTP_TRANSACTION_SPDY_SEND_REQUEST_HEADERS: { | |
126 scoped_ptr<Value> event_params(entry.ParametersToValue()); | |
127 net::SpdyHeaderBlock request_headers; | |
128 | |
129 if (!net::SpdyHeaderBlockFromNetLogParam(event_params.get(), | |
130 &request_headers)) { | |
131 NOTREACHED(); | |
132 } | |
133 | |
134 // We need to clear headers in case the same url_request is reused for | |
135 // several http requests (e.g. see http://crbug.com/80157). | |
136 info->request_headers.clear(); | |
137 | |
138 for (net::SpdyHeaderBlock::const_iterator it = request_headers.begin(); | |
139 it != request_headers.end(); ++it) { | |
140 info->request_headers.push_back(std::make_pair(it->first, it->second)); | |
141 } | |
142 info->request_headers_text = ""; | |
143 break; | |
144 } | |
145 case net::NetLog::TYPE_HTTP_TRANSACTION_READ_RESPONSE_HEADERS: { | |
146 scoped_ptr<Value> event_params(entry.ParametersToValue()); | |
147 | |
148 scoped_refptr<net::HttpResponseHeaders> response_headers; | |
149 | |
150 if (!net::HttpResponseHeaders::FromNetLogParam(event_params.get(), | |
151 &response_headers)) { | |
152 NOTREACHED(); | |
153 } | |
154 | |
155 info->http_status_code = response_headers->response_code(); | |
156 info->http_status_text = response_headers->GetStatusText(); | |
157 std::string name, value; | |
158 | |
159 // We need to clear headers in case the same url_request is reused for | |
160 // several http requests (e.g. see http://crbug.com/80157). | |
161 info->response_headers.clear(); | |
162 | |
163 for (void* it = NULL; | |
164 response_headers->EnumerateHeaderLines(&it, &name, &value); ) { | |
165 info->response_headers.push_back(std::make_pair(name, value)); | |
166 } | |
167 info->response_headers_text = | |
168 net::HttpUtil::ConvertHeadersBackToHTTPResponse( | |
169 response_headers->raw_headers()); | |
170 break; | |
171 } | |
172 case net::NetLog::TYPE_HTTP_STREAM_REQUEST_BOUND_TO_JOB: { | |
173 scoped_ptr<Value> event_params(entry.ParametersToValue()); | |
174 net::NetLog::Source http_stream_job_source; | |
175 if (!net::NetLog::Source::FromEventParameters(event_params.get(), | |
176 &http_stream_job_source)) { | |
177 NOTREACHED(); | |
178 break; | |
179 } | |
180 | |
181 uint32 http_stream_job_id = http_stream_job_source.id; | |
182 HTTPStreamJobToSocketMap::iterator it = | |
183 http_stream_job_to_socket_.find(http_stream_job_id); | |
184 if (it == http_stream_job_to_socket_.end()) | |
185 return; | |
186 uint32 socket_id = it->second; | |
187 | |
188 if (socket_to_request_.size() > kMaxNumEntries) { | |
189 LOG(WARNING) << "The url request observer socket count has grown " | |
190 "larger than expected, resetting"; | |
191 socket_to_request_.clear(); | |
192 } | |
193 | |
194 socket_to_request_[socket_id] = entry.source().id; | |
195 http_stream_job_to_socket_.erase(http_stream_job_id); | |
196 break; | |
197 } | |
198 default: | |
199 break; | |
200 } | |
201 } | |
202 | |
203 void DevToolsNetLogObserver::OnAddHTTPStreamJobEntry( | |
204 const net::NetLog::Entry& entry) { | |
205 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
206 | |
207 if (entry.type() == net::NetLog::TYPE_SOCKET_POOL_BOUND_TO_SOCKET) { | |
208 scoped_ptr<Value> event_params(entry.ParametersToValue()); | |
209 net::NetLog::Source socket_source; | |
210 if (!net::NetLog::Source::FromEventParameters(event_params.get(), | |
211 &socket_source)) { | |
212 NOTREACHED(); | |
213 return; | |
214 } | |
215 | |
216 // Prevents us from passively growing the memory unbounded in | |
217 // case something went wrong. Should not happen. | |
218 if (http_stream_job_to_socket_.size() > kMaxNumEntries) { | |
219 LOG(WARNING) << "The load timing observer http stream job count " | |
220 "has grown larger than expected, resetting"; | |
221 http_stream_job_to_socket_.clear(); | |
222 } | |
223 http_stream_job_to_socket_[entry.source().id] = socket_source.id; | |
224 } | |
225 } | |
226 | |
227 void DevToolsNetLogObserver::OnAddSocketEntry( | |
228 const net::NetLog::Entry& entry) { | |
229 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
230 | |
231 bool is_end = entry.phase() == net::NetLog::PHASE_END; | |
232 | |
233 SocketToRequestMap::iterator it = | |
234 socket_to_request_.find(entry.source().id); | |
235 if (it == socket_to_request_.end()) | |
236 return; | |
237 uint32 request_id = it->second; | |
238 | |
239 if (entry.type() == net::NetLog::TYPE_SOCKET_IN_USE) { | |
240 if (is_end) | |
241 socket_to_request_.erase(entry.source().id); | |
242 return; | |
243 } | |
244 | |
245 RequestToEncodedDataLengthMap::iterator encoded_data_length_it = | |
246 request_to_encoded_data_length_.find(request_id); | |
247 if (encoded_data_length_it == request_to_encoded_data_length_.end()) | |
248 return; | |
249 | |
250 if (net::NetLog::TYPE_SOCKET_BYTES_RECEIVED == entry.type()) { | |
251 int byte_count = 0; | |
252 scoped_ptr<Value> value(entry.ParametersToValue()); | |
253 if (!value->IsType(Value::TYPE_DICTIONARY)) | |
254 return; | |
255 | |
256 DictionaryValue* dValue = static_cast<DictionaryValue*>(value.get()); | |
257 if (!dValue->GetInteger("byte_count", &byte_count)) | |
258 return; | |
259 | |
260 encoded_data_length_it->second += byte_count; | |
261 } | |
262 } | |
263 | |
264 void DevToolsNetLogObserver::Attach() { | |
265 DCHECK(!instance_); | |
266 net::NetLog* net_log = GetContentClient()->browser()->GetNetLog(); | |
267 if (net_log) { | |
268 instance_ = new DevToolsNetLogObserver(); | |
269 net_log->AddThreadSafeObserver(instance_, net::NetLog::LOG_ALL_BUT_BYTES); | |
270 } | |
271 } | |
272 | |
273 void DevToolsNetLogObserver::Detach() { | |
274 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
275 | |
276 if (instance_) { | |
277 // Safest not to do this in the destructor to maintain thread safety across | |
278 // refactorings. | |
279 instance_->net_log()->RemoveThreadSafeObserver(instance_); | |
280 delete instance_; | |
281 instance_ = NULL; | |
282 } | |
283 } | |
284 | |
285 DevToolsNetLogObserver* DevToolsNetLogObserver::GetInstance() { | |
286 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
287 | |
288 return instance_; | |
289 } | |
290 | |
291 // static | |
292 void DevToolsNetLogObserver::PopulateResponseInfo( | |
293 net::URLRequest* request, | |
294 ResourceResponse* response) { | |
295 if (!(request->load_flags() & net::LOAD_REPORT_RAW_HEADERS)) | |
296 return; | |
297 | |
298 uint32 source_id = request->net_log().source().id; | |
299 DevToolsNetLogObserver* dev_tools_net_log_observer = | |
300 DevToolsNetLogObserver::GetInstance(); | |
301 if (dev_tools_net_log_observer == NULL) | |
302 return; | |
303 response->head.devtools_info = | |
304 dev_tools_net_log_observer->GetResourceInfo(source_id); | |
305 } | |
306 | |
307 // static | |
308 int DevToolsNetLogObserver::GetAndResetEncodedDataLength( | |
309 net::URLRequest* request) { | |
310 if (!(request->load_flags() & net::LOAD_REPORT_RAW_HEADERS)) | |
311 return -1; | |
312 | |
313 uint32 source_id = request->net_log().source().id; | |
314 DevToolsNetLogObserver* dev_tools_net_log_observer = | |
315 DevToolsNetLogObserver::GetInstance(); | |
316 if (dev_tools_net_log_observer == NULL) | |
317 return -1; | |
318 | |
319 RequestToEncodedDataLengthMap::iterator it = | |
320 dev_tools_net_log_observer->request_to_encoded_data_length_.find( | |
321 source_id); | |
322 if (it == dev_tools_net_log_observer->request_to_encoded_data_length_.end()) | |
323 return -1; | |
324 int encoded_data_length = it->second; | |
325 it->second = 0; | |
326 return encoded_data_length; | |
327 } | |
328 | |
329 } // namespace content | |
OLD | NEW |