package im.amomo.volley;
import com.android.volley.AuthFailureError;
import com.android.volley.Request;
import com.android.volley.Request.Method;
import com.android.volley.VolleyLog;
import com.squareup.okhttp.Dispatcher;
import com.squareup.okhttp.Interceptor;
import com.squareup.okhttp.MediaType;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.RequestBody;
import com.squareup.okhttp.Response;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
/**
* Created by GoogolMo on 10/22/13.
*/
public class OkHttpStack implements OkStack {
private final OkHttpClient mClient;
private final UrlRewriter mUrlRewriter;
/**
* An interface for transforming URLs before use.
*/
public interface UrlRewriter {
/**
* Returns a URL to use instead of the provided one, or null to indicate
* this URL should not be used at all.
*/
public String rewriteUrl(String originalUrl);
}
public OkHttpStack() {
this(null);
}
public OkHttpStack(UrlRewriter urlRewriter) {
this(urlRewriter, null);
}
public OkHttpStack(UrlRewriter urlRewriter, SSLSocketFactory sslSocketFactory) {
this.mClient = new OkHttpClient();
this.mUrlRewriter = urlRewriter;
this.mClient.setSslSocketFactory(sslSocketFactory);
}
/**
* set dispatcher to OkHttpClient
* @param dispatcher {@link OkHttpClient}.setDispatcher({@link Dispatcher})
*/
public void setDispatcher(Dispatcher dispatcher) {
if (dispatcher == null) {
return;
}
this.mClient.setDispatcher(dispatcher);
}
public void addInterceptor(Interceptor interceptor) {
if (interceptor == null) {
return;
}
this.mClient.interceptors().add(interceptor);
}
public void addNetworkInterceptor(Interceptor interceptor) {
if (interceptor == null) {
return;
}
this.mClient.networkInterceptors().add(interceptor);
}
public void removeInterceptor(Interceptor interceptor) {
if (interceptor == null) {
return;
}
this.mClient.interceptors().remove(interceptor);
}
public void removeNetworkInterceptor(Interceptor interceptor) {
if (interceptor == null) {
return;
}
this.mClient.networkInterceptors().remove(interceptor);
}
/**
* perform the request
*
* @param request request
* @param additionalHeaders headers
* @return http response
* @throws java.io.IOException
* @throws com.android.volley.AuthFailureError
*/
@Override
public Response performRequest(Request<?> request,
Map<String, String> additionalHeaders) throws IOException, AuthFailureError {
String url = request.getUrl();
HashMap<String, String> map = new HashMap<String, String>();
map.putAll(request.getHeaders());
map.putAll(additionalHeaders);
if (mUrlRewriter != null) {
String rewritten = mUrlRewriter.rewriteUrl(url);
if (rewritten == null) {
throw new IOException("URL blocked by rewriter: " + url);
}
url = rewritten;
}
com.squareup.okhttp.Request.Builder builder = new com.squareup.okhttp.Request.Builder();
builder.url(url);
for (String headerName : map.keySet()) {
builder.header(headerName, map.get(headerName));
// connection.addRequestProperty(headerName, map.get(headerName));
if (VolleyLog.DEBUG) {
// print header message
VolleyLog.d("RequestHeader: %1$s:%2$s", headerName, map.get(headerName));
}
}
setConnectionParametersForRequest(builder, request);
// Initialize HttpResponse with data from the okhttp.
Response okHttpResponse = mClient.newCall(builder.build()).execute();
int responseCode = okHttpResponse.code();
if (responseCode == -1) {
// -1 is returned by getResponseCode() if the response code could not be retrieved.
// Signal to the caller that something was wrong with the connection.
throw new IOException("Could not retrieve response code from HttpUrlConnection.");
}
return okHttpResponse;
}
/* package */
static void setConnectionParametersForRequest(com.squareup.okhttp.Request.Builder builder,
Request<?> request) throws IOException, AuthFailureError {
byte[] postBody = null;
if (VolleyLog.DEBUG) {
VolleyLog.d("request.method = %1$s", request.getMethod());
}
switch (request.getMethod()) {
case Method.DEPRECATED_GET_OR_POST:
// This is the deprecated way that needs to be handled for backwards compatibility.
// If the request's post body is null, then the assumption is that the request is
// GET. Otherwise, it is assumed that the request is a POST.
postBody = request.getBody();
if (postBody != null) {
// Prepare output. There is no need to set Content-Length explicitly,
// since this is handled by HttpURLConnection using the size of the prepared
// output stream.
builder.post(RequestBody.create(MediaType.parse(request.getBodyContentType()), postBody));
if (VolleyLog.DEBUG) {
VolleyLog.d("RequestHeader: %1$s:%2$s", OkRequest.HEADER_CONTENT_TYPE, request.getPostBodyContentType());
}
} else {
builder.get();
}
break;
case Method.GET:
// Not necessary to set the request method because connection defaults to GET but
// being explicit here.
builder.get();
break;
case Method.DELETE:
builder.delete();
break;
case Method.POST:
postBody = request.getBody();
if (postBody == null) {
builder.post(RequestBody.create(MediaType.parse(request.getBodyContentType()), ""));
} else {
builder.post(RequestBody.create(MediaType.parse(request.getBodyContentType()), postBody));
}
if (VolleyLog.DEBUG) {
VolleyLog.d("RequestHeader: %1$s:%2$s", OkRequest.HEADER_CONTENT_TYPE, request.getBodyContentType());
}
break;
case Method.PUT:
postBody = request.getBody();
if (postBody == null) {
builder.put(RequestBody.create(MediaType.parse(request.getBodyContentType()), ""));
} else {
builder.put(RequestBody.create(MediaType.parse(request.getBodyContentType()), postBody));
}
if (VolleyLog.DEBUG) {
VolleyLog.d("RequestHeader: %1$s:%2$s", OkRequest.HEADER_CONTENT_TYPE, request.getBodyContentType());
}
break;
case Method.HEAD:
builder.head();
break;
case Method.PATCH:
postBody = request.getBody();
if (postBody == null) {
builder.patch(RequestBody.create(MediaType.parse(request.getBodyContentType()), ""));
} else {
builder.patch(RequestBody.create(MediaType.parse(request.getBodyContentType()), postBody));
}
if (VolleyLog.DEBUG) {
VolleyLog.d("RequestHeader: %1$s:%2$s", OkRequest.HEADER_CONTENT_TYPE, request.getBodyContentType());
}
break;
default:
throw new IllegalStateException("Unknown method type.");
}
}
/**
* set request trust all certs include untrusts
*
* @return this http stact
*/
public OkHttpStack trustAllCerts() {
this.mClient.setSslSocketFactory(getTrustedFactory());
return this;
}
/**
* set request trust all hosts include hosts with untrusts
*
* @return
*/
public OkHttpStack trustAllHosts() {
this.mClient.setHostnameVerifier(getTrustedVerifier());
return this;
}
/**
* set custom host name verifier
*
* @param verifier verifier
* @return this http stack
*/
public OkHttpStack setHostnameVerifier(HostnameVerifier verifier) {
this.mClient.setHostnameVerifier(verifier);
return this;
}
private static SSLSocketFactory TRUSTED_FACTORY;
private static HostnameVerifier TRUSTED_VERIFIER;
private static SSLSocketFactory getTrustedFactory() {
if (TRUSTED_FACTORY == null) {
final TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
public void checkClientTrusted(X509Certificate[] chain, String authType) {
// Intentionally left blank
}
public void checkServerTrusted(X509Certificate[] chain, String authType) {
// Intentionally left blank
}
}};
try {
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, trustAllCerts, new SecureRandom());
TRUSTED_FACTORY = context.getSocketFactory();
} catch (GeneralSecurityException e) {
IOException ioException = new IOException(
"Security exception configuring SSL context");
ioException.initCause(e);
}
}
return TRUSTED_FACTORY;
}
private static HostnameVerifier getTrustedVerifier() {
if (TRUSTED_VERIFIER == null)
TRUSTED_VERIFIER = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
return TRUSTED_VERIFIER;
}
}