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