Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(16)

Side by Side Diff: components/update_client/request_sender.cc

Issue 1740333002: Allow fallback from https to http for component update checks. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/update_client/request_sender.h" 5 #include "components/update_client/request_sender.h"
6 6
7 #include <algorithm>
8
7 #include "base/base64.h" 9 #include "base/base64.h"
8 #include "base/bind.h" 10 #include "base/bind.h"
9 #include "base/bind_helpers.h" 11 #include "base/bind_helpers.h"
10 #include "base/location.h" 12 #include "base/location.h"
11 #include "base/logging.h" 13 #include "base/logging.h"
12 #include "base/single_thread_task_runner.h" 14 #include "base/single_thread_task_runner.h"
13 #include "base/strings/stringprintf.h" 15 #include "base/strings/stringprintf.h"
14 #include "base/thread_task_runner_handle.h" 16 #include "base/thread_task_runner_handle.h"
15 #include "components/client_update_protocol/ecdsa.h" 17 #include "components/client_update_protocol/ecdsa.h"
16 #include "components/update_client/configurator.h" 18 #include "components/update_client/configurator.h"
17 #include "components/update_client/utils.h" 19 #include "components/update_client/utils.h"
18 #include "net/http/http_response_headers.h" 20 #include "net/http/http_response_headers.h"
19 #include "net/url_request/url_fetcher.h" 21 #include "net/url_request/url_fetcher.h"
22 #include "net/url_request/url_request_status.h"
20 23
21 namespace update_client { 24 namespace update_client {
22 25
23 namespace { 26 namespace {
24 27
25 // This is an ECDSA prime256v1 named-curve key. 28 // This is an ECDSA prime256v1 named-curve key.
26 const int kKeyVersion = 5; 29 const int kKeyVersion = 5;
27 const char kKeyPubBytesBase64[] = 30 const char kKeyPubBytesBase64[] =
28 "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEB+Yi+3SdJKCyFJmm+suW3CyXygvVsbDbPnJgoC" 31 "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEB+Yi+3SdJKCyFJmm+suW3CyXygvVsbDbPnJgoC"
29 "X4GeTtoL8Q/WjPx7CGtXOL1Xjbx0VPPN3DrvqZSL/oXy9hVw=="; 32 "X4GeTtoL8Q/WjPx7CGtXOL1Xjbx0VPPN3DrvqZSL/oXy9hVw==";
30 33
34 // The ETag header carries the ECSDA signature of the protocol response, if
35 // signing has been used.
36 const char kHeaderEtag[] = "ETag";
37
38 // The server uses the optional X-Retry-After header to indicate that the
39 // current request should not be attempted again. Any response received along
40 // with the X-Retry-After header should be interpreted as it would have been
41 // without the X-Retry-After header.
42 //
43 // In addition to the presence of the header, the value of the header is
44 // used as a signal for when to do future update checks, but only when the
45 // response is over https. Values over http are not trusted and are ignored.
46 //
47 // The value of the header is the number of seconds to wait before trying to do
48 // a subsequent update check. The upper bound for the number of seconds to wait
49 // before trying to do a subsequent update check is capped at 24 hours.
50 const char kHeaderXRetryAfter[] = "X-Retry-After";
51 const int64_t kMaxRetryAfterSec = 24 * 60 * 60;
52
31 } // namespace 53 } // namespace
32 54
33 // This value is chosen not to conflict with network errors defined by 55 // This value is chosen not to conflict with network errors defined by
34 // net/base/net_error_list.h. The callers don't have to handle this error in 56 // net/base/net_error_list.h. The callers don't have to handle this error in
35 // any meaningful way, but this value may be reported in UMA stats, therefore 57 // any meaningful way, but this value may be reported in UMA stats, therefore
36 // avoiding collisions with known network errors is desirable. 58 // avoiding collisions with known network errors is desirable.
37 int RequestSender::kErrorResponseNotTrusted = -10000; 59 int RequestSender::kErrorResponseNotTrusted = -10000;
38 60
39 RequestSender::RequestSender(const scoped_refptr<Configurator>& config) 61 RequestSender::RequestSender(const scoped_refptr<Configurator>& config)
40 : config_(config), use_signing_(false) {} 62 : config_(config), use_signing_(false) {}
41 63
42 RequestSender::~RequestSender() { 64 RequestSender::~RequestSender() {
43 DCHECK(thread_checker_.CalledOnValidThread()); 65 DCHECK(thread_checker_.CalledOnValidThread());
44 } 66 }
45 67
46 void RequestSender::Send(bool use_signing, 68 void RequestSender::Send(bool use_signing,
47 const std::string& request_body, 69 const std::string& request_body,
48 const std::vector<GURL>& urls, 70 const std::vector<GURL>& urls,
49 const RequestSenderCallback& request_sender_callback) { 71 const RequestSenderCallback& request_sender_callback) {
50 DCHECK(thread_checker_.CalledOnValidThread()); 72 DCHECK(thread_checker_.CalledOnValidThread());
51 73
52 use_signing_ = use_signing; 74 use_signing_ = use_signing;
53 request_body_ = request_body; 75 request_body_ = request_body;
54 urls_ = urls; 76 urls_ = urls;
55 request_sender_callback_ = request_sender_callback; 77 request_sender_callback_ = request_sender_callback;
56 78
57 if (urls_.empty()) { 79 if (urls_.empty()) {
58 return HandleSendError(-1); 80 return HandleSendError(-1, 0);
59 } 81 }
60 82
61 cur_url_ = urls_.begin(); 83 cur_url_ = urls_.begin();
62 84
63 if (use_signing_) { 85 if (use_signing_) {
64 public_key_ = GetKey(kKeyPubBytesBase64); 86 public_key_ = GetKey(kKeyPubBytesBase64);
65 if (public_key_.empty()) 87 if (public_key_.empty())
66 return HandleSendError(-1); 88 return HandleSendError(-1, 0);
67 } 89 }
68 90
69 SendInternal(); 91 SendInternal();
70 } 92 }
71 93
72 void RequestSender::SendInternal() { 94 void RequestSender::SendInternal() {
73 DCHECK(cur_url_ != urls_.end()); 95 DCHECK(cur_url_ != urls_.end());
74 DCHECK(cur_url_->is_valid()); 96 DCHECK(cur_url_->is_valid());
75 DCHECK(thread_checker_.CalledOnValidThread()); 97 DCHECK(thread_checker_.CalledOnValidThread());
76 98
77 GURL url(*cur_url_); 99 GURL url(*cur_url_);
78 100
79 if (use_signing_) { 101 if (use_signing_) {
80 DCHECK(!public_key_.empty()); 102 DCHECK(!public_key_.empty());
81 signer_ = client_update_protocol::Ecdsa::Create(kKeyVersion, public_key_); 103 signer_ = client_update_protocol::Ecdsa::Create(kKeyVersion, public_key_);
82 std::string request_query_string; 104 std::string request_query_string;
83 signer_->SignRequest(request_body_, &request_query_string); 105 signer_->SignRequest(request_body_, &request_query_string);
84 106
85 url = BuildUpdateUrl(url, request_query_string); 107 url = BuildUpdateUrl(url, request_query_string);
86 } 108 }
87 109
88 url_fetcher_ = 110 url_fetcher_ =
89 SendProtocolRequest(url, request_body_, this, config_->RequestContext()); 111 SendProtocolRequest(url, request_body_, this, config_->RequestContext());
90 if (!url_fetcher_.get()) 112 if (!url_fetcher_.get())
91 base::ThreadTaskRunnerHandle::Get()->PostTask( 113 base::ThreadTaskRunnerHandle::Get()->PostTask(
92 FROM_HERE, 114 FROM_HERE,
93 base::Bind(&RequestSender::SendInternalComplete, base::Unretained(this), 115 base::Bind(&RequestSender::SendInternalComplete, base::Unretained(this),
94 -1, std::string(), std::string())); 116 -1, std::string(), std::string(), 0));
95 } 117 }
96 118
97 void RequestSender::SendInternalComplete(int error, 119 void RequestSender::SendInternalComplete(int error,
98 const std::string& response_body, 120 const std::string& response_body,
99 const std::string& response_etag) { 121 const std::string& response_etag,
122 int retry_after_sec) {
100 if (!error) { 123 if (!error) {
101 if (!use_signing_) { 124 if (!use_signing_) {
102 base::ThreadTaskRunnerHandle::Get()->PostTask( 125 base::ThreadTaskRunnerHandle::Get()->PostTask(
103 FROM_HERE, base::Bind(request_sender_callback_, 0, response_body)); 126 FROM_HERE, base::Bind(request_sender_callback_, 0, response_body,
127 retry_after_sec));
104 return; 128 return;
105 } 129 }
106 130
107 DCHECK(use_signing_); 131 DCHECK(use_signing_);
108 DCHECK(signer_.get()); 132 DCHECK(signer_.get());
109 if (signer_->ValidateResponse(response_body, response_etag)) { 133 if (signer_->ValidateResponse(response_body, response_etag)) {
110 base::ThreadTaskRunnerHandle::Get()->PostTask( 134 base::ThreadTaskRunnerHandle::Get()->PostTask(
111 FROM_HERE, base::Bind(request_sender_callback_, 0, response_body)); 135 FROM_HERE, base::Bind(request_sender_callback_, 0, response_body,
136 retry_after_sec));
112 return; 137 return;
113 } 138 }
114 139
115 error = kErrorResponseNotTrusted; 140 error = kErrorResponseNotTrusted;
116 } 141 }
117 142
118 DCHECK(error); 143 DCHECK(error);
119 if (++cur_url_ != urls_.end() && 144
145 // A positive |retry_after_sec| is a hint from the server that the client
146 // should not send further request until the cooldown has expired.
147 if (retry_after_sec <= 0 && ++cur_url_ != urls_.end() &&
120 base::ThreadTaskRunnerHandle::Get()->PostTask( 148 base::ThreadTaskRunnerHandle::Get()->PostTask(
121 FROM_HERE, 149 FROM_HERE,
122 base::Bind(&RequestSender::SendInternal, base::Unretained(this)))) { 150 base::Bind(&RequestSender::SendInternal, base::Unretained(this)))) {
123 return; 151 return;
124 } 152 }
125 153
126 HandleSendError(error); 154 HandleSendError(error, retry_after_sec);
127 } 155 }
128 156
129 void RequestSender::OnURLFetchComplete(const net::URLFetcher* source) { 157 void RequestSender::OnURLFetchComplete(const net::URLFetcher* source) {
130 DCHECK(thread_checker_.CalledOnValidThread()); 158 DCHECK(thread_checker_.CalledOnValidThread());
131 DCHECK(source); 159 DCHECK(source);
132 160
133 VLOG(1) << "request completed from url: " << source->GetOriginalURL().spec(); 161 const GURL original_url(source->GetOriginalURL());
162 VLOG(1) << "request completed from url: " << original_url.spec();
134 163
135 const int fetch_error(GetFetchError(*source)); 164 const int fetch_error(GetFetchError(*source));
136 std::string response_body; 165 std::string response_body;
137 CHECK(source->GetResponseAsString(&response_body)); 166 CHECK(source->GetResponseAsString(&response_body));
138 167
168 int64_t retry_after_sec(-1);
169 const auto status(source->GetStatus().status());
170 if (original_url.SchemeIsCryptographic() &&
171 status == net::URLRequestStatus::SUCCESS) {
172 retry_after_sec = GetInt64HeaderValue(source, kHeaderXRetryAfter);
173 retry_after_sec = std::min(retry_after_sec, kMaxRetryAfterSec);
174 }
175
139 base::ThreadTaskRunnerHandle::Get()->PostTask( 176 base::ThreadTaskRunnerHandle::Get()->PostTask(
140 FROM_HERE, 177 FROM_HERE, base::Bind(&RequestSender::SendInternalComplete,
141 base::Bind(&RequestSender::SendInternalComplete, base::Unretained(this), 178 base::Unretained(this), fetch_error, response_body,
142 fetch_error, response_body, GetServerETag(source))); 179 GetStringHeaderValue(source, kHeaderEtag),
180 static_cast<int>(retry_after_sec)));
143 } 181 }
144 182
145 void RequestSender::HandleSendError(int error) { 183 void RequestSender::HandleSendError(int error, int retry_after_sec) {
146 base::ThreadTaskRunnerHandle::Get()->PostTask( 184 base::ThreadTaskRunnerHandle::Get()->PostTask(
147 FROM_HERE, base::Bind(request_sender_callback_, error, std::string())); 185 FROM_HERE, base::Bind(request_sender_callback_, error, std::string(),
186 retry_after_sec));
148 } 187 }
149 188
150 std::string RequestSender::GetKey(const char* key_bytes_base64) { 189 std::string RequestSender::GetKey(const char* key_bytes_base64) {
151 std::string result; 190 std::string result;
152 return base::Base64Decode(std::string(key_bytes_base64), &result) 191 return base::Base64Decode(std::string(key_bytes_base64), &result)
153 ? result 192 ? result
154 : std::string(); 193 : std::string();
155 } 194 }
156 195
157 GURL RequestSender::BuildUpdateUrl(const GURL& url, 196 GURL RequestSender::BuildUpdateUrl(const GURL& url,
158 const std::string& query_params) { 197 const std::string& query_params) {
159 const std::string query_string( 198 const std::string query_string(
160 url.has_query() ? base::StringPrintf("%s&%s", url.query().c_str(), 199 url.has_query() ? base::StringPrintf("%s&%s", url.query().c_str(),
161 query_params.c_str()) 200 query_params.c_str())
162 : query_params); 201 : query_params);
163 GURL::Replacements replacements; 202 GURL::Replacements replacements;
164 replacements.SetQueryStr(query_string); 203 replacements.SetQueryStr(query_string);
165 204
166 return url.ReplaceComponents(replacements); 205 return url.ReplaceComponents(replacements);
167 } 206 }
168 207
169 std::string RequestSender::GetServerETag(const net::URLFetcher* source) { 208 std::string RequestSender::GetStringHeaderValue(const net::URLFetcher* source,
209 const char* header_name) {
170 const auto response_headers(source->GetResponseHeaders()); 210 const auto response_headers(source->GetResponseHeaders());
171 if (!response_headers) 211 if (!response_headers)
172 return std::string(); 212 return std::string();
173 213
174 std::string etag; 214 std::string etag;
175 return response_headers->EnumerateHeader(nullptr, "ETag", &etag) 215 return response_headers->EnumerateHeader(nullptr, header_name, &etag)
176 ? etag 216 ? etag
177 : std::string(); 217 : std::string();
178 } 218 }
179 219
220 int64_t RequestSender::GetInt64HeaderValue(const net::URLFetcher* source,
221 const char* header_name) {
222 const auto response_headers(source->GetResponseHeaders());
223 return response_headers ? response_headers->GetInt64HeaderValue(header_name)
224 : -1;
225 }
226
180 } // namespace update_client 227 } // namespace update_client
OLDNEW
« no previous file with comments | « components/update_client/request_sender.h ('k') | components/update_client/request_sender_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698