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