• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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