OLD | NEW |
---|---|
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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 package org.chromium.chrome.browser.media.remote; | 5 package org.chromium.chrome.browser.media.remote; |
6 | 6 |
7 import android.content.Context; | 7 import android.content.Context; |
8 import android.net.Uri; | 8 import android.net.Uri; |
9 import android.os.AsyncTask; | 9 import android.os.AsyncTask; |
10 import android.text.TextUtils; | 10 import android.text.TextUtils; |
11 import android.util.Log; | 11 import android.util.Log; |
12 | 12 |
13 import org.apache.http.Header; | |
14 import org.apache.http.message.BasicHeader; | |
15 import org.chromium.chrome.browser.ChromiumApplication; | 13 import org.chromium.chrome.browser.ChromiumApplication; |
16 | 14 |
17 import java.io.IOException; | 15 import java.io.IOException; |
18 import java.net.HttpURLConnection; | 16 import java.net.HttpURLConnection; |
19 import java.net.MalformedURLException; | 17 import java.net.MalformedURLException; |
20 import java.net.URI; | 18 import java.net.URI; |
21 import java.net.URISyntaxException; | 19 import java.net.URISyntaxException; |
22 import java.net.URL; | 20 import java.net.URL; |
23 import java.util.Arrays; | 21 import java.util.List; |
22 import java.util.Map; | |
24 | 23 |
25 /** | 24 /** |
26 * Resolves the final URL if it's a redirect. Works asynchronously, uses HTTP | 25 * Resolves the final URL if it's a redirect. Works asynchronously, uses HTTP |
27 * HEAD request to determine if the URL is redirected. | 26 * HEAD request to determine if the URL is redirected. |
28 */ | 27 */ |
29 public class MediaUrlResolver extends AsyncTask<Void, Void, MediaUrlResolver.Res ult> { | 28 public class MediaUrlResolver extends AsyncTask<Void, Void, MediaUrlResolver.Res ult> { |
30 | 29 |
31 /** | 30 /** |
32 * The interface to get the initial URI with cookies from and pass the final | 31 * The interface to get the initial URI with cookies from and pass the final |
33 * URI to. | 32 * URI to. |
34 */ | 33 */ |
35 public interface Delegate { | 34 public interface Delegate { |
36 /** | 35 /** |
37 * @return the original URL to resolve. | 36 * @return the original URL to resolve. |
38 */ | 37 */ |
39 Uri getUri(); | 38 Uri getUri(); |
40 | 39 |
41 /** | 40 /** |
42 * @return the cookies to fetch the URL with. | 41 * @return the cookies to fetch the URL with. |
43 */ | 42 */ |
44 String getCookies(); | 43 String getCookies(); |
45 | 44 |
46 /** | 45 /** |
47 * Passes the resolved URL to the delegate. | 46 * Passes the resolved URL to the delegate. |
48 * | 47 * |
49 * @param uri the resolved URL. | 48 * @param uri the resolved URL. |
50 */ | 49 */ |
51 void setUri(Uri uri, Header[] headers); | 50 void setUri(Uri uri, Map<String, List<String>> map); |
52 } | 51 } |
53 | 52 |
54 | 53 |
55 protected static final class Result { | 54 protected static final class Result { |
56 private final String mUri; | 55 private final String mUri; |
57 private final Header[] mRelevantHeaders; | 56 private final Map<String, List<String>> mHeaders; |
58 | 57 |
59 public Result(String uri, Header[] relevantHeaders) { | 58 public Result(String uri, Map<String, List<String>> headers) { |
60 mUri = uri; | 59 mUri = uri; |
61 mRelevantHeaders = | 60 mHeaders = headers; |
62 relevantHeaders != null | |
63 ? Arrays.copyOf(relevantHeaders, relevantHeaders.length) | |
64 : null; | |
65 } | 61 } |
66 | 62 |
67 public String getUri() { | 63 public String getUri() { |
68 return mUri; | 64 return mUri; |
69 } | 65 } |
70 | 66 |
71 public Header[] getRelevantHeaders() { | 67 public Map<String, List<String>> getHeaders() { |
whywhat
2015/03/30 15:51:34
I thought you were going to turn this array into a
aberent
2015/03/30 17:24:04
Done.
| |
72 return mRelevantHeaders != null | 68 return mHeaders; |
73 ? Arrays.copyOf(mRelevantHeaders, mRelevantHeaders.length) | |
74 : null; | |
75 } | 69 } |
76 } | 70 } |
77 | 71 |
78 private static final String TAG = "MediaUrlResolver"; | 72 private static final String TAG = "MediaUrlResolver"; |
79 | 73 |
80 private static final String CORS_HEADER_NAME = "Access-Control-Allow-Origin" ; | |
81 private static final String COOKIES_HEADER_NAME = "Cookies"; | 74 private static final String COOKIES_HEADER_NAME = "Cookies"; |
82 private static final String USER_AGENT_HEADER_NAME = "User-Agent"; | 75 private static final String USER_AGENT_HEADER_NAME = "User-Agent"; |
83 private static final String RANGE_HEADER_NAME = "Range"; | 76 private static final String RANGE_HEADER_NAME = "Range"; |
84 | 77 |
85 // We don't want to necessarily fetch the whole video but we don't want to m iss the CORS header. | 78 // We don't want to necessarily fetch the whole video but we don't want to m iss the CORS header. |
86 // Assume that 64k should be more than enough to keep all the headers. | 79 // Assume that 64k should be more than enough to keep all the headers. |
87 private static final String RANGE_HEADER_VALUE = "bytes: 0-65536"; | 80 private static final String RANGE_HEADER_VALUE = "bytes: 0-65536"; |
88 | 81 |
89 private final Context mContext; | 82 private final Context mContext; |
90 private final Delegate mDelegate; | 83 private final Delegate mDelegate; |
91 | 84 |
92 /** | 85 /** |
93 * The constructor | 86 * The constructor |
94 * @param context the context to use to resolve the URL | 87 * @param context the context to use to resolve the URL |
95 * @param delegate The customer for this URL resolver. | 88 * @param delegate The customer for this URL resolver. |
96 */ | 89 */ |
97 public MediaUrlResolver(Context context, Delegate delegate) { | 90 public MediaUrlResolver(Context context, Delegate delegate) { |
98 mContext = context; | 91 mContext = context; |
99 mDelegate = delegate; | 92 mDelegate = delegate; |
100 } | 93 } |
101 | 94 |
102 @Override | 95 @Override |
103 protected MediaUrlResolver.Result doInBackground(Void... params) { | 96 protected MediaUrlResolver.Result doInBackground(Void... params) { |
104 Uri uri = mDelegate.getUri(); | 97 Uri uri = mDelegate.getUri(); |
105 String url = uri.toString(); | 98 String url = uri.toString(); |
106 Header[] relevantHeaders = null; | |
107 String cookies = mDelegate.getCookies(); | 99 String cookies = mDelegate.getCookies(); |
108 String userAgent = ChromiumApplication.getBrowserUserAgent(); | 100 String userAgent = ChromiumApplication.getBrowserUserAgent(); |
109 // URL may already be partially percent encoded; double percent encoding will break | 101 // URL may already be partially percent encoded; double percent encoding will break |
110 // things, so decode it before sanitizing it. | 102 // things, so decode it before sanitizing it. |
111 String sanitizedUrl = sanitizeUrl(Uri.decode(url)); | 103 String sanitizedUrl = sanitizeUrl(Uri.decode(url)); |
104 Map<String, List<String>> headers = null; | |
112 | 105 |
113 // If we failed to sanitize the URL (e.g. because the host name contains underscores) then | 106 // If we failed to sanitize the URL (e.g. because the host name contains underscores) then |
114 // HttpURLConnection won't work,so we can't follow redirections. Just tr y to use it as is. | 107 // HttpURLConnection won't work,so we can't follow redirections. Just tr y to use it as is. |
115 // TODO (aberent): Find out if there is a way of following redirections that is not so | 108 // TODO (aberent): Find out if there is a way of following redirections that is not so |
116 // strict on the URL format. | 109 // strict on the URL format. |
117 if (!sanitizedUrl.equals("")) { | 110 if (!sanitizedUrl.equals("")) { |
118 HttpURLConnection urlConnection = null; | 111 HttpURLConnection urlConnection = null; |
119 try { | 112 try { |
120 URL requestUrl = new URL(sanitizedUrl); | 113 URL requestUrl = new URL(sanitizedUrl); |
121 urlConnection = (HttpURLConnection) requestUrl.openConnection(); | 114 urlConnection = (HttpURLConnection) requestUrl.openConnection(); |
122 if (!TextUtils.isEmpty(cookies)) { | 115 if (!TextUtils.isEmpty(cookies)) { |
123 urlConnection.setRequestProperty(COOKIES_HEADER_NAME, cookie s); | 116 urlConnection.setRequestProperty(COOKIES_HEADER_NAME, cookie s); |
124 } | 117 } |
125 urlConnection.setRequestProperty(USER_AGENT_HEADER_NAME, userAge nt); | 118 urlConnection.setRequestProperty(USER_AGENT_HEADER_NAME, userAge nt); |
126 urlConnection.setRequestProperty(RANGE_HEADER_NAME, RANGE_HEADER _VALUE); | 119 urlConnection.setRequestProperty(RANGE_HEADER_NAME, RANGE_HEADER _VALUE); |
127 | 120 |
128 // This triggers resolving the URL and receiving the headers. | 121 // This triggers resolving the URL and receiving the headers. |
129 urlConnection.getHeaderFields(); | 122 urlConnection.getHeaderFields(); |
130 | 123 |
131 url = urlConnection.getURL().toString(); | 124 url = urlConnection.getURL().toString(); |
132 String corsHeader = urlConnection.getHeaderField(CORS_HEADER_NAM E); | 125 headers = urlConnection.getHeaderFields(); |
133 if (corsHeader != null) { | |
134 relevantHeaders = new Header[1]; | |
135 relevantHeaders[0] = new BasicHeader(CORS_HEADER_NAME, corsH eader); | |
136 } | |
137 } catch (IOException e) { | 126 } catch (IOException e) { |
138 Log.e(TAG, "Failed to fetch the final URI", e); | 127 Log.e(TAG, "Failed to fetch the final URI", e); |
139 url = ""; | 128 url = ""; |
140 } | 129 } |
141 if (urlConnection != null) urlConnection.disconnect(); | 130 if (urlConnection != null) urlConnection.disconnect(); |
142 } | 131 } |
143 return new MediaUrlResolver.Result(url, relevantHeaders); | 132 return new MediaUrlResolver.Result(url, headers); |
144 } | 133 } |
145 | 134 |
146 @Override | 135 @Override |
147 protected void onPostExecute(MediaUrlResolver.Result result) { | 136 protected void onPostExecute(MediaUrlResolver.Result result) { |
148 String url = result.getUri(); | 137 String url = result.getUri(); |
149 Uri uri = "".equals(url) ? Uri.EMPTY : Uri.parse(url); | 138 Uri uri = "".equals(url) ? Uri.EMPTY : Uri.parse(url); |
150 mDelegate.setUri(uri, result.getRelevantHeaders()); | 139 mDelegate.setUri(uri, result.getHeaders()); |
151 } | 140 } |
152 | 141 |
153 private String sanitizeUrl(String unsafeUrl) { | 142 private String sanitizeUrl(String unsafeUrl) { |
154 URL url; | 143 URL url; |
155 URI uri; | 144 URI uri; |
156 try { | 145 try { |
157 url = new URL(unsafeUrl); | 146 url = new URL(unsafeUrl); |
158 uri = new URI(url.getProtocol(), url.getUserInfo(), url.getHost(), u rl.getPort(), | 147 uri = new URI(url.getProtocol(), url.getUserInfo(), url.getHost(), u rl.getPort(), |
159 url.getPath(), url.getQuery(), url.getRef()); | 148 url.getPath(), url.getQuery(), url.getRef()); |
160 return uri.toURL().toString(); | 149 return uri.toURL().toString(); |
161 } catch (URISyntaxException syntaxException) { | 150 } catch (URISyntaxException syntaxException) { |
162 Log.w(TAG, "URISyntaxException " + syntaxException); | 151 Log.w(TAG, "URISyntaxException " + syntaxException); |
163 } catch (MalformedURLException malformedUrlException) { | 152 } catch (MalformedURLException malformedUrlException) { |
164 Log.w(TAG, "MalformedURLException " + malformedUrlException); | 153 Log.w(TAG, "MalformedURLException " + malformedUrlException); |
165 } | 154 } |
166 return ""; | 155 return ""; |
167 } | 156 } |
168 } | 157 } |
OLD | NEW |