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