| // Copyright (c) 2011 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 "chrome/browser/debugger/devtools_netlog_observer.h" |
| |
| #include "base/string_util.h" |
| #include "base/values.h" |
| #include "chrome/browser/io_thread.h" |
| #include "content/common/resource_response.h" |
| #include "net/base/load_flags.h" |
| #include "net/http/http_net_log_params.h" |
| #include "net/http/http_response_headers.h" |
| #include "net/url_request/url_request.h" |
| #include "net/url_request/url_request_netlog_params.h" |
| #include "webkit/glue/resource_loader_bridge.h" |
| |
| const size_t kMaxNumEntries = 1000; |
| |
| DevToolsNetLogObserver* DevToolsNetLogObserver::instance_ = NULL; |
| |
| DevToolsNetLogObserver::DevToolsNetLogObserver(ChromeNetLog* chrome_net_log) |
| : ChromeNetLog::ThreadSafeObserver(net::NetLog::LOG_ALL_BUT_BYTES), |
| chrome_net_log_(chrome_net_log) { |
| chrome_net_log_->AddObserver(this); |
| } |
| |
| DevToolsNetLogObserver::~DevToolsNetLogObserver() { |
| chrome_net_log_->RemoveObserver(this); |
| } |
| |
| 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(net::NetLog::EventType type, |
| const base::TimeTicks& time, |
| const net::NetLog::Source& source, |
| net::NetLog::EventPhase phase, |
| net::NetLog::EventParameters* params) { |
| // The events that the Observer is interested in only occur on the IO thread. |
| if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) |
| return; |
| |
| // The events that the Observer is interested in only occur on the IO thread. |
| if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) |
| return; |
| if (source.type == net::NetLog::SOURCE_URL_REQUEST) |
| OnAddURLRequestEntry(type, time, source, phase, params); |
| else if (source.type == net::NetLog::SOURCE_HTTP_STREAM_JOB) |
| OnAddHTTPStreamJobEntry(type, time, source, phase, params); |
| else if (source.type == net::NetLog::SOURCE_SOCKET) |
| OnAddSocketEntry(type, time, source, phase, params); |
| } |
| |
| void DevToolsNetLogObserver::OnAddURLRequestEntry( |
| net::NetLog::EventType type, |
| const base::TimeTicks& time, |
| const net::NetLog::Source& source, |
| net::NetLog::EventPhase phase, |
| net::NetLog::EventParameters* params) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| |
| bool is_begin = phase == net::NetLog::PHASE_BEGIN; |
| bool is_end = phase == net::NetLog::PHASE_END; |
| |
| if (type == net::NetLog::TYPE_URL_REQUEST_START_JOB) { |
| if (is_begin) { |
| int load_flags = static_cast< |
| net::URLRequestStartEventParameters*>(params)->load_flags(); |
| 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_[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_[source.id] = 0; |
| } |
| return; |
| } else if (type == net::NetLog::TYPE_REQUEST_ALIVE) { |
| // Cleanup records based on the TYPE_REQUEST_ALIVE entry. |
| if (is_end) { |
| request_to_info_.erase(source.id); |
| request_to_encoded_data_length_.erase(source.id); |
| } |
| return; |
| } |
| |
| ResourceInfo* info = GetResourceInfo(source.id); |
| if (!info) |
| return; |
| |
| switch (type) { |
| case net::NetLog::TYPE_HTTP_TRANSACTION_SEND_REQUEST_HEADERS: { |
| const net::HttpRequestHeaders &request_headers = |
| static_cast<net::NetLogHttpRequestParameter*>(params)->GetHeaders(); |
| for (net::HttpRequestHeaders::Iterator it(request_headers); |
| it.GetNext();) { |
| info->request_headers.push_back(std::make_pair(it.name(), |
| it.value())); |
| } |
| break; |
| } |
| case net::NetLog::TYPE_HTTP_TRANSACTION_READ_RESPONSE_HEADERS: { |
| const net::HttpResponseHeaders& response_headers = |
| static_cast<net::NetLogHttpResponseParameter*>(params)->GetHeaders(); |
| info->http_status_code = response_headers.response_code(); |
| info->http_status_text = response_headers.GetStatusText(); |
| std::string name, value; |
| for (void* it = NULL; |
| response_headers.EnumerateHeaderLines(&it, &name, &value); ) { |
| info->response_headers.push_back(std::make_pair(name, value)); |
| } |
| break; |
| } |
| case net::NetLog::TYPE_HTTP_STREAM_REQUEST_BOUND_TO_JOB: { |
| uint32 http_stream_job_id = static_cast<net::NetLogSourceParameter*>( |
| params)->value().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] = source.id; |
| http_stream_job_to_socket_.erase(http_stream_job_id); |
| break; |
| } |
| default: |
| break; |
| } |
| } |
| |
| void DevToolsNetLogObserver::OnAddHTTPStreamJobEntry( |
| net::NetLog::EventType type, |
| const base::TimeTicks& time, |
| const net::NetLog::Source& source, |
| net::NetLog::EventPhase phase, |
| net::NetLog::EventParameters* params) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| |
| if (type == net::NetLog::TYPE_SOCKET_POOL_BOUND_TO_SOCKET) { |
| uint32 socket_id = static_cast<net::NetLogSourceParameter*>( |
| params)->value().id; |
| |
| // 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_[source.id] = socket_id; |
| } |
| } |
| |
| void DevToolsNetLogObserver::OnAddSocketEntry( |
| net::NetLog::EventType type, |
| const base::TimeTicks& time, |
| const net::NetLog::Source& source, |
| net::NetLog::EventPhase phase, |
| net::NetLog::EventParameters* params) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| |
| bool is_end = phase == net::NetLog::PHASE_END; |
| |
| SocketToRequestMap::iterator it = socket_to_request_.find(source.id); |
| if (it == socket_to_request_.end()) |
| return; |
| uint32 request_id = it->second; |
| |
| if (type == net::NetLog::TYPE_SOCKET_IN_USE) { |
| if (is_end) |
| socket_to_request_.erase(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 == type) { |
| int byte_count = 0; |
| Value* value = params->ToValue(); |
| if (!value->IsType(Value::TYPE_DICTIONARY)) |
| return; |
| |
| DictionaryValue* dValue = static_cast<DictionaryValue*>(value); |
| if (!dValue->GetInteger("byte_count", &byte_count)) |
| return; |
| |
| encoded_data_length_it->second += byte_count; |
| } |
| } |
| |
| void DevToolsNetLogObserver::Attach(IOThread* io_thread) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| DCHECK(!instance_); |
| |
| instance_ = new DevToolsNetLogObserver(io_thread->net_log()); |
| } |
| |
| void DevToolsNetLogObserver::Detach() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| DCHECK(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->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; |
| } |