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