package com.litesuits.http.impl.huc; import android.util.Log; import com.litesuits.http.HttpClient; import com.litesuits.http.HttpConfig; import com.litesuits.http.data.Charsets; import com.litesuits.http.data.Consts; import com.litesuits.http.data.HttpStatus; import com.litesuits.http.data.NameValuePair; import com.litesuits.http.exception.*; import com.litesuits.http.listener.StatisticsListener; import com.litesuits.http.log.HttpLog; import com.litesuits.http.parser.DataParser; import com.litesuits.http.request.AbstractRequest; import com.litesuits.http.request.content.HttpBody; import com.litesuits.http.request.param.HttpMethods; import com.litesuits.http.response.InternalResponse; import javax.net.ssl.*; import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; import java.io.OutputStream; import java.net.*; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; /** * @author 氢一 @http://def.so * @date 2016-04-03 */ public class HttpUrlClient implements HttpClient { private static final String TAG = "HttpUrlClient"; private SSLSocketFactory sslSocketFactory; private HostnameVerifier hostnameVerifier; public SSLSocketFactory getSslSocketFactory() { return sslSocketFactory; } public HttpUrlClient setSslSocketFactory(SSLSocketFactory sslSocketFactory) { this.sslSocketFactory = sslSocketFactory; return this; } public HostnameVerifier getHostnameVerifier() { return hostnameVerifier; } public HttpUrlClient setHostnameVerifier(HostnameVerifier hostnameVerifier) { this.hostnameVerifier = hostnameVerifier; return this; } @Override public void config(HttpConfig config) { // do nothing } @Override public <T> void connect(AbstractRequest<T> request, InternalResponse response) throws HttpClientException, IOException, URISyntaxException, HttpNetException, HttpServerException { InputStream inputStream = null; int maxRedirectTimes = request.getMaxRedirectTimes(); StatisticsListener statistic = response.getStatistics(); try { // 0. build URL URL url = new URL(request.createFullUri()); // 1. open connection and set SSL factory and hostname verifier. HttpURLConnection connection; if (Consts.SCHEME_HTTPS.equals(url.getProtocol())) { HttpsURLConnection httpsConn; if (sslSocketFactory == null) { trustAllCertificate(); httpsConn = (HttpsURLConnection) url.openConnection(); } else { httpsConn = (HttpsURLConnection) url.openConnection(); httpsConn.setSSLSocketFactory(sslSocketFactory); } if (hostnameVerifier != null) { httpsConn.setHostnameVerifier(hostnameVerifier); } else { httpsConn.setHostnameVerifier(trustAllVerifier()); } connection = httpsConn; } else { connection = (HttpURLConnection) url.openConnection(); } // 3. set connection parameters. connection.setDoInput(true); connection.setUseCaches(false); connection.setInstanceFollowRedirects(false); connection.setReadTimeout(request.getSocketTimeout()); connection.setConnectTimeout(request.getConnectTimeout()); // 3. update http header if (request.getHeaders() != null) { Set<Map.Entry<String, String>> set = request.getHeaders().entrySet(); for (Map.Entry<String, String> en : set) { connection.addRequestProperty(en.getKey(), en.getValue()); } } // 4. set method and user-agent connection.setRequestMethod(request.getMethod().getMethodName()); connection.setRequestProperty(Consts.USER_AGENT, HttpConfig.userAgent); // 5. write data and get input stream if (statistic != null) { statistic.onPreConnect(request); } try { writeDataIfNecessary(connection, request); inputStream = connection.getInputStream(); } catch (SocketTimeoutException e) { throw e; } catch (InterruptedIOException e) { Log.w(TAG, TAG + " InterruptedIOException ", e); // when thread interrupted, get an InterruptedIOException. // but sometimes Thread.interrupted() is false, so we cancel it now. request.cancel(); } catch (IOException e) { Log.w(TAG, TAG + " IOException ", e); inputStream = connection.getErrorStream(); } finally { if (statistic != null) { statistic.onAfterConnect(request); } } // if input stream is null if (inputStream == null) { throw new HttpNetException(NetException.NetworkUnreachable); } // 6. handle http status code int statusCode = connection.getResponseCode(); HttpStatus httpStatus = new HttpStatus(statusCode, connection.getResponseMessage()); response.setHttpStatus(httpStatus); // 7. handle headers and content info ArrayList<NameValuePair> headers = new ArrayList<NameValuePair>(); for (Map.Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) { if (header.getKey() != null) { List<String> values = header.getValue(); if (values != null) { for (String value : values) { headers.add(new NameValuePair(header.getKey(), value)); //if (Consts.CONTENT_LEN.equalsIgnoreCase(header.getKey())) { // response.setContentLength(Long.parseLong(value)); //} else if (Consts.CONTENT_TYPE.equalsIgnoreCase(header.getKey())) { // response.setContentType(value); //} else if (Consts.CONTENT_ENCODING.equalsIgnoreCase(header.getKey())) { // response.setContentEncoding(value); //} } } } } response.setHeaders(headers); response.setContentLength(connection.getContentLength()); response.setContentEncoding(connection.getContentEncoding()); response.setContentType(connection.getContentType()); // 8. is cancelled ? if (request.isCancelledOrInterrupted()) { return; } // 9. handle data body by status code if (statusCode <= 299 || statusCode == 600) { // 9.1 get charset and length String charSet = getCharsetByContentType(response.getContentType(), request.getCharSet()); response.setCharSet(charSet); long len = response.getContentLength(); // 9.2 read and parse stream if (statistic != null) { statistic.onPreRead(request); } DataParser<T> parser = request.getDataParser(); parser.readFromNetStream(inputStream, len, charSet); if (statistic != null) { statistic.onAfterRead(request); } // 9.3 data parser has closed input stream inputStream = null; // 9.4 set readed length response.setReadedLength(parser.getReadedLength()); } else if (statusCode <= 399) { // 10. handle redirect if (response.getRedirectTimes() < maxRedirectTimes) { // get the location header to find out where to redirect to String location = connection.getHeaderField(Consts.REDIRECT_LOCATION); if (location != null && location.length() > 0) { if (!location.toLowerCase().startsWith("http")) { URI uri = new URI(request.createFullUri()); URI redirect = new URI(uri.getScheme(), uri.getHost(), location, null); location = redirect.toString(); } response.setRedirectTimes(response.getRedirectTimes() + 1); request.setUri(location); if (HttpLog.isPrint) { HttpLog.i(TAG, "Redirect to : " + location); } if (request.getHttpListener() != null) { request.getHttpListener().notifyCallRedirect( request, maxRedirectTimes, response.getRedirectTimes()); } connect(request, response); return; } throw new HttpServerException(httpStatus); } else { throw new HttpServerException(ServerException.RedirectTooMuch); } } else if (statusCode <= 499) { // 客户端被拒 throw new HttpServerException(httpStatus); } else if (statusCode < 599) { // 服务器有误 throw new HttpServerException(httpStatus); } } finally { if (inputStream != null) { inputStream.close(); } } } private String getCharsetByContentType(String contentType, String defCharset) { if (contentType != null) { String[] values = contentType.split(";"); // values.length should be 2 for (String value : values) { value = value.trim(); if (value.toLowerCase().startsWith("charset=")) { return value.substring("charset=".length()); } } } return defCharset == null ? Charsets.UTF_8 : defCharset; } private void writeDataIfNecessary(HttpURLConnection connection, AbstractRequest<?> request) throws IOException { HttpMethods method = request.getMethod(); if (method == HttpMethods.Post || method == HttpMethods.Put || method == HttpMethods.Patch) { HttpBody body = request.getHttpBody(); if (body != null) { connection.setDoOutput(true); connection.setRequestProperty(Consts.CONTENT_TYPE, body.getContentType()); OutputStream outStream = connection.getOutputStream(); body.writeTo(outStream); outStream.close(); } } } public static void trustAllCertificate() { // Install the all-trusting trust manager try { // Create a trust manager that does not validate certificate chains // Android use X509 cert TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() { public java.security.cert.X509Certificate[] getAcceptedIssuers() { return new java.security.cert.X509Certificate[]{}; } public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } }}; SSLContext sc = SSLContext.getInstance("TLS"); sc.init(null, trustAllCerts, new java.security.SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); } catch (Exception e) { e.printStackTrace(); } } private HostnameVerifier trustAllVerifier() { return new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { return true; } }; } }