Merge from "Barcode Scanner Plus": avoid deprecated AndroidHttpClient and use java.net for networking; better HTTP headers, better redirect handling

git-svn-id: https://zxing.googlecode.com/svn/trunk@1958 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
srowen 2011-10-12 11:53:54 +00:00
parent 3bd4e1e903
commit d2ed7976ef
8 changed files with 269 additions and 379 deletions

View file

@ -1,170 +0,0 @@
/*
* Copyright (C) 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.client.android;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.params.HttpClientParams;
import org.apache.http.client.protocol.ClientContext;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import java.io.IOException;
/**
* <p>Subclass of the Apache {@link DefaultHttpClient} that is configured with
* reasonable default settings and registered schemes for Android, and
* also lets the user add {@link HttpRequestInterceptor} classes.
* Don't create this directly, use the {@link #newInstance} factory method.</p>
* <p/>
* <p>This client processes cookies but does not retain them by default.
* To retain cookies, simply add a cookie store to the HttpContext:
* <pre>context.setAttribute(ClientContext.COOKIE_STORE, cookieStore);</pre>
* </p>
*/
public final class AndroidHttpClient implements HttpClient {
/**
* Create a new HttpClient with reasonable defaults (which you can update).
*
* @param userAgent to report in your HTTP requests.
* @return AndroidHttpClient for you to use for all your requests.
*/
public static AndroidHttpClient newInstance(String userAgent) {
HttpParams params = new BasicHttpParams();
// Turn off stale checking. Our connections break all the time anyway,
// and it's not worth it to pay the penalty of checking every time.
HttpConnectionParams.setStaleCheckingEnabled(params, false);
// Default connection and socket timeout of 20 seconds. Tweak to taste.
HttpConnectionParams.setConnectionTimeout(params, 20 * 1000);
HttpConnectionParams.setSoTimeout(params, 20 * 1000);
HttpConnectionParams.setSocketBufferSize(params, 8192);
// Don't handle redirects -- return them to the caller. Our code
// often wants to re-POST after a redirect, which we must do ourselves.
HttpClientParams.setRedirecting(params, false);
// Set the specified user agent and register standard protocols.
if (userAgent != null) {
HttpProtocolParams.setUserAgent(params, userAgent);
}
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
schemeRegistry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
ClientConnectionManager manager = new ThreadSafeClientConnManager(params, schemeRegistry);
// We use a factory method to modify superclass initialization
// parameters without the funny call-a-static-method dance.
return new AndroidHttpClient(manager, params);
}
private final HttpClient delegate;
private AndroidHttpClient(ClientConnectionManager ccm, HttpParams params) {
this.delegate = new DelegateHttpClient(ccm, params);
}
/**
* Release resources associated with this client. You must call this,
* or significant resources (sockets and memory) may be leaked.
*/
public void close() {
getConnectionManager().shutdown();
}
public HttpParams getParams() {
return delegate.getParams();
}
public ClientConnectionManager getConnectionManager() {
return delegate.getConnectionManager();
}
public HttpResponse execute(HttpUriRequest request) throws IOException {
return delegate.execute(request);
}
public HttpResponse execute(HttpUriRequest request, HttpContext context) throws IOException {
return delegate.execute(request, context);
}
public HttpResponse execute(HttpHost target, HttpRequest request) throws IOException {
return delegate.execute(target, request);
}
public HttpResponse execute(HttpHost target, HttpRequest request, HttpContext context) throws IOException {
return delegate.execute(target, request, context);
}
public <T> T execute(HttpUriRequest request, ResponseHandler<? extends T> responseHandler) throws IOException {
return delegate.execute(request, responseHandler);
}
public <T> T execute(HttpUriRequest request, ResponseHandler<? extends T> responseHandler, HttpContext context)
throws IOException {
return delegate.execute(request, responseHandler, context);
}
public <T> T execute(HttpHost target, HttpRequest request, ResponseHandler<? extends T> responseHandler)
throws IOException {
return delegate.execute(target, request, responseHandler);
}
public <T> T execute(HttpHost target, HttpRequest request,
ResponseHandler<? extends T> responseHandler,
HttpContext context) throws IOException {
return delegate.execute(target, request, responseHandler, context);
}
private static class DelegateHttpClient extends DefaultHttpClient {
private DelegateHttpClient(ClientConnectionManager ccm, HttpParams params) {
super(ccm, params);
}
@Override
protected HttpContext createHttpContext() {
// Same as DefaultHttpClient.createHttpContext() minus the
// cookie store.
HttpContext context = new BasicHttpContext();
context.setAttribute(ClientContext.AUTHSCHEME_REGISTRY, getAuthSchemes());
context.setAttribute(ClientContext.COOKIESPEC_REGISTRY, getCookieSpecs());
context.setAttribute(ClientContext.CREDS_PROVIDER, getCredentialsProvider());
return context;
}
}
}

