summaryrefslogtreecommitdiffstats
path: root/content/browser/devtools/devtools_netlog_observer.cc
diff options
context:
space:
mode:
Diffstat (limited to 'content/browser/devtools/devtools_netlog_observer.cc')
-rw-r--r--content/browser/devtools/devtools_netlog_observer.cc329
1 files changed, 329 insertions, 0 deletions
diff --git a/content/browser/devtools/devtools_netlog_observer.cc b/content/browser/devtools/devtools_netlog_observer.cc
new file mode 100644
index 0000000..f65bc42
--- /dev/null
+++ b/content/browser/devtools/devtools_netlog_observer.cc
@@ -0,0 +1,329 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/devtools/devtools_netlog_observer.h"
+
+#include "base/string_tokenizer.h"
+#include "base/string_util.h"
+#include "base/values.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/content_browser_client.h"
+#include "content/public/common/resource_response.h"
+#include "net/base/load_flags.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_util.h"
+#include "net/spdy/spdy_header_block.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_netlog_params.h"
+#include "webkit/glue/resource_loader_bridge.h"
+
+namespace content {
+const size_t kMaxNumEntries = 1000;
+
+DevToolsNetLogObserver* DevToolsNetLogObserver::instance_ = NULL;
+
+DevToolsNetLogObserver::DevToolsNetLogObserver() {
+}
+
+DevToolsNetLogObserver::~DevToolsNetLogObserver() {
+}
+
+DevToolsNetLogObserver::ResourceInfo*
+DevToolsNetLogObserver::GetResourceInfo(uint32 id) {
+ RequestToInfoMap::iterator it = request_to_info_.find(id);
+ if (it != request_to_info_.end())
+ return it->second;
+ return NULL;
+}
+
+void DevToolsNetLogObserver::OnAddEntry(const net::NetLog::Entry& entry) {
+ // The events that the Observer is interested in only occur on the IO thread.
+ if (!BrowserThread::CurrentlyOn(BrowserThread::IO))
+ return;
+
+ if (entry.source().type == net::NetLog::SOURCE_URL_REQUEST)
+ OnAddURLRequestEntry(entry);
+ else if (entry.source().type == net::NetLog::SOURCE_HTTP_STREAM_JOB)
+ OnAddHTTPStreamJobEntry(entry);
+ else if (entry.source().type == net::NetLog::SOURCE_SOCKET)
+ OnAddSocketEntry(entry);
+}
+
+void DevToolsNetLogObserver::OnAddURLRequestEntry(
+ const net::NetLog::Entry& entry) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ bool is_begin = entry.phase() == net::NetLog::PHASE_BEGIN;
+ bool is_end = entry.phase() == net::NetLog::PHASE_END;
+
+ if (entry.type() == net::NetLog::TYPE_URL_REQUEST_START_JOB) {
+ if (is_begin) {
+ int load_flags;
+ scoped_ptr<Value> event_param(entry.ParametersToValue());
+ if (!net::StartEventLoadFlagsFromEventParams(event_param.get(),
+ &load_flags)) {
+ return;
+ }
+
+ if (!(load_flags & net::LOAD_REPORT_RAW_HEADERS))
+ return;
+
+ if (request_to_info_.size() > kMaxNumEntries) {
+ LOG(WARNING) << "The raw headers observer url request count has grown "
+ "larger than expected, resetting";
+ request_to_info_.clear();
+ }
+
+ request_to_info_[entry.source().id] = new ResourceInfo();
+
+ if (request_to_encoded_data_length_.size() > kMaxNumEntries) {
+ LOG(WARNING) << "The encoded data length observer url request count "
+ "has grown larger than expected, resetting";
+ request_to_encoded_data_length_.clear();
+ }
+
+ request_to_encoded_data_length_[entry.source().id] = 0;
+ }
+ return;
+ } else if (entry.type() == net::NetLog::TYPE_REQUEST_ALIVE) {
+ // Cleanup records based on the TYPE_REQUEST_ALIVE entry.
+ if (is_end) {
+ request_to_info_.erase(entry.source().id);
+ request_to_encoded_data_length_.erase(entry.source().id);
+ }
+ return;
+ }
+
+ ResourceInfo* info = GetResourceInfo(entry.source().id);
+ if (!info)
+ return;
+
+ switch (entry.type()) {
+ case net::NetLog::TYPE_HTTP_TRANSACTION_SEND_REQUEST_HEADERS: {
+ scoped_ptr<Value> event_params(entry.ParametersToValue());
+ std::string request_line;
+ net::HttpRequestHeaders request_headers;
+
+ if (!net::HttpRequestHeaders::FromNetLogParam(event_params.get(),
+ &request_headers,
+ &request_line)) {
+ NOTREACHED();
+ }
+
+ // We need to clear headers in case the same url_request is reused for
+ // several http requests (e.g. see http://crbug.com/80157).
+ info->request_headers.clear();
+
+ for (net::HttpRequestHeaders::Iterator it(request_headers);
+ it.GetNext();) {
+ info->request_headers.push_back(std::make_pair(it.name(), it.value()));
+ }
+ info->request_headers_text = request_line + request_headers.ToString();
+ break;
+ }
+ case net::NetLog::TYPE_HTTP_TRANSACTION_SPDY_SEND_REQUEST_HEADERS: {
+ scoped_ptr<Value> event_params(entry.ParametersToValue());
+ net::SpdyHeaderBlock request_headers;
+
+ if (!net::SpdyHeaderBlockFromNetLogParam(event_params.get(),
+ &request_headers)) {
+ NOTREACHED();
+ }
+
+ // We need to clear headers in case the same url_request is reused for
+ // several http requests (e.g. see http://crbug.com/80157).
+ info->request_headers.clear();
+
+ for (net::SpdyHeaderBlock::const_iterator it = request_headers.begin();
+ it != request_headers.end(); ++it) {
+ info->request_headers.push_back(std::make_pair(it->first, it->second));
+ }
+ info->request_headers_text = "";
+ break;
+ }
+ case net::NetLog::TYPE_HTTP_TRANSACTION_READ_RESPONSE_HEADERS: {
+ scoped_ptr<Value> event_params(entry.ParametersToValue());
+
+ scoped_refptr<net::HttpResponseHeaders> response_headers;
+
+ if (!net::HttpResponseHeaders::FromNetLogParam(event_params.get(),
+ &response_headers)) {
+ NOTREACHED();
+ }
+
+ info->http_status_code = response_headers->response_code();
+ info->http_status_text = response_headers->GetStatusText();
+ std::string name, value;
+
+ // We need to clear headers in case the same url_request is reused for
+ // several http requests (e.g. see http://crbug.com/80157).
+ info->response_headers.clear();
+
+ for (void* it = NULL;
+ response_headers->EnumerateHeaderLines(&it, &name, &value); ) {
+ info->response_headers.push_back(std::make_pair(name, value));
+ }
+ info->response_headers_text =
+ net::HttpUtil::ConvertHeadersBackToHTTPResponse(
+ response_headers->raw_headers());
+ break;
+ }
+ case net::NetLog::TYPE_HTTP_STREAM_REQUEST_BOUND_TO_JOB: {
+ scoped_ptr<Value> event_params(entry.ParametersToValue());
+ net::NetLog::Source http_stream_job_source;
+ if (!net::NetLog::Source::FromEventParameters(event_params.get(),
+ &http_stream_job_source)) {
+ NOTREACHED();
+ break;
+ }
+
+ uint32 http_stream_job_id = http_stream_job_source.id;
+ HTTPStreamJobToSocketMap::iterator it =
+ http_stream_job_to_socket_.find(http_stream_job_id);
+ if (it == http_stream_job_to_socket_.end())
+ return;
+ uint32 socket_id = it->second;
+
+ if (socket_to_request_.size() > kMaxNumEntries) {
+ LOG(WARNING) << "The url request observer socket count has grown "
+ "larger than expected, resetting";
+ socket_to_request_.clear();
+ }
+
+ socket_to_request_[socket_id] = entry.source().id;
+ http_stream_job_to_socket_.erase(http_stream_job_id);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void DevToolsNetLogObserver::OnAddHTTPStreamJobEntry(
+ const net::NetLog::Entry& entry) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ if (entry.type() == net::NetLog::TYPE_SOCKET_POOL_BOUND_TO_SOCKET) {
+ scoped_ptr<Value> event_params(entry.ParametersToValue());
+ net::NetLog::Source socket_source;
+ if (!net::NetLog::Source::FromEventParameters(event_params.get(),
+ &socket_source)) {
+ NOTREACHED();
+ return;
+ }
+
+ // Prevents us from passively growing the memory unbounded in
+ // case something went wrong. Should not happen.
+ if (http_stream_job_to_socket_.size() > kMaxNumEntries) {
+ LOG(WARNING) << "The load timing observer http stream job count "
+ "has grown larger than expected, resetting";
+ http_stream_job_to_socket_.clear();
+ }
+ http_stream_job_to_socket_[entry.source().id] = socket_source.id;
+ }
+}
+
+void DevToolsNetLogObserver::OnAddSocketEntry(
+ const net::NetLog::Entry& entry) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ bool is_end = entry.phase() == net::NetLog::PHASE_END;
+
+ SocketToRequestMap::iterator it =
+ socket_to_request_.find(entry.source().id);
+ if (it == socket_to_request_.end())
+ return;
+ uint32 request_id = it->second;
+
+ if (entry.type() == net::NetLog::TYPE_SOCKET_IN_USE) {
+ if (is_end)
+ socket_to_request_.erase(entry.source().id);
+ return;
+ }
+
+ RequestToEncodedDataLengthMap::iterator encoded_data_length_it =
+ request_to_encoded_data_length_.find(request_id);
+ if (encoded_data_length_it == request_to_encoded_data_length_.end())
+ return;
+
+ if (net::NetLog::TYPE_SOCKET_BYTES_RECEIVED == entry.type()) {
+ int byte_count = 0;
+ scoped_ptr<Value> value(entry.ParametersToValue());
+ if (!value->IsType(Value::TYPE_DICTIONARY))
+ return;
+
+ DictionaryValue* dValue = static_cast<DictionaryValue*>(value.get());
+ if (!dValue->GetInteger("byte_count", &byte_count))
+ return;
+
+ encoded_data_length_it->second += byte_count;
+ }
+}
+
+void DevToolsNetLogObserver::Attach() {
+ DCHECK(!instance_);
+ net::NetLog* net_log = GetContentClient()->browser()->GetNetLog();
+ if (net_log) {
+ instance_ = new DevToolsNetLogObserver();
+ net_log->AddThreadSafeObserver(instance_, net::NetLog::LOG_ALL_BUT_BYTES);
+ }
+}
+
+void DevToolsNetLogObserver::Detach() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ if (instance_) {
+ // Safest not to do this in the destructor to maintain thread safety across
+ // refactorings.
+ instance_->net_log()->RemoveThreadSafeObserver(instance_);
+ delete instance_;
+ instance_ = NULL;
+ }
+}
+
+DevToolsNetLogObserver* DevToolsNetLogObserver::GetInstance() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ return instance_;
+}
+
+// static
+void DevToolsNetLogObserver::PopulateResponseInfo(
+ net::URLRequest* request,
+ ResourceResponse* response) {
+ if (!(request->load_flags() & net::LOAD_REPORT_RAW_HEADERS))
+ return;
+
+ uint32 source_id = request->net_log().source().id;
+ DevToolsNetLogObserver* dev_tools_net_log_observer =
+ DevToolsNetLogObserver::GetInstance();
+ if (dev_tools_net_log_observer == NULL)
+ return;
+ response->head.devtools_info =
+ dev_tools_net_log_observer->GetResourceInfo(source_id);
+}
+
+// static
+int DevToolsNetLogObserver::GetAndResetEncodedDataLength(
+ net::URLRequest* request) {
+ if (!(request->load_flags() & net::LOAD_REPORT_RAW_HEADERS))
+ return -1;
+
+ uint32 source_id = request->net_log().source().id;
+ DevToolsNetLogObserver* dev_tools_net_log_observer =
+ DevToolsNetLogObserver::GetInstance();
+ if (dev_tools_net_log_observer == NULL)
+ return -1;
+
+ RequestToEncodedDataLengthMap::iterator it =
+ dev_tools_net_log_observer->request_to_encoded_data_length_.find(
+ source_id);
+ if (it == dev_tools_net_log_observer->request_to_encoded_data_length_.end())
+ return -1;
+ int encoded_data_length = it->second;
+ it->second = 0;
+ return encoded_data_length;
+}
+
+} // namespace content