11ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath/* 21ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath * Copyright (C) 2010 The Android Open Source Project 31ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath * 41ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath * Licensed under the Apache License, Version 2.0 (the "License"); 51ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath * you may not use this file except in compliance with the License. 61ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath * You may obtain a copy of the License at 71ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath * 81ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath * http://www.apache.org/licenses/LICENSE-2.0 91ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath * 101ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath * Unless required by applicable law or agreed to in writing, software 111ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath * distributed under the License is distributed on an "AS IS" BASIS, 121ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 131ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath * See the License for the specific language governing permissions and 141ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath * limitations under the License. 151ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath */ 161ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath 171ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamathpackage com.android.quicksearchbox.util; 181ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath 191ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamathimport android.os.Build; 201ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamathimport android.util.Log; 211ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath 221ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamathimport java.io.BufferedReader; 231ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamathimport java.io.IOException; 241ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamathimport java.io.InputStreamReader; 251ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamathimport java.io.OutputStreamWriter; 261ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamathimport java.net.HttpURLConnection; 271ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamathimport java.net.URL; 281ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamathimport java.util.HashMap; 291ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamathimport java.util.Map; 301ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath 311ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath/** 321ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath * Simple HTTP client API. 331ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath */ 341ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamathpublic class JavaNetHttpHelper implements HttpHelper { 351ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath private static final String TAG = "QSB.JavaNetHttpHelper"; 361ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath private static final boolean DBG = false; 371ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath 381ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath private static final int BUFFER_SIZE = 1024 * 4; 391ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath private static final String USER_AGENT_HEADER = "User-Agent"; 401ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath private static final String DEFAULT_CHARSET = "UTF-8"; 411ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath 421ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath private int mConnectTimeout; 431ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath private int mReadTimeout; 441ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath private final String mUserAgent; 451ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath private final HttpHelper.UrlRewriter mRewriter; 461ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath 471ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath /** 481ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath * Creates a new HTTP helper. 491ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath * 501ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath * @param rewriter URI rewriter 511ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath * @param userAgent User agent string, e.g. "MyApp/1.0". 521ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath */ 531ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath public JavaNetHttpHelper(UrlRewriter rewriter, String userAgent) { 541ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath mUserAgent = userAgent + " (" + Build.DEVICE + " " + Build.ID + ")"; 551ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath mRewriter = rewriter; 561ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath } 571ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath 581ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath /** 591ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath * Executes a GET request and returns the response content. 601ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath * 611ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath * @param request Request. 621ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath * @return The response content. This is the empty string if the response 631ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath * contained no content. 641ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath * @throws IOException If an IO error occurs. 651ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath * @throws HttpException If the response has a status code other than 200. 661ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath */ 671ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath public String get(GetRequest request) throws IOException, HttpException { 681ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath return get(request.getUrl(), request.getHeaders()); 691ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath } 701ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath 711ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath /** 721ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath * Executes a GET request and returns the response content. 731ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath * 741ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath * @param url Request URI. 751ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath * @param requestHeaders Request headers. 761ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath * @return The response content. This is the empty string if the response 771ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath * contained no content. 781ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath * @throws IOException If an IO error occurs. 791ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath * @throws HttpException If the response has a status code other than 200. 801ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath */ 811ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath public String get(String url, Map<String,String> requestHeaders) 821ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath throws IOException, HttpException { 831ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath HttpURLConnection c = null; 841ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath try { 851ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath c = createConnection(url, requestHeaders); 861ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath c.setRequestMethod("GET"); 871ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath c.connect(); 881ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath return getResponseFrom(c); 891ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath } finally { 901ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath if (c != null) { 911ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath c.disconnect(); 921ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath } 931ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath } 941ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath } 951ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath 961ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath @Override 971ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath public String post(PostRequest request) throws IOException, HttpException { 981ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath return post(request.getUrl(), request.getHeaders(), request.getContent()); 991ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath } 1001ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath 1011ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath public String post(String url, Map<String,String> requestHeaders, String content) 1021ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath throws IOException, HttpException { 1031ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath HttpURLConnection c = null; 1041ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath try { 1051ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath if (requestHeaders == null) { 1061ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath requestHeaders = new HashMap<String, String>(); 1071ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath } 1081ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath requestHeaders.put("Content-Length", 1091ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath Integer.toString(content == null ? 0 : content.length())); 1101ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath c = createConnection(url, requestHeaders); 1111ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath c.setDoOutput(content != null); 1121ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath c.setRequestMethod("POST"); 1131ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath c.connect(); 1141ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath if (content != null) { 1151ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath OutputStreamWriter writer = new OutputStreamWriter(c.getOutputStream()); 1161ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath writer.write(content); 1171ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath writer.close(); 1181ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath } 1191ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath return getResponseFrom(c); 1201ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath } finally { 1211ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath if (c != null) { 1221ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath c.disconnect(); 1231ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath } 1241ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath } 1251ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath } 1261ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath 1271ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath private HttpURLConnection createConnection(String url, Map<String, String> headers) 1281ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath throws IOException, HttpException { 1291ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath URL u = new URL(mRewriter.rewrite(url)); 1301ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath if (DBG) Log.d(TAG, "URL=" + url + " rewritten='" + u + "'"); 1311ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath HttpURLConnection c = (HttpURLConnection) u.openConnection(); 1321ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath if (headers != null) { 1331ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath for (Map.Entry<String,String> e : headers.entrySet()) { 1341ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath String name = e.getKey(); 1351ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath String value = e.getValue(); 1361ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath if (DBG) Log.d(TAG, " " + name + ": " + value); 1371ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath c.addRequestProperty(name, value); 1381ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath } 1391ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath } 1401ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath c.addRequestProperty(USER_AGENT_HEADER, mUserAgent); 1411ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath if (mConnectTimeout != 0) { 1421ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath c.setConnectTimeout(mConnectTimeout); 1431ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath } 1441ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath if (mReadTimeout != 0) { 1451ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath c.setReadTimeout(mReadTimeout); 1461ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath } 1471ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath return c; 1481ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath } 1491ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath 1501ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath private String getResponseFrom(HttpURLConnection c) throws IOException, HttpException { 1511ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath if (c.getResponseCode() != HttpURLConnection.HTTP_OK) { 1521ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath throw new HttpException(c.getResponseCode(), c.getResponseMessage()); 1531ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath } 1541ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath if (DBG) { 1551ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath Log.d(TAG, "Content-Type: " + c.getContentType() + " (assuming " + 1561ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath DEFAULT_CHARSET + ")"); 1571ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath } 1581ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath BufferedReader reader = new BufferedReader( 1591ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath new InputStreamReader(c.getInputStream(), DEFAULT_CHARSET)); 1601ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath StringBuilder string = new StringBuilder(); 1611ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath char[] chars = new char[BUFFER_SIZE]; 1621ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath int bytes; 1631ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath while ((bytes = reader.read(chars)) != -1) { 1641ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath string.append(chars, 0, bytes); 1651ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath } 1661ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath return string.toString(); 1671ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath } 1681ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath 1691ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath public void setConnectTimeout(int timeoutMillis) { 1701ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath mConnectTimeout = timeoutMillis; 1711ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath } 1721ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath 1731ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath public void setReadTimeout(int timeoutMillis) { 1741ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath mReadTimeout = timeoutMillis; 1751ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath } 1761ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath 177ef6dbc776667219ac89bd5f80c181f65260457acNarayan Kamath /** 178ef6dbc776667219ac89bd5f80c181f65260457acNarayan Kamath * A Url rewriter that does nothing, i.e., returns the 179ef6dbc776667219ac89bd5f80c181f65260457acNarayan Kamath * url that is passed to it. 180ef6dbc776667219ac89bd5f80c181f65260457acNarayan Kamath */ 181ef6dbc776667219ac89bd5f80c181f65260457acNarayan Kamath public static class PassThroughRewriter implements UrlRewriter { 182ef6dbc776667219ac89bd5f80c181f65260457acNarayan Kamath @Override 183ef6dbc776667219ac89bd5f80c181f65260457acNarayan Kamath public String rewrite(String url) { 184ef6dbc776667219ac89bd5f80c181f65260457acNarayan Kamath return url; 185ef6dbc776667219ac89bd5f80c181f65260457acNarayan Kamath } 186ef6dbc776667219ac89bd5f80c181f65260457acNarayan Kamath } 1871ea50e57e0bb5bf1184e3c1e4fbda27c3d8e44a6Narayan Kamath} 188