devtools_netlog_observer.cc revision ddb351dbec246cf1fab5ec20d2d5520909041de1
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
18const size_t kMaxNumEntries = 1000;
19
20DevToolsNetLogObserver* DevToolsNetLogObserver::instance_ = NULL;
21
22DevToolsNetLogObserver::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
28DevToolsNetLogObserver::~DevToolsNetLogObserver() {
29  chrome_net_log_->RemoveObserver(this);
30}
31
32DevToolsNetLogObserver::ResourceInfo*
33DevToolsNetLogObserver::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
40void 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
60void 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
155void 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
178void 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
218void 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
225void DevToolsNetLogObserver::Detach() {
226  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
227  DCHECK(instance_);
228
229  delete instance_;
230  instance_ = NULL;
231}
232
233DevToolsNetLogObserver* DevToolsNetLogObserver::GetInstance() {
234  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
235
236  return instance_;
237}
238
239// static
240void 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
255int 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