blob: 9424bc3c2ffea9c8737281c2a551f73b41543d03 [file] [log] [blame]
// 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/loader/netlog_observer.h"
#include <stddef.h>
#include "base/strings/string_util.h"
#include "base/values.h"
#include "content/browser/loader/resource_request_info_impl.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/log/net_log_event_type.h"
#include "net/log/net_log_source_type.h"
#include "net/spdy/spdy_header_block.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_netlog_params.h"
namespace content {
const size_t kMaxNumEntries = 1000;
// static
NetLogObserver* NetLogObserver::instance_ = NULL;
// static
base::LazyInstance<std::unique_ptr<base::ThreadChecker>>::Leaky
NetLogObserver::io_thread_checker_;
NetLogObserver::NetLogObserver() {}
NetLogObserver::~NetLogObserver() {}
NetLogObserver::ResourceInfo* NetLogObserver::GetResourceInfo(uint32_t id) {
RequestToInfoMap::iterator it = request_to_info_.find(id);
if (it != request_to_info_.end())
return it->second.get();
return NULL;
}
void NetLogObserver::OnAddEntry(const net::NetLog::Entry& entry) {
DCHECK(io_thread_checker_.Get().get());
// The events that the Observer is interested in only occur on the IO thread.
if (!io_thread_checker_.Get()->CalledOnValidThread())
return;
if (entry.source().type == net::NetLogSourceType::URL_REQUEST)
OnAddURLRequestEntry(entry);
}
void NetLogObserver::OnAddURLRequestEntry(const net::NetLog::Entry& entry) {
bool is_begin = entry.phase() == net::NetLogEventPhase::BEGIN;
bool is_end = entry.phase() == net::NetLogEventPhase::END;
if (entry.type() == net::NetLogEventType::URL_REQUEST_START_JOB) {
if (is_begin) {
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();
}
return;
} else if (entry.type() == net::NetLogEventType::REQUEST_ALIVE) {
// Cleanup records based on the TYPE_REQUEST_ALIVE entry.
if (is_end)
request_to_info_.erase(entry.source().id);
return;
}
ResourceInfo* info = GetResourceInfo(entry.source().id);
if (!info)
return;
switch (entry.type()) {
case net::NetLogEventType::HTTP_TRANSACTION_SEND_REQUEST_HEADERS: {
std::unique_ptr<base::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::NetLogEventType::HTTP_TRANSACTION_HTTP2_SEND_REQUEST_HEADERS: {
std::unique_ptr<base::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.as_string(), it->second.as_string()));
}
info->request_headers_text = "";
break;
}
case net::NetLogEventType::HTTP_TRANSACTION_READ_RESPONSE_HEADERS: {
std::unique_ptr<base::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 (size_t it = 0;
response_headers->EnumerateHeaderLines(&it, &name, &value);) {
info->response_headers.push_back(std::make_pair(name, value));
}
if (!info->request_headers_text.empty()) {
info->response_headers_text =
net::HttpUtil::ConvertHeadersBackToHTTPResponse(
response_headers->raw_headers());
} else {
// SPDY request.
info->response_headers_text = "";
}
break;
}
default:
break;
}
}
void NetLogObserver::Attach(net::NetLog* net_log) {
DCHECK(!instance_);
io_thread_checker_.Get().reset(new base::ThreadChecker());
if (net_log) {
instance_ = new NetLogObserver();
net_log->DeprecatedAddObserver(
instance_, net::NetLogCaptureMode::IncludeCookiesAndCredentials());
}
}
void NetLogObserver::Detach() {
DCHECK(io_thread_checker_.Get().get() &&
io_thread_checker_.Get()->CalledOnValidThread());
io_thread_checker_.Get().reset();
if (instance_) {
// Safest not to do this in the destructor to maintain thread safety across
// refactorings.
instance_->net_log()->DeprecatedRemoveObserver(instance_);
delete instance_;
instance_ = NULL;
}
}
NetLogObserver* NetLogObserver::GetInstance() {
if (!io_thread_checker_.Get().get())
return nullptr;
DCHECK(io_thread_checker_.Get()->CalledOnValidThread());
return instance_;
}
// static
void NetLogObserver::PopulateResponseInfo(net::URLRequest* request,
ResourceResponse* response) {
const ResourceRequestInfoImpl* request_info =
ResourceRequestInfoImpl::ForRequest(request);
if (!request_info || !request_info->ShouldReportRawHeaders())
return;
uint32_t source_id = request->net_log().source().id;
NetLogObserver* dev_tools_net_log_observer = NetLogObserver::GetInstance();
if (dev_tools_net_log_observer == NULL)
return;
response->head.devtools_info =
dev_tools_net_log_observer->GetResourceInfo(source_id);
}
} // namespace content