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

Side by Side Diff: tools/clang/traffic_annotation_extractor/tests/inputs/payments_client.cc

Issue 2448133006: Tool added to extract network traffic annotations. (Closed)
Patch Set: nits Created 3 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
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
battre 2017/04/07 08:42:27 Same here. I think that we want unittests, not int
Ramin Halavati 2017/04/07 11:33:32 Ditto.
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 "components/autofill/core/browser/payments/payments_client.h"
6
7 #include <memory>
8 #include <utility>
9 #include <vector>
10
11 #include "base/command_line.h"
12 #include "base/json/json_reader.h"
13 #include "base/json/json_writer.h"
14 #include "base/memory/ptr_util.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/values.h"
20 #include "build/build_config.h"
21 #include "components/autofill/core/browser/autofill_data_model.h"
22 #include "components/autofill/core/browser/autofill_type.h"
23 #include "components/autofill/core/browser/credit_card.h"
24 #include "components/autofill/core/browser/payments/payments_request.h"
25 #include "components/autofill/core/browser/payments/payments_service_url.h"
26 #include "components/data_use_measurement/core/data_use_user_data.h"
27 #include "google_apis/gaia/identity_provider.h"
28 #include "net/base/escape.h"
29 #include "net/base/load_flags.h"
30 #include "net/http/http_status_code.h"
31 #include "net/traffic_annotation/network_traffic_annotation.h"
32 #include "net/url_request/url_fetcher.h"
33 #include "net/url_request/url_request_context_getter.h"
34
35 namespace autofill {
36 namespace payments {
37
38 namespace {
39
40 const char kUnmaskCardRequestPath[] =
41 "payments/apis-secure/creditcardservice/getrealpan?s7e_suffix=chromewallet";
42 const char kUnmaskCardRequestFormat[] =
43 "requestContentType=application/json; charset=utf-8&request=%s"
44 "&s7e_13_cvc=%s";
45
46 const char kGetUploadDetailsRequestPath[] =
47 "payments/apis/chromepaymentsservice/getdetailsforsavecard";
48
49 const char kUploadCardRequestPath[] =
50 "payments/apis-secure/chromepaymentsservice/savecard"
51 "?s7e_suffix=chromewallet";
52 const char kUploadCardRequestFormat[] =
53 "requestContentType=application/json; charset=utf-8&request=%s"
54 "&s7e_1_pan=%s&s7e_13_cvc=%s";
55
56 const char kTokenServiceConsumerId[] = "wallet_client";
57 const char kPaymentsOAuth2Scope[] =
58 "https://www.googleapis.com/auth/wallet.chrome";
59
60 GURL GetRequestUrl(const std::string& path) {
61 if (base::CommandLine::ForCurrentProcess()->HasSwitch("sync-url")) {
62 if (IsPaymentsProductionEnabled()) {
63 LOG(ERROR) << "You are using production Payments but you specified a "
64 "--sync-url. You likely want to disable the sync sandbox "
65 "or switch to sandbox Payments. Both are controlled in "
66 "about:flags.";
67 }
68 } else if (!IsPaymentsProductionEnabled()) {
69 LOG(ERROR) << "You are using sandbox Payments but you didn't specify a "
70 "--sync-url. You likely want to enable the sync sandbox "
71 "or switch to production Payments. Both are controlled in "
72 "about:flags.";
73 }
74
75 return GetBaseSecureUrl().Resolve(path);
76 }
77
78 std::unique_ptr<base::DictionaryValue> BuildRiskDictionary(
79 const std::string& encoded_risk_data) {
80 std::unique_ptr<base::DictionaryValue> risk_data(new base::DictionaryValue());
81 #if defined(OS_IOS)
82 // Browser fingerprinting is not available on iOS. Instead, we generate
83 // RiskAdvisoryData.
84 risk_data->SetString("message_type", "RISK_ADVISORY_DATA");
85 risk_data->SetString("encoding_type", "BASE_64_URL");
86 #else
87 risk_data->SetString("message_type", "BROWSER_NATIVE_FINGERPRINTING");
88 risk_data->SetString("encoding_type", "BASE_64");
89 #endif
90
91 risk_data->SetString("value", encoded_risk_data);
92
93 return risk_data;
94 }
95
96 void SetStringIfNotEmpty(const AutofillDataModel& profile,
97 const ServerFieldType& type,
98 const std::string& app_locale,
99 const std::string& path,
100 base::DictionaryValue* dictionary) {
101 const base::string16 value = profile.GetInfo(AutofillType(type), app_locale);
102 if (!value.empty())
103 dictionary->SetString(path, value);
104 }
105
106 void AppendStringIfNotEmpty(const AutofillProfile& profile,
107 const ServerFieldType& type,
108 const std::string& app_locale,
109 base::ListValue* list) {
110 const base::string16 value = profile.GetInfo(AutofillType(type), app_locale);
111 if (!value.empty())
112 list->AppendString(value);
113 }
114
115 // Returns a dictionary with the structure expected by Payments RPCs, containing
116 // each of the fields in |profile|, formatted according to |app_locale|. If
117 // |include_non_location_data| is false, the name and phone number in |profile|
118 // are not included.
119 std::unique_ptr<base::DictionaryValue> BuildAddressDictionary(
120 const AutofillProfile& profile,
121 const std::string& app_locale,
122 bool include_non_location_data) {
123 std::unique_ptr<base::DictionaryValue> postal_address(
124 new base::DictionaryValue());
125
126 if (include_non_location_data) {
127 SetStringIfNotEmpty(profile, NAME_FULL, app_locale,
128 PaymentsClient::kRecipientName, postal_address.get());
129 }
130
131 std::unique_ptr<base::ListValue> address_lines(new base::ListValue());
132 AppendStringIfNotEmpty(profile, ADDRESS_HOME_LINE1, app_locale,
133 address_lines.get());
134 AppendStringIfNotEmpty(profile, ADDRESS_HOME_LINE2, app_locale,
135 address_lines.get());
136 AppendStringIfNotEmpty(profile, ADDRESS_HOME_LINE3, app_locale,
137 address_lines.get());
138 if (!address_lines->empty())
139 postal_address->Set("address_line", std::move(address_lines));
140
141 SetStringIfNotEmpty(profile, ADDRESS_HOME_CITY, app_locale, "locality_name",
142 postal_address.get());
143 SetStringIfNotEmpty(profile, ADDRESS_HOME_STATE, app_locale,
144 "administrative_area_name", postal_address.get());
145 SetStringIfNotEmpty(profile, ADDRESS_HOME_ZIP, app_locale,
146 "postal_code_number", postal_address.get());
147
148 // Use GetRawInfo to get a country code instead of the country name:
149 const base::string16 country_code = profile.GetRawInfo(ADDRESS_HOME_COUNTRY);
150 if (!country_code.empty())
151 postal_address->SetString("country_name_code", country_code);
152
153 std::unique_ptr<base::DictionaryValue> address(new base::DictionaryValue());
154 address->Set("postal_address", std::move(postal_address));
155
156 if (include_non_location_data) {
157 SetStringIfNotEmpty(profile, PHONE_HOME_WHOLE_NUMBER, app_locale,
158 PaymentsClient::kPhoneNumber, address.get());
159 }
160
161 return address;
162 }
163
164 class UnmaskCardRequest : public PaymentsRequest {
165 public:
166 UnmaskCardRequest(const PaymentsClient::UnmaskRequestDetails& request_details)
167 : request_details_(request_details) {
168 DCHECK(
169 CreditCard::MASKED_SERVER_CARD == request_details.card.record_type() ||
170 CreditCard::FULL_SERVER_CARD == request_details.card.record_type());
171 }
172 ~UnmaskCardRequest() override {}
173
174 std::string GetRequestUrlPath() override { return kUnmaskCardRequestPath; }
175
176 std::string GetRequestContentType() override {
177 return "application/x-www-form-urlencoded";
178 }
179
180 std::string GetRequestContent() override {
181 base::DictionaryValue request_dict;
182 request_dict.SetString("encrypted_cvc", "__param:s7e_13_cvc");
183 request_dict.SetString("credit_card_id", request_details_.card.server_id());
184 request_dict.Set("risk_data_encoded",
185 BuildRiskDictionary(request_details_.risk_data));
186 request_dict.Set("context", base::MakeUnique<base::DictionaryValue>());
187
188 int value = 0;
189 if (base::StringToInt(request_details_.user_response.exp_month, &value))
190 request_dict.SetInteger("expiration_month", value);
191 if (base::StringToInt(request_details_.user_response.exp_year, &value))
192 request_dict.SetInteger("expiration_year", value);
193
194 std::string json_request;
195 base::JSONWriter::Write(request_dict, &json_request);
196 std::string request_content = base::StringPrintf(
197 kUnmaskCardRequestFormat,
198 net::EscapeUrlEncodedData(json_request, true).c_str(),
199 net::EscapeUrlEncodedData(
200 base::UTF16ToASCII(request_details_.user_response.cvc), true)
201 .c_str());
202 VLOG(3) << "getrealpan request body: " << request_content;
203 return request_content;
204 }
205
206 void ParseResponse(std::unique_ptr<base::DictionaryValue> response) override {
207 response->GetString("pan", &real_pan_);
208 }
209
210 bool IsResponseComplete() override { return !real_pan_.empty(); }
211
212 void RespondToDelegate(PaymentsClientDelegate* delegate,
213 AutofillClient::PaymentsRpcResult result) override {
214 delegate->OnDidGetRealPan(result, real_pan_);
215 }
216
217 private:
218 PaymentsClient::UnmaskRequestDetails request_details_;
219 std::string real_pan_;
220 };
221
222 class GetUploadDetailsRequest : public PaymentsRequest {
223 public:
224 GetUploadDetailsRequest(const std::vector<AutofillProfile>& addresses,
225 const std::string& app_locale)
226 : addresses_(addresses), app_locale_(app_locale) {}
227 ~GetUploadDetailsRequest() override {}
228
229 std::string GetRequestUrlPath() override {
230 return kGetUploadDetailsRequestPath;
231 }
232
233 std::string GetRequestContentType() override { return "application/json"; }
234
235 std::string GetRequestContent() override {
236 base::DictionaryValue request_dict;
237 std::unique_ptr<base::DictionaryValue> context(new base::DictionaryValue());
238 context->SetString("language_code", app_locale_);
239 request_dict.Set("context", std::move(context));
240
241 std::unique_ptr<base::ListValue> addresses(new base::ListValue());
242 for (const AutofillProfile& profile : addresses_) {
243 // These addresses are used by Payments to (1) accurately determine the
244 // user's country in order to show the correct legal documents and (2) to
245 // verify that the addresses are valid for their purposes so that we don't
246 // offer save in a case where it would definitely fail (e.g. P.O. boxes).
247 // The final parameter directs BuildAddressDictionary to omit names and
248 // phone numbers, which aren't useful for these purposes.
249 addresses->Append(BuildAddressDictionary(profile, app_locale_, false));
250 }
251 request_dict.Set("address", std::move(addresses));
252
253 std::string request_content;
254 base::JSONWriter::Write(request_dict, &request_content);
255 VLOG(3) << "getdetailsforsavecard request body: " << request_content;
256 return request_content;
257 }
258
259 void ParseResponse(std::unique_ptr<base::DictionaryValue> response) override {
260 response->GetString("context_token", &context_token_);
261 base::DictionaryValue* unowned_legal_message;
262 if (response->GetDictionary("legal_message", &unowned_legal_message))
263 legal_message_ = unowned_legal_message->CreateDeepCopy();
264 }
265
266 bool IsResponseComplete() override {
267 return !context_token_.empty() && legal_message_;
268 }
269
270 void RespondToDelegate(PaymentsClientDelegate* delegate,
271 AutofillClient::PaymentsRpcResult result) override {
272 delegate->OnDidGetUploadDetails(result, context_token_,
273 std::move(legal_message_));
274 }
275
276 private:
277 std::vector<AutofillProfile> addresses_;
278 std::string app_locale_;
279 base::string16 context_token_;
280 std::unique_ptr<base::DictionaryValue> legal_message_;
281 };
282
283 class UploadCardRequest : public PaymentsRequest {
284 public:
285 UploadCardRequest(const PaymentsClient::UploadRequestDetails& request_details)
286 : request_details_(request_details) {}
287 ~UploadCardRequest() override {}
288
289 std::string GetRequestUrlPath() override { return kUploadCardRequestPath; }
290
291 std::string GetRequestContentType() override {
292 return "application/x-www-form-urlencoded";
293 }
294
295 std::string GetRequestContent() override {
296 base::DictionaryValue request_dict;
297 request_dict.SetString("encrypted_pan", "__param:s7e_1_pan");
298 request_dict.SetString("encrypted_cvc", "__param:s7e_13_cvc");
299 request_dict.Set("risk_data_encoded",
300 BuildRiskDictionary(request_details_.risk_data));
301
302 const std::string& app_locale = request_details_.app_locale;
303 std::unique_ptr<base::DictionaryValue> context(new base::DictionaryValue());
304 context->SetString("language_code", app_locale);
305 request_dict.Set("context", std::move(context));
306
307 SetStringIfNotEmpty(request_details_.card, CREDIT_CARD_NAME_FULL,
308 app_locale, "cardholder_name", &request_dict);
309
310 std::unique_ptr<base::ListValue> addresses(new base::ListValue());
311 for (const AutofillProfile& profile : request_details_.profiles) {
312 addresses->Append(BuildAddressDictionary(profile, app_locale, true));
313 }
314 request_dict.Set("address", std::move(addresses));
315
316 request_dict.SetString("context_token", request_details_.context_token);
317
318 int value = 0;
319 const base::string16 exp_month = request_details_.card.GetInfo(
320 AutofillType(CREDIT_CARD_EXP_MONTH), app_locale);
321 const base::string16 exp_year = request_details_.card.GetInfo(
322 AutofillType(CREDIT_CARD_EXP_4_DIGIT_YEAR), app_locale);
323 if (base::StringToInt(exp_month, &value))
324 request_dict.SetInteger("expiration_month", value);
325 if (base::StringToInt(exp_year, &value))
326 request_dict.SetInteger("expiration_year", value);
327
328 const base::string16 pan = request_details_.card.GetInfo(
329 AutofillType(CREDIT_CARD_NUMBER), app_locale);
330 std::string json_request;
331 base::JSONWriter::Write(request_dict, &json_request);
332 std::string request_content = base::StringPrintf(
333 kUploadCardRequestFormat,
334 net::EscapeUrlEncodedData(json_request, true).c_str(),
335 net::EscapeUrlEncodedData(base::UTF16ToASCII(pan), true).c_str(),
336 net::EscapeUrlEncodedData(base::UTF16ToASCII(request_details_.cvc),
337 true)
338 .c_str());
339 VLOG(3) << "savecard request body: " << request_content;
340 return request_content;
341 }
342
343 void ParseResponse(std::unique_ptr<base::DictionaryValue> response) override {
344 }
345
346 bool IsResponseComplete() override { return true; }
347
348 void RespondToDelegate(PaymentsClientDelegate* delegate,
349 AutofillClient::PaymentsRpcResult result) override {
350 delegate->OnDidUploadCard(result);
351 }
352
353 private:
354 PaymentsClient::UploadRequestDetails request_details_;
355 };
356
357 } // namespace
358
359 const char PaymentsClient::kRecipientName[] = "recipient_name";
360 const char PaymentsClient::kPhoneNumber[] = "phone_number";
361
362 PaymentsClient::UnmaskRequestDetails::UnmaskRequestDetails() {}
363 PaymentsClient::UnmaskRequestDetails::~UnmaskRequestDetails() {}
364
365 PaymentsClient::UploadRequestDetails::UploadRequestDetails() {}
366 PaymentsClient::UploadRequestDetails::UploadRequestDetails(
367 const UploadRequestDetails& other) = default;
368 PaymentsClient::UploadRequestDetails::~UploadRequestDetails() {}
369
370 PaymentsClient::PaymentsClient(net::URLRequestContextGetter* context_getter,
371 PaymentsClientDelegate* delegate)
372 : OAuth2TokenService::Consumer(kTokenServiceConsumerId),
373 context_getter_(context_getter),
374 delegate_(delegate),
375 has_retried_authorization_(false),
376 weak_ptr_factory_(this) {
377 DCHECK(delegate);
378 }
379
380 PaymentsClient::~PaymentsClient() {}
381
382 void PaymentsClient::Prepare() {
383 if (access_token_.empty())
384 StartTokenFetch(false);
385 }
386
387 void PaymentsClient::UnmaskCard(
388 const PaymentsClient::UnmaskRequestDetails& request_details) {
389 IssueRequest(base::MakeUnique<UnmaskCardRequest>(request_details), true);
390 }
391
392 void PaymentsClient::GetUploadDetails(
393 const std::vector<AutofillProfile>& addresses,
394 const std::string& app_locale) {
395 IssueRequest(base::MakeUnique<GetUploadDetailsRequest>(addresses, app_locale),
396 false);
397 }
398
399 void PaymentsClient::UploadCard(
400 const PaymentsClient::UploadRequestDetails& request_details) {
401 IssueRequest(base::MakeUnique<UploadCardRequest>(request_details), true);
402 }
403
404 void PaymentsClient::IssueRequest(std::unique_ptr<PaymentsRequest> request,
405 bool authenticate) {
406 request_ = std::move(request);
407 has_retried_authorization_ = false;
408 InitializeUrlFetcher();
409
410 if (!authenticate)
411 url_fetcher_->Start();
412 else if (access_token_.empty())
413 StartTokenFetch(false);
414 else
415 SetOAuth2TokenAndStartRequest();
416 }
417
418 void PaymentsClient::InitializeUrlFetcher() {
419 net::NetworkTrafficAnnotationTag traffic_annotation =
420 net::DefineNetworkTrafficAnnotation("payments_sync_cards", R"(
421 semantics {
422 sender: "Payments"
423 description:
424 "This service communicates with Google Payments servers to upload "
425 "(save) or receive the user's credit card info."
426 trigger:
427 "Requests are triggered by a user action, such as selecting a "
428 "masked server card from Chromium's credit card autofill dropdown, "
429 "submitting a form which has credit card information, or accepting "
430 "the prompt to save a credit card to Payments servers."
431 data:
432 "In case of save, a protocol buffer containing relevant address "
433 "and credit card information which should be saved in Google "
434 "Payments servers, along with user credentials. In case of load, a "
435 "protocol buffer containing the id of the credit card to unmask, "
436 "an encrypted cvc value, an optional updated card expiration date, "
437 "and user credentials."
438 destination: GOOGLE_OWNED_SERVICE
439 }
440 policy {
441 cookies_allowed: false
442 setting:
443 "Users can enable or disable this feature in Chromium settings by "
444 "toggling 'Credit cards and addresses using Google Payments', "
445 "under 'Advanced sync settings...'. This feature is enabled by "
446 "default."
447 chrome_policy {
448 AutoFillEnabled {
449 policy_options {mode: MANDATORY}
450 AutoFillEnabled: false
451 }
452 }
453 })");
454 url_fetcher_ =
455 net::URLFetcher::Create(0, GetRequestUrl(request_->GetRequestUrlPath()),
456 net::URLFetcher::POST, this, traffic_annotation);
457
458 data_use_measurement::DataUseUserData::AttachToFetcher(
459 url_fetcher_.get(), data_use_measurement::DataUseUserData::AUTOFILL);
460 url_fetcher_->SetRequestContext(context_getter_.get());
461 url_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES |
462 net::LOAD_DO_NOT_SEND_COOKIES |
463 net::LOAD_DISABLE_CACHE);
464
465 url_fetcher_->SetUploadData(request_->GetRequestContentType(),
466 request_->GetRequestContent());
467 }
468
469 void PaymentsClient::CancelRequest() {
470 request_.reset();
471 url_fetcher_.reset();
472 access_token_request_.reset();
473 access_token_.clear();
474 has_retried_authorization_ = false;
475 }
476
477 void PaymentsClient::OnURLFetchComplete(const net::URLFetcher* source) {
478 DCHECK_EQ(source, url_fetcher_.get());
479
480 // |url_fetcher_|, which is aliased to |source|, might continue to be used in
481 // this method, but should be freed once control leaves the method.
482 std::unique_ptr<net::URLFetcher> scoped_url_fetcher(std::move(url_fetcher_));
483 std::unique_ptr<base::DictionaryValue> response_dict;
484 int response_code = source->GetResponseCode();
485 std::string data;
486 source->GetResponseAsString(&data);
487 VLOG(2) << "Got data: " << data;
488
489 AutofillClient::PaymentsRpcResult result = AutofillClient::SUCCESS;
490
491 switch (response_code) {
492 // Valid response.
493 case net::HTTP_OK: {
494 std::string error_code;
495 std::unique_ptr<base::Value> message_value = base::JSONReader::Read(data);
496 if (message_value.get() &&
497 message_value->IsType(base::Value::Type::DICTIONARY)) {
498 response_dict.reset(
499 static_cast<base::DictionaryValue*>(message_value.release()));
500 response_dict->GetString("error.code", &error_code);
501 request_->ParseResponse(std::move(response_dict));
502 }
503
504 if (base::LowerCaseEqualsASCII(error_code, "internal"))
505 result = AutofillClient::TRY_AGAIN_FAILURE;
506 else if (!error_code.empty() || !request_->IsResponseComplete())
507 result = AutofillClient::PERMANENT_FAILURE;
508
509 break;
510 }
511
512 case net::HTTP_UNAUTHORIZED: {
513 if (has_retried_authorization_) {
514 result = AutofillClient::PERMANENT_FAILURE;
515 break;
516 }
517 has_retried_authorization_ = true;
518
519 InitializeUrlFetcher();
520 StartTokenFetch(true);
521 return;
522 }
523
524 // TODO(estade): is this actually how network connectivity issues are
525 // reported?
526 case net::HTTP_REQUEST_TIMEOUT: {
527 result = AutofillClient::NETWORK_ERROR;
528 break;
529 }
530
531 // Handle anything else as a generic (permanent) failure.
532 default: {
533 result = AutofillClient::PERMANENT_FAILURE;
534 break;
535 }
536 }
537
538 if (result != AutofillClient::SUCCESS) {
539 VLOG(1) << "Payments returned error: " << response_code
540 << " with data: " << data;
541 }
542
543 request_->RespondToDelegate(delegate_, result);
544 }
545
546 void PaymentsClient::OnGetTokenSuccess(
547 const OAuth2TokenService::Request* request,
548 const std::string& access_token,
549 const base::Time& expiration_time) {
550 DCHECK_EQ(request, access_token_request_.get());
551 access_token_ = access_token;
552 if (url_fetcher_)
553 SetOAuth2TokenAndStartRequest();
554
555 access_token_request_.reset();
556 }
557
558 void PaymentsClient::OnGetTokenFailure(
559 const OAuth2TokenService::Request* request,
560 const GoogleServiceAuthError& error) {
561 DCHECK_EQ(request, access_token_request_.get());
562 VLOG(1) << "Unhandled OAuth2 error: " << error.ToString();
563 if (url_fetcher_) {
564 url_fetcher_.reset();
565 request_->RespondToDelegate(delegate_, AutofillClient::PERMANENT_FAILURE);
566 }
567 access_token_request_.reset();
568 }
569
570 void PaymentsClient::StartTokenFetch(bool invalidate_old) {
571 // We're still waiting for the last request to come back.
572 if (!invalidate_old && access_token_request_)
573 return;
574
575 OAuth2TokenService::ScopeSet payments_scopes;
576 payments_scopes.insert(kPaymentsOAuth2Scope);
577 IdentityProvider* identity = delegate_->GetIdentityProvider();
578 if (invalidate_old) {
579 DCHECK(!access_token_.empty());
580 identity->GetTokenService()->InvalidateAccessToken(
581 identity->GetActiveAccountId(), payments_scopes, access_token_);
582 }
583 access_token_.clear();
584 access_token_request_ = identity->GetTokenService()->StartRequest(
585 identity->GetActiveAccountId(), payments_scopes, this);
586 }
587
588 void PaymentsClient::SetOAuth2TokenAndStartRequest() {
589 url_fetcher_->AddExtraRequestHeader(net::HttpRequestHeaders::kAuthorization +
590 std::string(": Bearer ") + access_token_);
591
592 url_fetcher_->Start();
593 }
594
595 } // namespace payments
596 } // namespace autofill
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698