OLD | NEW |
(Empty) | |
| 1 // Copyright 2017 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 package org.chromium.chrome.browser.payments; |
| 6 |
| 7 import android.os.AsyncTask; |
| 8 |
| 9 import java.io.IOException; |
| 10 import java.net.HttpURLConnection; |
| 11 import java.net.URL; |
| 12 import java.util.List; |
| 13 import java.util.Map; |
| 14 import java.util.Scanner; |
| 15 import java.util.regex.Matcher; |
| 16 import java.util.regex.Pattern; |
| 17 |
| 18 /** |
| 19 * Downloads the payment method manifest based on the payment method name that i
s a URL with HTTPS |
| 20 * scheme, e.g., https://bobpay.com. The download happens via two consecutive HT
TP requests: |
| 21 * |
| 22 * 1) HEAD request for the payment method name. The HTTP response header is pars
ed for Link header |
| 23 * that points to the location of the payment method manifest file. Example o
f a relative |
| 24 * location: |
| 25 * |
| 26 * Link: <data/payment-manifest.json>; rel="payment-method-manifest
" |
| 27 * |
| 28 * (This is relative to the payment method URL.) Example of an absolute locat
ion: |
| 29 * |
| 30 * Link: <https://bobpay.com/data/payment-manifest.json>; rel="paym
ent-method-manifest" |
| 31 * |
| 32 * The absolute location must use HTTPS scheme. |
| 33 * |
| 34 * 2) GET request for the payment method manifest file. |
| 35 */ |
| 36 public class ManifestDownloader { |
| 37 /** Interface for the callback to invoke when finished downloading. */ |
| 38 public interface ManifestDownloadCallback { |
| 39 /** |
| 40 * Called on successful download of a payment method manifest. |
| 41 * |
| 42 * @param contents The successfully downloaded payment method manifest. |
| 43 */ |
| 44 void onManifestDownloadSuccess(String contents); |
| 45 |
| 46 /** Called on failed download of a payment method manifest. */ |
| 47 void onManifestDownloadFailure(); |
| 48 } |
| 49 |
| 50 /** The prefix of a payment method name for which a manifest can be download
ed. */ |
| 51 public static final String MANIFEST_SCHEME = "https://"; |
| 52 |
| 53 /** HTTP HEAD method name. */ |
| 54 private static final String HTTP_METHOD_HEAD = "HEAD"; |
| 55 |
| 56 /** HTTP Link header name. */ |
| 57 private static final String HTTP_HEADER_LINK = "Link"; |
| 58 |
| 59 /** The regular expression that captures the URL of the payment method manif
est. */ |
| 60 private static final String PATTERN_LINK = "^<(.*)>; rel=\"payment-method-ma
nifest\"$"; |
| 61 |
| 62 /** The regular expression capture group for the URL of the payment method m
anifest. */ |
| 63 private static final int GROUP_URL = 1; |
| 64 |
| 65 /** The token for requesting the full text of the download stream. */ |
| 66 private static final String SCANNER_FULL_TEXT_TOKEN = "\\A"; |
| 67 |
| 68 /** |
| 69 * Downloads the manifest file asynchronously. |
| 70 * |
| 71 * @param methodName The payment method name that is a URL with HTTPS scheme
. Must be a URL and |
| 72 * must start with "https://". |
| 73 * @param callback The callback to invoke when finished downloading. |
| 74 */ |
| 75 public static void download(final String methodName, final ManifestDownloadC
allback callback) { |
| 76 assert methodName != null; |
| 77 assert callback != null; |
| 78 assert methodName.startsWith(MANIFEST_SCHEME); |
| 79 |
| 80 (new AsyncTask<Void, Void, String>() { |
| 81 @Override |
| 82 protected String doInBackground(Void... ignored) { |
| 83 HttpURLConnection httpConnection = null; |
| 84 try { |
| 85 httpConnection = (HttpURLConnection) new URL(methodName).ope
nConnection(); |
| 86 if (httpConnection == null) return null; |
| 87 |
| 88 httpConnection.setRequestMethod(HTTP_METHOD_HEAD); |
| 89 int responseCode = httpConnection.getResponseCode(); |
| 90 if (responseCode != 200) { |
| 91 httpConnection.disconnect(); |
| 92 return null; |
| 93 } |
| 94 |
| 95 Map<String, List<String>> headers = httpConnection.getHeader
Fields(); |
| 96 Pattern pattern = Pattern.compile(PATTERN_LINK); |
| 97 String manifestUrl = null; |
| 98 for (Map.Entry<String, List<String>> header : headers.entryS
et()) { |
| 99 if (HTTP_HEADER_LINK.equals(header.getKey())) { |
| 100 List<String> values = header.getValue(); |
| 101 for (int i = 0; i < values.size(); i++) { |
| 102 Matcher matcher = pattern.matcher(values.get(i))
; |
| 103 if (matcher.matches()) manifestUrl = matcher.gro
up(GROUP_URL); |
| 104 } |
| 105 } |
| 106 } |
| 107 |
| 108 if (manifestUrl == null) { |
| 109 httpConnection.disconnect(); |
| 110 return null; |
| 111 } |
| 112 |
| 113 if (!manifestUrl.startsWith(MANIFEST_SCHEME)) { |
| 114 manifestUrl = methodName + "/" + manifestUrl; |
| 115 } |
| 116 |
| 117 httpConnection.disconnect(); |
| 118 httpConnection = (HttpURLConnection) new URL(manifestUrl).op
enConnection(); |
| 119 responseCode = httpConnection.getResponseCode(); |
| 120 if (responseCode != 200) { |
| 121 httpConnection.disconnect(); |
| 122 return null; |
| 123 } |
| 124 |
| 125 Scanner scanner = new Scanner(httpConnection.getInputStream(
)) |
| 126 .useDelimiter(SCANNER_FULL_TEXT_TOKEN); |
| 127 String result = scanner.hasNext() ? scanner.next() : null; |
| 128 httpConnection.disconnect(); |
| 129 return result; |
| 130 } catch (SecurityException | IOException e) { |
| 131 if (httpConnection != null) httpConnection.disconnect(); |
| 132 return null; |
| 133 } |
| 134 } |
| 135 |
| 136 @Override |
| 137 protected void onPostExecute(String data) { |
| 138 if (data != null) { |
| 139 callback.onManifestDownloadSuccess(data); |
| 140 } else { |
| 141 callback.onManifestDownloadFailure(); |
| 142 } |
| 143 } |
| 144 }).execute(); |
| 145 } |
| 146 |
| 147 private ManifestDownloader() {} |
| 148 } |
OLD | NEW |