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 "chrome/browser/android/intercept_download_resource_throttle.h" | |
6 | |
7 #include "base/feature_list.h" | |
8 #include "base/metrics/histogram_macros.h" | |
9 #include "chrome/browser/android/chrome_feature_list.h" | |
10 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_heade
rs.h" | |
11 #include "content/public/browser/browser_thread.h" | |
12 #include "content/public/browser/resource_controller.h" | |
13 #include "net/http/http_request_headers.h" | |
14 #include "net/http/http_response_headers.h" | |
15 #include "net/url_request/url_request.h" | |
16 #include "net/url_request/url_request_context.h" | |
17 | |
18 using content::BrowserThread; | |
19 | |
20 namespace { | |
21 | |
22 // UMA histogram for tracking reasons that chrome fails to intercept the | |
23 // download. Keep this in sync with MobileDownloadInterceptFailureReasons in | |
24 // histograms.xml. | |
25 enum MobileDownloadInterceptFailureReason { | |
26 NO_FAILURE = 0, | |
27 EMPTY_URL, | |
28 NON_HTTP_OR_HTTPS, | |
29 NON_GET_METHODS, | |
30 NO_REQUEST_HEADERS, | |
31 USE_HTTP_AUTH, | |
32 USE_CHANNEL_BOUND_COOKIES, | |
33 // FAILURE_REASON_SIZE should always be last - this is a count of the number | |
34 // of items in this enum. | |
35 FAILURE_REASON_SIZE, | |
36 }; | |
37 | |
38 void RecordInterceptFailureReasons( | |
39 MobileDownloadInterceptFailureReason reason) { | |
40 UMA_HISTOGRAM_ENUMERATION("MobileDownload.InterceptFailureReason", | |
41 reason, | |
42 FAILURE_REASON_SIZE); | |
43 } | |
44 | |
45 } // namespace | |
46 | |
47 namespace chrome { | |
48 | |
49 // static | |
50 bool InterceptDownloadResourceThrottle::IsDownloadInterceptionEnabled() { | |
51 return base::FeatureList::IsEnabled(chrome::android::kSystemDownloadManager); | |
52 } | |
53 | |
54 InterceptDownloadResourceThrottle::InterceptDownloadResourceThrottle( | |
55 net::URLRequest* request, | |
56 const content::ResourceRequestInfo::WebContentsGetter& wc_getter, | |
57 bool must_download) | |
58 : request_(request), | |
59 wc_getter_(wc_getter), | |
60 must_download_(must_download), | |
61 weak_factory_(this) { | |
62 } | |
63 | |
64 InterceptDownloadResourceThrottle::~InterceptDownloadResourceThrottle() { | |
65 } | |
66 | |
67 void InterceptDownloadResourceThrottle::WillProcessResponse(bool* defer) { | |
68 ProcessDownloadRequest(defer); | |
69 } | |
70 | |
71 const char* InterceptDownloadResourceThrottle::GetNameForLogging() const { | |
72 return "InterceptDownloadResourceThrottle"; | |
73 } | |
74 | |
75 void InterceptDownloadResourceThrottle::ProcessDownloadRequest(bool* defer) { | |
76 if (!IsDownloadInterceptionEnabled()) | |
77 return; | |
78 | |
79 if (request_->url_chain().empty()) { | |
80 RecordInterceptFailureReasons(EMPTY_URL); | |
81 return; | |
82 } | |
83 | |
84 GURL url = request_->url_chain().back(); | |
85 if (!url.SchemeIsHTTPOrHTTPS()) { | |
86 RecordInterceptFailureReasons(NON_HTTP_OR_HTTPS); | |
87 return; | |
88 } | |
89 | |
90 if (request_->method() != net::HttpRequestHeaders::kGetMethod) { | |
91 RecordInterceptFailureReasons(NON_GET_METHODS); | |
92 return; | |
93 } | |
94 | |
95 net::HttpRequestHeaders headers; | |
96 if (!request_->GetFullRequestHeaders(&headers)) { | |
97 RecordInterceptFailureReasons(NO_REQUEST_HEADERS); | |
98 return; | |
99 } | |
100 | |
101 // In general, if the request uses HTTP authorization, either with the origin | |
102 // or a proxy, then the network stack should handle the download. The one | |
103 // exception is a request that is fetched via the Chrome Proxy and does not | |
104 // authenticate with the origin. | |
105 if (request_->response_info().did_use_http_auth) { | |
106 if (headers.HasHeader(net::HttpRequestHeaders::kAuthorization) || | |
107 !(request_->response_info().headers.get() && | |
108 data_reduction_proxy::HasDataReductionProxyViaHeader( | |
109 request_->response_info().headers.get(), NULL))) { | |
110 RecordInterceptFailureReasons(USE_HTTP_AUTH); | |
111 return; | |
112 } | |
113 } | |
114 | |
115 // If the cookie is possibly channel-bound, don't pass it to android download | |
116 // manager. | |
117 // TODO(qinmin): add a test for this. http://crbug.com/430541. | |
118 if (request_->ssl_info().channel_id_sent) { | |
119 RecordInterceptFailureReasons(USE_CHANNEL_BOUND_COOKIES); | |
120 return; | |
121 } | |
122 | |
123 net::CookieStore* cookie_store = request_->context()->cookie_store(); | |
124 if (cookie_store) { | |
125 // Cookie is obtained via asynchonous call. Setting |*defer| to true | |
126 // keeps the throttle alive in the meantime. | |
127 *defer = true; | |
128 net::CookieOptions options; | |
129 options.set_include_httponly(); | |
130 cookie_store->GetCookieListWithOptionsAsync( | |
131 request_->url(), | |
132 options, | |
133 base::Bind(&InterceptDownloadResourceThrottle::CheckCookiePolicy, | |
134 weak_factory_.GetWeakPtr())); | |
135 } else { | |
136 // Can't get any cookies, start android download. | |
137 StartDownload(DownloadInfo(request_)); | |
138 } | |
139 } | |
140 | |
141 void InterceptDownloadResourceThrottle::CheckCookiePolicy( | |
142 const net::CookieList& cookie_list) { | |
143 DownloadInfo info(request_); | |
144 if (request_->context()->network_delegate()->CanGetCookies(*request_, | |
145 cookie_list)) { | |
146 std::string cookie = net::CookieStore::BuildCookieLine(cookie_list); | |
147 if (!cookie.empty()) { | |
148 info.cookie = cookie; | |
149 } | |
150 } | |
151 StartDownload(info); | |
152 } | |
153 | |
154 void InterceptDownloadResourceThrottle::StartDownload( | |
155 const DownloadInfo& info) { | |
156 DownloadControllerBase::Get()->CreateGETDownload( | |
157 wc_getter_, must_download_, info); | |
158 controller()->Cancel(); | |
159 RecordInterceptFailureReasons(NO_FAILURE); | |
160 } | |
161 | |
162 } // namespace chrome | |
OLD | NEW |