1 // Copyright (c) 2012 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 "content/browser/devtools/devtools_netlog_observer.h" 6 7 #include "base/strings/string_util.h" 8 #include "base/values.h" 9 #include "content/public/browser/browser_thread.h" 10 #include "content/public/browser/content_browser_client.h" 11 #include "content/public/common/resource_response.h" 12 #include "net/base/load_flags.h" 13 #include "net/http/http_response_headers.h" 14 #include "net/http/http_util.h" 15 #include "net/spdy/spdy_header_block.h" 16 #include "net/url_request/url_request.h" 17 #include "net/url_request/url_request_netlog_params.h" 18 19 namespace content { 20 const size_t kMaxNumEntries = 1000; 21 22 DevToolsNetLogObserver* DevToolsNetLogObserver::instance_ = NULL; 23 24 DevToolsNetLogObserver::DevToolsNetLogObserver() { 25 } 26 27 DevToolsNetLogObserver::~DevToolsNetLogObserver() { 28 } 29 30 DevToolsNetLogObserver::ResourceInfo* 31 DevToolsNetLogObserver::GetResourceInfo(uint32 id) { 32 RequestToInfoMap::iterator it = request_to_info_.find(id); 33 if (it != request_to_info_.end()) 34 return it->second.get(); 35 return NULL; 36 } 37 38 void DevToolsNetLogObserver::OnAddEntry(const net::NetLog::Entry& entry) { 39 // The events that the Observer is interested in only occur on the IO thread. 40 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) 41 return; 42 43 if (entry.source().type == net::NetLog::SOURCE_URL_REQUEST) 44 OnAddURLRequestEntry(entry); 45 else if (entry.source().type == net::NetLog::SOURCE_HTTP_STREAM_JOB) 46 OnAddHTTPStreamJobEntry(entry); 47 else if (entry.source().type == net::NetLog::SOURCE_SOCKET) 48 OnAddSocketEntry(entry); 49 } 50 51 void DevToolsNetLogObserver::OnAddURLRequestEntry( 52 const net::NetLog::Entry& entry) { 53 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 54 55 bool is_begin = entry.phase() == net::NetLog::PHASE_BEGIN; 56 bool is_end = entry.phase() == net::NetLog::PHASE_END; 57 58 if (entry.type() == net::NetLog::TYPE_URL_REQUEST_START_JOB) { 59 if (is_begin) { 60 int load_flags; 61 scoped_ptr<base::Value> event_param(entry.ParametersToValue()); 62 if (!net::StartEventLoadFlagsFromEventParams(event_param.get(), 63 &load_flags)) { 64 return; 65 } 66 67 if (!(load_flags & net::LOAD_REPORT_RAW_HEADERS)) 68 return; 69 70 if (request_to_info_.size() > kMaxNumEntries) { 71 LOG(WARNING) << "The raw headers observer url request count has grown " 72 "larger than expected, resetting"; 73 request_to_info_.clear(); 74 } 75 76 request_to_info_[entry.source().id] = new ResourceInfo(); 77 78 if (request_to_encoded_data_length_.size() > kMaxNumEntries) { 79 LOG(WARNING) << "The encoded data length observer url request count " 80 "has grown larger than expected, resetting"; 81 request_to_encoded_data_length_.clear(); 82 } 83 84 request_to_encoded_data_length_[entry.source().id] = 0; 85 } 86 return; 87 } else if (entry.type() == net::NetLog::TYPE_REQUEST_ALIVE) { 88 // Cleanup records based on the TYPE_REQUEST_ALIVE entry. 89 if (is_end) { 90 request_to_info_.erase(entry.source().id); 91 request_to_encoded_data_length_.erase(entry.source().id); 92 } 93 return; 94 } 95 96 ResourceInfo* info = GetResourceInfo(entry.source().id); 97 if (!info) 98 return; 99 100 switch (entry.type()) { 101 case net::NetLog::TYPE_HTTP_TRANSACTION_SEND_REQUEST_HEADERS: { 102 scoped_ptr<base::Value> event_params(entry.ParametersToValue()); 103 std::string request_line; 104 net::HttpRequestHeaders request_headers; 105 106 if (!net::HttpRequestHeaders::FromNetLogParam(event_params.get(), 107 &request_headers, 108 &request_line)) { 109 NOTREACHED(); 110 } 111 112 // We need to clear headers in case the same url_request is reused for 113 // several http requests (e.g. see http://crbug.com/80157). 114 info->request_headers.clear(); 115 116 for (net::HttpRequestHeaders::Iterator it(request_headers); 117 it.GetNext();) { 118 info->request_headers.push_back(std::make_pair(it.name(), it.value())); 119 } 120 info->request_headers_text = request_line + request_headers.ToString(); 121 break; 122 } 123 case net::NetLog::TYPE_HTTP_TRANSACTION_SPDY_SEND_REQUEST_HEADERS: { 124 scoped_ptr<base::Value> event_params(entry.ParametersToValue()); 125 net::SpdyHeaderBlock request_headers; 126 127 if (!net::SpdyHeaderBlockFromNetLogParam(event_params.get(), 128 &request_headers)) { 129 NOTREACHED(); 130 } 131 132 // We need to clear headers in case the same url_request is reused for 133 // several http requests (e.g. see http://crbug.com/80157). 134 info->request_headers.clear(); 135 136 for (net::SpdyHeaderBlock::const_iterator it = request_headers.begin(); 137 it != request_headers.end(); ++it) { 138 info->request_headers.push_back(std::make_pair(it->first, it->second)); 139 } 140 info->request_headers_text = ""; 141 break; 142 } 143 case net::NetLog::TYPE_HTTP_TRANSACTION_READ_RESPONSE_HEADERS: { 144 scoped_ptr<base::Value> event_params(entry.ParametersToValue()); 145 146 scoped_refptr<net::HttpResponseHeaders> response_headers; 147 148 if (!net::HttpResponseHeaders::FromNetLogParam(event_params.get(), 149 &response_headers)) { 150 NOTREACHED(); 151 } 152 153 info->http_status_code = response_headers->response_code(); 154 info->http_status_text = response_headers->GetStatusText(); 155 std::string name, value; 156 157 // We need to clear headers in case the same url_request is reused for 158 // several http requests (e.g. see http://crbug.com/80157). 159 info->response_headers.clear(); 160 161 for (void* it = NULL; 162 response_headers->EnumerateHeaderLines(&it, &name, &value); ) { 163 info->response_headers.push_back(std::make_pair(name, value)); 164 } 165 info->response_headers_text = 166 net::HttpUtil::ConvertHeadersBackToHTTPResponse( 167 response_headers->raw_headers()); 168 break; 169 } 170 case net::NetLog::TYPE_HTTP_STREAM_REQUEST_BOUND_TO_JOB: { 171 scoped_ptr<base::Value> event_params(entry.ParametersToValue()); 172 net::NetLog::Source http_stream_job_source; 173 if (!net::NetLog::Source::FromEventParameters(event_params.get(), 174 &http_stream_job_source)) { 175 NOTREACHED(); 176 break; 177 } 178 179 uint32 http_stream_job_id = http_stream_job_source.id; 180 HTTPStreamJobToSocketMap::iterator it = 181 http_stream_job_to_socket_.find(http_stream_job_id); 182 if (it == http_stream_job_to_socket_.end()) 183 return; 184 uint32 socket_id = it->second; 185 186 if (socket_to_request_.size() > kMaxNumEntries) { 187 LOG(WARNING) << "The url request observer socket count has grown " 188 "larger than expected, resetting"; 189 socket_to_request_.clear(); 190 } 191 192 socket_to_request_[socket_id] = entry.source().id; 193 http_stream_job_to_socket_.erase(http_stream_job_id); 194 break; 195 } 196 default: 197 break; 198 } 199 } 200 201 void DevToolsNetLogObserver::OnAddHTTPStreamJobEntry( 202 const net::NetLog::Entry& entry) { 203 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 204 205 if (entry.type() == net::NetLog::TYPE_SOCKET_POOL_BOUND_TO_SOCKET) { 206 scoped_ptr<base::Value> event_params(entry.ParametersToValue()); 207 net::NetLog::Source socket_source; 208 if (!net::NetLog::Source::FromEventParameters(event_params.get(), 209 &socket_source)) { 210 NOTREACHED(); 211 return; 212 } 213 214 // Prevents us from passively growing the memory unbounded in 215 // case something went wrong. Should not happen. 216 if (http_stream_job_to_socket_.size() > kMaxNumEntries) { 217 LOG(WARNING) << "The load timing observer http stream job count " 218 "has grown larger than expected, resetting"; 219 http_stream_job_to_socket_.clear(); 220 } 221 http_stream_job_to_socket_[entry.source().id] = socket_source.id; 222 } 223 } 224 225 void DevToolsNetLogObserver::OnAddSocketEntry( 226 const net::NetLog::Entry& entry) { 227 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 228 229 bool is_end = entry.phase() == net::NetLog::PHASE_END; 230 231 SocketToRequestMap::iterator it = 232 socket_to_request_.find(entry.source().id); 233 if (it == socket_to_request_.end()) 234 return; 235 uint32 request_id = it->second; 236 237 if (entry.type() == net::NetLog::TYPE_SOCKET_IN_USE) { 238 if (is_end) 239 socket_to_request_.erase(entry.source().id); 240 return; 241 } 242 243 RequestToEncodedDataLengthMap::iterator encoded_data_length_it = 244 request_to_encoded_data_length_.find(request_id); 245 if (encoded_data_length_it == request_to_encoded_data_length_.end()) 246 return; 247 248 if (net::NetLog::TYPE_SOCKET_BYTES_RECEIVED == entry.type()) { 249 int byte_count = 0; 250 scoped_ptr<base::Value> value(entry.ParametersToValue()); 251 if (!value->IsType(base::Value::TYPE_DICTIONARY)) 252 return; 253 254 base::DictionaryValue* dValue = 255 static_cast<base::DictionaryValue*>(value.get()); 256 if (!dValue->GetInteger("byte_count", &byte_count)) 257 return; 258 259 encoded_data_length_it->second += byte_count; 260 } 261 } 262 263 void DevToolsNetLogObserver::Attach() { 264 DCHECK(!instance_); 265 net::NetLog* net_log = GetContentClient()->browser()->GetNetLog(); 266 if (net_log) { 267 instance_ = new DevToolsNetLogObserver(); 268 net_log->AddThreadSafeObserver(instance_, net::NetLog::LOG_ALL_BUT_BYTES); 269 } 270 } 271 272 void DevToolsNetLogObserver::Detach() { 273 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 274 275 if (instance_) { 276 // Safest not to do this in the destructor to maintain thread safety across 277 // refactorings. 278 instance_->net_log()->RemoveThreadSafeObserver(instance_); 279 delete instance_; 280 instance_ = NULL; 281 } 282 } 283 284 DevToolsNetLogObserver* DevToolsNetLogObserver::GetInstance() { 285 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 286 287 return instance_; 288 } 289 290 // static 291 void DevToolsNetLogObserver::PopulateResponseInfo( 292 net::URLRequest* request, 293 ResourceResponse* response) { 294 if (!(request->load_flags() & net::LOAD_REPORT_RAW_HEADERS)) 295 return; 296 297 uint32 source_id = request->net_log().source().id; 298 DevToolsNetLogObserver* dev_tools_net_log_observer = 299 DevToolsNetLogObserver::GetInstance(); 300 if (dev_tools_net_log_observer == NULL) 301 return; 302 response->head.devtools_info = 303 dev_tools_net_log_observer->GetResourceInfo(source_id); 304 } 305 306 // static 307 int DevToolsNetLogObserver::GetAndResetEncodedDataLength( 308 net::URLRequest* request) { 309 if (!(request->load_flags() & net::LOAD_REPORT_RAW_HEADERS)) 310 return -1; 311 312 uint32 source_id = request->net_log().source().id; 313 DevToolsNetLogObserver* dev_tools_net_log_observer = 314 DevToolsNetLogObserver::GetInstance(); 315 if (dev_tools_net_log_observer == NULL) 316 return -1; 317 318 RequestToEncodedDataLengthMap::iterator it = 319 dev_tools_net_log_observer->request_to_encoded_data_length_.find( 320 source_id); 321 if (it == dev_tools_net_log_observer->request_to_encoded_data_length_.end()) 322 return -1; 323 int encoded_data_length = it->second; 324 it->second = 0; 325 return encoded_data_length; 326 } 327 328 } // namespace content 329