• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
DevToolsNetLogObserver()24 DevToolsNetLogObserver::DevToolsNetLogObserver() {
25 }
26 
~DevToolsNetLogObserver()27 DevToolsNetLogObserver::~DevToolsNetLogObserver() {
28 }
29 
30 DevToolsNetLogObserver::ResourceInfo*
GetResourceInfo(uint32 id)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 
OnAddEntry(const net::NetLog::Entry & entry)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 
OnAddURLRequestEntry(const net::NetLog::Entry & entry)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 
Attach()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 
Detach()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 
GetInstance()188 DevToolsNetLogObserver* DevToolsNetLogObserver::GetInstance() {
189   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
190 
191   return instance_;
192 }
193 
194 // static
PopulateResponseInfo(net::URLRequest * request,ResourceResponse * response)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