]> rtime.felk.cvut.cz Git - hornmich/skoda-qr-demo.git/blob - BarCodeScanner/mobile/src/main/java/cz/cvut/fel/dce/barcodescanner/HttpHelper.java
Initial commit
[hornmich/skoda-qr-demo.git] / BarCodeScanner / mobile / src / main / java / cz / cvut / fel / dce / barcodescanner / HttpHelper.java
1 /*
2  * Copyright 2011 ZXing authors
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package cz.cvut.fel.dce.barcodescanner;
18
19 import android.util.Log;
20
21 import java.io.IOException;
22 import java.io.InputStreamReader;
23 import java.io.Reader;
24 import java.net.HttpURLConnection;
25 import java.net.URI;
26 import java.net.URISyntaxException;
27 import java.net.URL;
28 import java.net.URLConnection;
29 import java.util.Arrays;
30 import java.util.Collection;
31 import java.util.HashSet;
32
33 /**
34  * Utility methods for retrieving content over HTTP using the more-supported {@code java.net} classes
35  * in Android.
36  */
37 public final class HttpHelper {
38
39   private static final String TAG = HttpHelper.class.getSimpleName();
40
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"
44   ));
45
46   private HttpHelper() {
47   }
48   
49   public enum ContentType {
50     /** HTML-like content type, including HTML, XHTML, etc. */
51     HTML,
52     /** JSON content */
53     JSON,
54     /** XML */
55     XML,
56     /** Plain text content */
57     TEXT,
58   }
59
60   /**
61    * Downloads the entire resource instead of part.
62    *
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)
68    */
69   public static CharSequence downloadViaHttp(String uri, ContentType type) throws IOException {
70     return downloadViaHttp(uri, type, Integer.MAX_VALUE);
71   }
72
73   /**
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.
79    */
80   public static CharSequence downloadViaHttp(String uri, ContentType type, int maxChars) throws IOException {
81     String contentTypes;
82     switch (type) {
83       case HTML:
84         contentTypes = "application/xhtml+xml,text/html,text/*,*/*";
85         break;
86       case JSON:
87         contentTypes = "application/json,text/*,*/*";
88         break;
89       case XML:
90         contentTypes = "application/xml,text/*,*/*";
91         break;
92       case TEXT:
93       default:
94         contentTypes = "text/*,*/*";
95     }
96     return downloadViaHttp(uri, contentTypes, maxChars);
97   }
98
99   private static CharSequence downloadViaHttp(String uri, String contentTypes, int maxChars) throws IOException {
100     int redirects = 0;
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)");
108       try {
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) {
116               uri = location;
117               redirects++;
118               continue;
119             }
120             throw new IOException("No Location");
121           default:
122             throw new IOException("Bad HTTP response: " + responseCode);
123         }
124       } finally {
125         connection.disconnect();
126       }
127     }
128     throw new IOException("Too many redirects");
129   }
130
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());
137       }
138     }
139     return "UTF-8";
140   }
141
142   private static CharSequence consume(URLConnection connection, int maxChars) throws IOException {
143     String encoding = getEncoding(connection);
144     StringBuilder out = new StringBuilder();
145     Reader in = null;
146     try {
147       in = new InputStreamReader(connection.getInputStream(), encoding);
148       char[] buffer = new char[1024];
149       int charsRead;
150       while (out.length() < maxChars && (charsRead = in.read(buffer)) > 0) {
151         out.append(buffer, 0, charsRead);
152       }
153     } finally {
154       if (in != null) {
155         try {
156           in.close();
157         } catch (IOException | NullPointerException ioe) {
158           // continue
159         }
160       }
161     }
162     return out;
163   }
164
165   public static URI unredirect(URI uri) throws IOException {
166     if (!REDIRECTOR_DOMAINS.contains(uri.getHost())) {
167       return uri;
168     }
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)");
175     try {
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) {
185             try {
186               return new URI(location);
187             } catch (URISyntaxException e) {
188               // nevermind
189             }
190           }
191       }
192       return uri;
193     } finally {
194       connection.disconnect();
195     }
196   }
197   
198   private static HttpURLConnection safelyOpenConnection(URL url) throws IOException {
199     URLConnection conn;
200     try {
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);
206     }
207     if (!(conn instanceof HttpURLConnection)) {
208       throw new IOException();
209     }
210     return (HttpURLConnection) conn;
211   }
212
213   private static int safelyConnect(HttpURLConnection connection) throws IOException {
214     try {
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);
219     }
220     try {
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);
225     }
226   }
227
228 }