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 } 46 47 void DevToolsNetLogObserver::OnAddURLRequestEntry( 48 const net::NetLog::Entry& entry) { 49 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 50 51 bool is_begin = entry.phase() == net::NetLog::PHASE_BEGIN; 52 bool is_end = entry.phase() == net::NetLog::PHASE_END; 53 54 if (entry.type() == net::NetLog::TYPE_URL_REQUEST_START_JOB) { 55 if (is_begin) { 56 int load_flags; 57 scoped_ptr<base::Value> event_param(entry.ParametersToValue()); 58 if (!net::StartEventLoadFlagsFromEventParams(event_param.get(), 59 &load_flags)) { 60 return; 61 } 62 63 if (!(load_flags & net::LOAD_REPORT_RAW_HEADERS)) 64 return; 65 66 if (request_to_info_.size() > kMaxNumEntries) { 67 LOG(WARNING) << "The raw headers observer url request count has grown " 68 "larger than expected, resetting"; 69 request_to_info_.clear(); 70 } 71 72 request_to_info_[entry.source().id] = new ResourceInfo(); 73 } 74 return; 75 } else if (entry.type() == net::NetLog::TYPE_REQUEST_ALIVE) { 76 // Cleanup records based on the TYPE_REQUEST_ALIVE entry. 77 if (is_end) 78 request_to_info_.erase(entry.source().id); 79 return; 80 } 81 82 ResourceInfo* info = GetResourceInfo(entry.source().id); 83 if (!info) 84 return; 85 86 switch (entry.type()) { 87 case net::NetLog::TYPE_HTTP_TRANSACTION_SEND_REQUEST_HEADERS: { 88 scoped_ptr<base::Value> event_params(entry.ParametersToValue()); 89 std::string request_line; 90 net::HttpRequestHeaders request_headers; 91 92 if (!net::HttpRequestHeaders::FromNetLogParam(event_params.get(), 93 &request_headers, 94 &request_line)) { 95 NOTREACHED(); 96 } 97 98 // We need to clear headers in case the same url_request is reused for 99 // several http requests (e.g. see http://crbug.com/80157). 100 info->request_headers.clear(); 101 102 for (net::HttpRequestHeaders::Iterator it(request_headers); 103 it.GetNext();) { 104 info->request_headers.push_back(std::make_pair(it.name(), it.value())); 105 } 106 info->request_headers_text = request_line + request_headers.ToString(); 107 break; 108 } 109 case net::NetLog::TYPE_HTTP_TRANSACTION_SPDY_SEND_REQUEST_HEADERS: { 110 scoped_ptr<base::Value> event_params(entry.ParametersToValue()); 111 net::SpdyHeaderBlock request_headers; 112 113 if (!net::SpdyHeaderBlockFromNetLogParam(event_params.get(), 114 &request_headers)) { 115 NOTREACHED(); 116 } 117 118 // We need to clear headers in case the same url_request is reused for 119 // several http requests (e.g. see http://crbug.com/80157). 120 info->request_headers.clear(); 121 122 for (net::SpdyHeaderBlock::const_iterator it = request_headers.begin(); 123 it != request_headers.end(); ++it) { 124 info->request_headers.push_back(std::make_pair(it->first, it->second)); 125 } 126 info->request_headers_text = ""; 127 break; 128 } 129 case net::NetLog::TYPE_HTTP_TRANSACTION_READ_RESPONSE_HEADERS: { 130 scoped_ptr<base::Value> event_params(entry.ParametersToValue()); 131 132 scoped_refptr<net::HttpResponseHeaders> response_headers; 133 134 if (!net::HttpResponseHeaders::FromNetLogParam(event_params.get(), 135 &response_headers)) { 136 NOTREACHED(); 137 } 138 139 info->http_status_code = response_headers->response_code(); 140 info->http_status_text = response_headers->GetStatusText(); 141 std::string name, value; 142 143 // We need to clear headers in case the same url_request is reused for 144 // several http requests (e.g. see http://crbug.com/80157). 145 info->response_headers.clear(); 146 147 for (void* it = NULL; 148 response_headers->EnumerateHeaderLines(&it, &name, &value); ) { 149 info->response_headers.push_back(std::make_pair(name, value)); 150 } 151 152 if (!info->request_headers_text.empty()) { 153 info->response_headers_text = 154 net::HttpUtil::ConvertHeadersBackToHTTPResponse( 155 response_headers->raw_headers()); 156 } else { 157 // SPDY request. 158 info->response_headers_text = ""; 159 } 160 break; 161 } 162 default: 163 break; 164 } 165 } 166 167 void DevToolsNetLogObserver::Attach() { 168 DCHECK(!instance_); 169 net::NetLog* net_log = GetContentClient()->browser()->GetNetLog(); 170 if (net_log) { 171 instance_ = new DevToolsNetLogObserver(); 172 net_log->AddThreadSafeObserver(instance_, net::NetLog::LOG_ALL_BUT_BYTES); 173 } 174 } 175 176 void DevToolsNetLogObserver::Detach() { 177 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 178 179 if (instance_) { 180 // Safest not to do this in the destructor to maintain thread safety across 181 // refactorings. 182 instance_->net_log()->RemoveThreadSafeObserver(instance_); 183 delete instance_; 184 instance_ = NULL; 185 } 186 } 187 188 DevToolsNetLogObserver* DevToolsNetLogObserver::GetInstance() { 189 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 190 191 return instance_; 192 } 193 194 // static 195 void DevToolsNetLogObserver::PopulateResponseInfo( 196 net::URLRequest* request, 197 ResourceResponse* response) { 198 if (!(request->load_flags() & net::LOAD_REPORT_RAW_HEADERS)) 199 return; 200 201 uint32 source_id = request->net_log().source().id; 202 DevToolsNetLogObserver* dev_tools_net_log_observer = 203 DevToolsNetLogObserver::GetInstance(); 204 if (dev_tools_net_log_observer == NULL) 205 return; 206 response->head.devtools_info = 207 dev_tools_net_log_observer->GetResourceInfo(source_id); 208 } 209 210 } // namespace content 211