View file

@ -0,0 +1,168 @@
/*
* Copyright 2011 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.client.android;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
/**
* Utility methods for retrieving content over HTTP using the more-supported {@code java.net} classes
* in Android.
*/
public final class HttpHelper {
private static final Collection<String> REDIRECTOR_DOMAINS = new HashSet<String>(Arrays.asList(
"amzn.to", "bit.ly", "bitly.com", "fb.me", "goo.gl", "is.gd", "j.mp", "lnkd.in", "ow.ly",
"SCN.BY", "su.pr", "t.co", "tinyurl.com", "tr.im"
));
private HttpHelper() {
}
public enum ContentType {
/** HTML-like content type, including HTML, XHTML, etc. */
HTML,
/** JSON content */
JSON,
/** Plain text content */
TEXT,
}
/**
* @param uri URI to retrieve
* @param type expected text-like MIME type of that content
* @return content as a {@code String}
* @throws IOException if the content can't be retrieved because of a bad URI, network problem, etc.
*/
public static String downloadViaHttp(String uri, ContentType type) throws IOException {
String contentTypes;
switch (type) {
case HTML:
contentTypes = "application/xhtml+xml,text/html,text/*,*/*";
break;
case JSON:
contentTypes = "application/json,text/*,*/*";
break;
case TEXT:
default:
contentTypes = "text/*,*/*";
}
return downloadViaHttp(uri, contentTypes);
}
private static String downloadViaHttp(String uri, String contentTypes) throws IOException {
URL url = new URL(uri);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestProperty("Accept", contentTypes);
connection.setRequestProperty("Accept-Charset", "utf-8,*");
connection.setRequestProperty("User-Agent", "ZXing (Android)");
try {
connection.connect();
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
throw new IOException("Bad HTTP response: " + connection.getResponseCode());
}
return consume(connection);
} finally {
connection.disconnect();
}
}
private static String getEncoding(HttpURLConnection connection) {
String contentTypeHeader = connection.getHeaderField("Content-Type");
if (contentTypeHeader != null) {
int charsetStart = contentTypeHeader.indexOf("charset=");
if (charsetStart >= 0) {
return contentTypeHeader.substring(charsetStart + "charset=".length());
}
}
return "UTF-8";
}
private static String consume(HttpURLConnection connection) throws IOException {
String encoding = getEncoding(connection);
ByteArrayOutputStream out = new ByteArrayOutputStream();
InputStream in = connection.getInputStream();
try {
in = connection.getInputStream();
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = in.read(buffer)) > 0) {
out.write(buffer, 0, bytesRead);
}
} finally {
try {
in.close();
} catch (IOException ioe) {
// continue
}
}
try {
return new String(out.toByteArray(), encoding);
} catch (UnsupportedEncodingException uee) {
try {
return new String(out.toByteArray(), "UTF-8");
} catch (UnsupportedEncodingException uee2) {
// can't happen
throw new IllegalStateException(uee2);
}
}
}
public static URI unredirect(URI uri) throws IOException {
if (!REDIRECTOR_DOMAINS.contains(uri.getHost())) {
return uri;
}
URL url = uri.toURL();
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setInstanceFollowRedirects(false);
connection.setDoInput(false);
connection.setRequestMethod("HEAD");
connection.setRequestProperty("User-Agent", "ZXing (Android)");
try {
connection.connect();
switch (connection.getResponseCode()) {
case HttpURLConnection.HTTP_MULT_CHOICE:
case HttpURLConnection.HTTP_MOVED_PERM:
case HttpURLConnection.HTTP_MOVED_TEMP:
case HttpURLConnection.HTTP_SEE_OTHER:
case 307: // No constant for 307 Temporary Redirect ?
String location = connection.getHeaderField("Location");
if (location != null) {
try {
return new URI(location);
} catch (URISyntaxException e) {
// nevermind
}
}
}
return uri;
} finally {
connection.disconnect();
}
}
}

View file

