2 * Copyright 2011 ZXing authors
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package cz.cvut.fel.dce.barcodescanner;
19 import android.util.Log;
21 import java.io.IOException;
22 import java.io.InputStreamReader;
23 import java.io.Reader;
24 import java.net.HttpURLConnection;
26 import java.net.URISyntaxException;
28 import java.net.URLConnection;
29 import java.util.Arrays;
30 import java.util.Collection;
31 import java.util.HashSet;
34 * Utility methods for retrieving content over HTTP using the more-supported {@code java.net} classes
37 public final class HttpHelper {
39 private static final String TAG = HttpHelper.class.getSimpleName();
41 private static final Collection<String> REDIRECTOR_DOMAINS = new HashSet<>(Arrays.asList(
42 "amzn.to", "bit.ly", "bitly.com", "fb.me", "goo.gl", "is.gd", "j.mp", "lnkd.in", "ow.ly",
43 "R.BEETAGG.COM", "r.beetagg.com", "SCN.BY", "su.pr", "t.co", "tinyurl.com", "tr.im"
46 private HttpHelper() {
49 public enum ContentType {
50 /** HTML-like content type, including HTML, XHTML, etc. */
56 /** Plain text content */
61 * Downloads the entire resource instead of part.
63 * @param uri URI to retrieve
64 * @param type expected text-like MIME type of that content
65 * @return content as a {@code String}
66 * @throws java.io.IOException if the content can't be retrieved because of a bad URI, network problem, etc.
67 * @see #downloadViaHttp(String, HttpHelper.ContentType, int)
69 public static CharSequence downloadViaHttp(String uri, ContentType type) throws IOException {
70 return downloadViaHttp(uri, type, Integer.MAX_VALUE);
74 * @param uri URI to retrieve
75 * @param type expected text-like MIME type of that content
76 * @param maxChars approximate maximum characters to read from the source
77 * @return content as a {@code String}
78 * @throws java.io.IOException if the content can't be retrieved because of a bad URI, network problem, etc.
80 public static CharSequence downloadViaHttp(String uri, ContentType type, int maxChars) throws IOException {
84 contentTypes = "application/xhtml+xml,text/html,text/*,*/*";
87 contentTypes = "application/json,text/*,*/*";
90 contentTypes = "application/xml,text/*,*/*";
94 contentTypes = "text/*,*/*";
96 return downloadViaHttp(uri, contentTypes, maxChars);
99 private static CharSequence downloadViaHttp(String uri, String contentTypes, int maxChars) throws IOException {
101 while (redirects < 5) {
102 URL url = new URL(uri);
103 HttpURLConnection connection = safelyOpenConnection(url);
104 connection.setInstanceFollowRedirects(true); // Won't work HTTP -> HTTPS or vice versa
105 connection.setRequestProperty("Accept", contentTypes);
106 connection.setRequestProperty("Accept-Charset", "utf-8,*");
107 connection.setRequestProperty("User-Agent", "ZXing (Android)");
109 int responseCode = safelyConnect(connection);
110 switch (responseCode) {
111 case HttpURLConnection.HTTP_OK:
112 return consume(connection, maxChars);
113 case HttpURLConnection.HTTP_MOVED_TEMP:
114 String location = connection.getHeaderField("Location");
115 if (location != null) {
120 throw new IOException("No Location");
122 throw new IOException("Bad HTTP response: " + responseCode);
125 connection.disconnect();
128 throw new IOException("Too many redirects");
131 private static String getEncoding(URLConnection connection) {
132 String contentTypeHeader = connection.getHeaderField("Content-Type");
133 if (contentTypeHeader != null) {
134 int charsetStart = contentTypeHeader.indexOf("charset=");
135 if (charsetStart >= 0) {
136 return contentTypeHeader.substring(charsetStart + "charset=".length());
142 private static CharSequence consume(URLConnection connection, int maxChars) throws IOException {
143 String encoding = getEncoding(connection);
144 StringBuilder out = new StringBuilder();
147 in = new InputStreamReader(connection.getInputStream(), encoding);
148 char[] buffer = new char[1024];
150 while (out.length() < maxChars && (charsRead = in.read(buffer)) > 0) {
151 out.append(buffer, 0, charsRead);
157 } catch (IOException | NullPointerException ioe) {
165 public static URI unredirect(URI uri) throws IOException {
166 if (!REDIRECTOR_DOMAINS.contains(uri.getHost())) {
169 URL url = uri.toURL();
170 HttpURLConnection connection = safelyOpenConnection(url);
171 connection.setInstanceFollowRedirects(false);
172 connection.setDoInput(false);
173 connection.setRequestMethod("HEAD");
174 connection.setRequestProperty("User-Agent", "ZXing (Android)");
176 int responseCode = safelyConnect(connection);
177 switch (responseCode) {
178 case HttpURLConnection.HTTP_MULT_CHOICE:
179 case HttpURLConnection.HTTP_MOVED_PERM:
180 case HttpURLConnection.HTTP_MOVED_TEMP:
181 case HttpURLConnection.HTTP_SEE_OTHER:
182 case 307: // No constant for 307 Temporary Redirect ?
183 String location = connection.getHeaderField("Location");
184 if (location != null) {
186 return new URI(location);
187 } catch (URISyntaxException e) {
194 connection.disconnect();
198 private static HttpURLConnection safelyOpenConnection(URL url) throws IOException {
201 conn = url.openConnection();
202 } catch (NullPointerException npe) {
203 // Another strange bug in Android?
204 Log.w(TAG, "Bad URI? " + url);
205 throw new IOException(npe);
207 if (!(conn instanceof HttpURLConnection)) {
208 throw new IOException();
210 return (HttpURLConnection) conn;
213 private static int safelyConnect(HttpURLConnection connection) throws IOException {
215 connection.connect();
216 } catch (NullPointerException | IllegalArgumentException | IndexOutOfBoundsException | SecurityException e) {
217 // this is an Android bug: http://code.google.com/p/android/issues/detail?id=16895
218 throw new IOException(e);
221 return connection.getResponseCode();
222 } catch (NullPointerException | StringIndexOutOfBoundsException | IllegalArgumentException e) {
223 // this is maybe this Android bug: http://code.google.com/p/android/issues/detail?id=15554
224 throw new IOException(e);