@ -0,0 +1,76 @@
/*
* Copyright (C) 2010 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.client.android.book;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import com.google.zxing.client.android.HttpHelper;
import com.google.zxing.client.android.LocaleManager;
import com.google.zxing.client.android.R;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
final class NetworkWorker implements Runnable {
private static final String TAG = NetworkWorker.class.getSimpleName();
private final String isbn;
private final String query;
private final Handler handler;
NetworkWorker(String isbn, String query, Handler handler) {
this.isbn = isbn;
this.query = query;
this.handler = handler;
}
@Override
public void run() {
try {
// These return a JSON result which describes if and where the query was found. This API may
// break or disappear at any time in the future. Since this is an API call rather than a
// website, we don't use LocaleManager to change the TLD.
String uri;
if (LocaleManager.isBookSearchUrl(isbn)) {
int equals = isbn.indexOf('=');
String volumeId = isbn.substring(equals + 1);
uri = "http://www.google.com/books?id=" + volumeId + "&jscmd=SearchWithinVolume2&q=" + query;
} else {
uri = "http://www.google.com/books?vid=isbn" + isbn + "&jscmd=SearchWithinVolume2&q=" + query;
}
try {
String content = HttpHelper.downloadViaHttp(uri, HttpHelper.ContentType.JSON);
JSONObject json = new JSONObject(content);
Message message = Message.obtain(handler, R.id.search_book_contents_succeeded);
message.obj = json;
message.sendToTarget();
} catch (IOException ioe) {
Message message = Message.obtain(handler, R.id.search_book_contents_failed);
message.sendToTarget();
}
} catch (JSONException je) {
Log.w(TAG, "Error accessing book search", je);
Message message = Message.obtain(handler, R.id.search_book_contents_failed);
message.sendToTarget();
}
}
}

View file

@ -31,19 +31,10 @@ import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpUriRequest;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
@ -51,7 +42,6 @@ import java.util.regex.Pattern;
import com.google.zxing.client.android.LocaleManager;
import com.google.zxing.client.android.R;
import com.google.zxing.client.android.Intents;
import com.google.zxing.client.android.AndroidHttpClient;
/**
* Uses Google Book Search to find a word or phrase in the requested book.
@ -59,16 +49,16 @@ import com.google.zxing.client.android.AndroidHttpClient;
* @author dswitkin@google.com (Daniel Switkin)
*/
public final class SearchBookContentsActivity extends Activity {
private static final String TAG = SearchBookContentsActivity.class.getSimpleName();
private static final String USER_AGENT = "ZXing (Android)";
private static final Pattern TAG_PATTERN = Pattern.compile("\\<.*?\\>");
private static final Pattern LT_ENTITY_PATTERN = Pattern.compile("&lt;");
private static final Pattern GT_ENTITY_PATTERN = Pattern.compile("&gt;");
private static final Pattern QUOTE_ENTITY_PATTERN = Pattern.compile("&#39;");
private static final Pattern QUOT_ENTITY_PATTERN = Pattern.compile("&quot;");
private NetworkThread networkThread;
private Thread networkThread;
private String isbn;
private EditText queryTextView;
private Button queryButton;
@ -169,7 +159,7 @@ public final class SearchBookContentsActivity extends Activity {
if (networkThread == null) {
String query = queryTextView.getText().toString();
if (query != null && query.length() > 0) {
networkThread = new NetworkThread(isbn, query, handler);
networkThread = new Thread(new NetworkWorker(isbn, query, handler));
networkThread.start();
headerView.setText(R.string.msg_sbc_searching_book);
resultListView.setAdapter(null);
@ -240,105 +230,4 @@ public final class SearchBookContentsActivity extends Activity {
}
}
private static final class NetworkThread extends Thread {
private final String isbn;
private final String query;
private final Handler handler;
NetworkThread(String isbn, String query, Handler handler) {
this.isbn = isbn;
this.query = query;
this.handler = handler;
}
@Override
public void run() {
AndroidHttpClient client = null;
try {
// These return a JSON result which describes if and where the query was found. This API may
// break or disappear at any time in the future. Since this is an API call rather than a
// website, we don't use LocaleManager to change the TLD.
URI uri;
if (LocaleManager.isBookSearchUrl(isbn)) {
int equals = isbn.indexOf('=');
String volumeId = isbn.substring(equals + 1);
uri = new URI("http", null, "www.google.com", -1, "/books", "id=" + volumeId +
"&jscmd=SearchWithinVolume2&q=" + query, null);
} else {
uri = new URI("http", null, "www.google.com", -1, "/books", "vid=isbn" + isbn +
"&jscmd=SearchWithinVolume2&q=" + query, null);
}
HttpUriRequest get = new HttpGet(uri);
get.setHeader("cookie", getCookie(uri.toString()));
client = AndroidHttpClient.newInstance(USER_AGENT);
HttpResponse response = client.execute(get);
if (response.getStatusLine().getStatusCode() == 200) {
HttpEntity entity = response.getEntity();
ByteArrayOutputStream jsonHolder = new ByteArrayOutputStream();
entity.writeTo(jsonHolder);
jsonHolder.flush();
JSONObject json = new JSONObject(jsonHolder.toString(getEncoding(entity)));
jsonHolder.close();
Message message = Message.obtain(handler, R.id.search_book_contents_succeeded);
message.obj = json;
message.sendToTarget();
} else {
Log.w(TAG, "HTTP returned " + response.getStatusLine().getStatusCode() + " for " + uri);
Message message = Message.obtain(handler, R.id.search_book_contents_failed);
message.sendToTarget();
}
} catch (Exception e) {
Log.w(TAG, "Error accessing book search", e);
Message message = Message.obtain(handler, R.id.search_book_contents_failed);
message.sendToTarget();
} finally {
if (client != null) {
client.close();
}
}
}
// Book Search requires a cookie to work, which we store persistently. If the cookie does
// not exist, this could be the first search or it has expired. Either way, do a quick HEAD
// request to fetch it, save it via the CookieSyncManager to flash, then return it.
private static String getCookie(String url) {
String cookie = CookieManager.getInstance().getCookie(url);
if (cookie == null || cookie.length() == 0) {
Log.d(TAG, "Book Search cookie was missing or expired");
HttpUriRequest head = new HttpHead(url);
AndroidHttpClient client = AndroidHttpClient.newInstance(USER_AGENT);
try {
HttpResponse response = client.execute(head);
if (response.getStatusLine().getStatusCode() == 200) {
Header[] cookies = response.getHeaders("set-cookie");
for (Header theCookie : cookies) {
CookieManager.getInstance().setCookie(url, theCookie.getValue());
}
CookieSyncManager.getInstance().sync();
cookie = CookieManager.getInstance().getCookie(url);
}
} catch (IOException e) {
Log.w(TAG, "Error setting book search cookie", e);
}
client.close();
}
return cookie;
}
private static String getEncoding(HttpEntity entity) {
// FIXME: The server is returning ISO-8859-1 but the content is actually windows-1252.
// Once Jeff fixes the HTTP response, remove this hardcoded value and go back to getting
// the encoding dynamically.
return "windows-1252";
// HeaderElement[] elements = entity.getContentType().getElements();
// if (elements != null && elements.length > 0) {
// String encoding = elements[0].getParameterByName("charset").getValue();
// if (encoding != null && encoding.length() > 0) {
// return encoding;
// }
// }
// return "UTF-8";
}
}
}

View file

@ -22,6 +22,7 @@ import java.util.Collection;
import android.content.Context;
import android.widget.TextView;
import com.google.zxing.client.android.HttpHelper;
import com.google.zxing.client.android.LocaleManager;
import com.google.zxing.client.android.R;
import org.json.JSONArray;
@ -58,7 +59,8 @@ public final class BookResultInfoRetriever extends SupplementalInfoRetriever {
@Override
void retrieveSupplementalInfo() throws IOException, InterruptedException {
String contents = downloadViaHttp("https://www.googleapis.com/books/v1/volumes?q=isbn:" + isbn);
String contents = HttpHelper.downloadViaHttp("https://www.googleapis.com/books/v1/volumes?q=isbn:" + isbn,
HttpHelper.ContentType.JSON);
if (contents.length() == 0) {
return;

View file

@ -20,6 +20,7 @@ import android.content.Context;
import android.os.Handler;
import android.text.Html;
import android.widget.TextView;
import com.google.zxing.client.android.HttpHelper;
import com.google.zxing.client.android.R;
import com.google.zxing.client.android.history.HistoryManager;
import com.google.zxing.client.android.LocaleManager;
@ -56,7 +57,7 @@ final class ProductResultInfoRetriever extends SupplementalInfoRetriever {
String encodedProductID = URLEncoder.encode(productID, "UTF-8");
String uri = BASE_PRODUCT_URI + encodedProductID;
String content = downloadViaHttp(uri);
String content = HttpHelper.downloadViaHttp(uri, HttpHelper.ContentType.HTML);
Matcher matcher = PRODUCT_NAME_PRICE_PATTERN.matcher(content);
if (matcher.find()) {

View file

@ -24,21 +24,13 @@ import android.text.Spanned;
import android.text.method.LinkMovementMethod;
import android.text.style.URLSpan;
import android.widget.TextView;
import com.google.zxing.client.android.AndroidHttpClient;
import com.google.zxing.client.result.ISBNParsedResult;
import com.google.zxing.client.result.ParsedResult;
import com.google.zxing.client.result.ProductParsedResult;
import com.google.zxing.client.result.URIParsedResult;
import com.google.zxing.client.android.history.HistoryManager;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
@ -156,42 +148,4 @@ public abstract class SupplementalInfoRetriever implements Callable<Void> {
historyManager.addHistoryItemDetails(itemID, newText);
}
protected static String downloadViaHttp(String uri) throws IOException {
HttpUriRequest get = new HttpGet(uri);
AndroidHttpClient client = AndroidHttpClient.newInstance(null);
HttpResponse response = client.execute(get);
int status = response.getStatusLine().getStatusCode();
if (status != 200) {
throw new IOException();
}
return consume(response.getEntity());
}
private static String consume(HttpEntity entity) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
InputStream in = null;
try {
in = entity.getContent();
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = in.read(buffer)) > 0) {
out.write(buffer, 0, bytesRead);
}
} finally {
if (in != null) {
try {
in.close();
} catch (IOException ioe) {
// continue
}
}
}
try {
return new String(out.toByteArray(), "UTF-8");
} catch (UnsupportedEncodingException uee) {
// can't happen
throw new IllegalStateException(uee);
}
}
}

View file

@ -19,26 +19,18 @@ package com.google.zxing.client.android.result.supplement;
import android.content.Context;
import android.os.Handler;
import android.widget.TextView;
import com.google.zxing.client.android.HttpHelper;
import com.google.zxing.client.android.history.HistoryManager;
import com.google.zxing.client.android.AndroidHttpClient;
import com.google.zxing.client.android.R;
import com.google.zxing.client.result.URIParsedResult;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpUriRequest;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
final class URIResultInfoRetriever extends SupplementalInfoRetriever {
private static final String[] REDIRECTOR_HOSTS = {
"http://bit.ly/",
"http://tinyurl.com/",
"http://tr.im/",
"http://goo.gl/",
"http://ow.ly/",
};
private static final int MAX_REDIRECTS = 5;
private final URIParsedResult result;
private final String redirectString;
@ -55,44 +47,22 @@ final class URIResultInfoRetriever extends SupplementalInfoRetriever {
@Override
void retrieveSupplementalInfo() throws IOException, InterruptedException {
String oldURI = result.getURI();
String newURI = unredirect(oldURI);
URI oldURI;
try {
oldURI = new URI(result.getURI());
} catch (URISyntaxException e) {
return;
}
URI newURI = HttpHelper.unredirect(oldURI);
int count = 0;
while (count < 3 && !oldURI.equals(newURI)) {
append(result.getDisplayResult(), null, new String[] { redirectString + " : " + newURI }, newURI);
count++;
while (count++ < MAX_REDIRECTS && !oldURI.equals(newURI)) {
append(result.getDisplayResult(),
null,
new String[] { redirectString + " : " + newURI },
newURI.toString());
oldURI = newURI;
newURI = unredirect(newURI);
newURI = HttpHelper.unredirect(newURI);
}
}
private static String unredirect(String uri) throws IOException {
if (!isRedirector(uri)) {
return uri;
}
HttpUriRequest head = new HttpHead(uri);
AndroidHttpClient client = AndroidHttpClient.newInstance(null);
HttpResponse response = client.execute(head);
int status = response.getStatusLine().getStatusCode();
if (status == 301 || status == 302) {
Header redirect = response.getFirstHeader("Location");
if (redirect != null) {
String location = redirect.getValue();
if (location != null) {
return location;
}
}
}
return uri;
}
private static boolean isRedirector(String uri) {
for (String redirectorHost : REDIRECTOR_HOSTS) {
if (uri.startsWith(redirectorHost)) {
return true;
}
}
return false;
}
}