package com.github.scribejava.httpclient.okhttp;
import com.github.scribejava.core.httpclient.HttpClient;
import com.github.scribejava.core.model.OAuthAsyncRequestCallback;
import com.github.scribejava.core.model.OAuthConstants;
import com.github.scribejava.core.model.OAuthRequest;
import com.github.scribejava.core.model.Verb;
import okhttp3.Call;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.internal.http.HttpMethod;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.Future;
import com.github.scribejava.core.model.Response;
import java.io.File;
import java.util.HashMap;
import java.util.concurrent.ExecutionException;
import okhttp3.Cache;
import okhttp3.Headers;
public class OkHttpHttpClient implements HttpClient {
private static final MediaType DEFAULT_CONTENT_TYPE_MEDIA_TYPE = MediaType.parse(DEFAULT_CONTENT_TYPE);
private final OkHttpClient client;
public OkHttpHttpClient(OkHttpHttpClientConfig config) {
final OkHttpClient.Builder clientBuilder = config.getClientBuilder();
client = clientBuilder == null ? new OkHttpClient() : clientBuilder.build();
}
public OkHttpHttpClient(OkHttpClient client) {
this.client = client;
}
@Override
public void close() throws IOException {
client.dispatcher().executorService().shutdown();
client.connectionPool().evictAll();
final Cache cache = client.cache();
if (cache != null) {
cache.close();
}
}
@Override
public <T> Future<T> executeAsync(String userAgent, Map<String, String> headers, Verb httpVerb, String completeUrl,
byte[] bodyContents, OAuthAsyncRequestCallback<T> callback, OAuthRequest.ResponseConverter<T> converter) {
return doExecuteAsync(userAgent, headers, httpVerb, completeUrl, BodyType.BYTE_ARRAY, bodyContents, callback,
converter);
}
@Override
public <T> Future<T> executeAsync(String userAgent, Map<String, String> headers, Verb httpVerb, String completeUrl,
String bodyContents, OAuthAsyncRequestCallback<T> callback, OAuthRequest.ResponseConverter<T> converter) {
return doExecuteAsync(userAgent, headers, httpVerb, completeUrl, BodyType.STRING, bodyContents, callback,
converter);
}
@Override
public <T> Future<T> executeAsync(String userAgent, Map<String, String> headers, Verb httpVerb, String completeUrl,
File bodyContents, OAuthAsyncRequestCallback<T> callback, OAuthRequest.ResponseConverter<T> converter) {
return doExecuteAsync(userAgent, headers, httpVerb, completeUrl, BodyType.FILE, bodyContents, callback,
converter);
}
private <T> Future<T> doExecuteAsync(String userAgent, Map<String, String> headers, Verb httpVerb,
String completeUrl, BodyType bodyType, Object bodyContents, OAuthAsyncRequestCallback<T> callback,
OAuthRequest.ResponseConverter<T> converter) {
final Call call = createCall(userAgent, headers, httpVerb, completeUrl, bodyType, bodyContents);
final OkHttpFuture<T> okHttpFuture = new OkHttpFuture<>(call);
call.enqueue(new OAuthAsyncCompletionHandler<>(callback, converter, okHttpFuture));
return okHttpFuture;
}
@Override
public Response execute(String userAgent, Map<String, String> headers, Verb httpVerb, String completeUrl,
byte[] bodyContents) throws InterruptedException, ExecutionException, IOException {
return doExecute(userAgent, headers, httpVerb, completeUrl, BodyType.BYTE_ARRAY, bodyContents);
}
@Override
public Response execute(String userAgent, Map<String, String> headers, Verb httpVerb, String completeUrl,
String bodyContents) throws InterruptedException, ExecutionException, IOException {
return doExecute(userAgent, headers, httpVerb, completeUrl, BodyType.STRING, bodyContents);
}
@Override
public Response execute(String userAgent, Map<String, String> headers, Verb httpVerb, String completeUrl,
File bodyContents) throws InterruptedException, ExecutionException, IOException {
return doExecute(userAgent, headers, httpVerb, completeUrl, BodyType.FILE, bodyContents);
}
private Response doExecute(String userAgent, Map<String, String> headers, Verb httpVerb, String completeUrl,
BodyType bodyType, Object bodyContents) throws IOException {
final Call call = createCall(userAgent, headers, httpVerb, completeUrl, bodyType, bodyContents);
return convertResponse(call.execute());
}
private Call createCall(String userAgent, Map<String, String> headers, Verb httpVerb, String completeUrl,
BodyType bodyType, Object bodyContents) {
final Request.Builder requestBuilder = new Request.Builder();
requestBuilder.url(completeUrl);
final String method = httpVerb.name();
// prepare body
final RequestBody body;
if (bodyContents != null && HttpMethod.permitsRequestBody(method)) {
final MediaType mediaType = headers.containsKey(CONTENT_TYPE) ? MediaType.parse(headers.get(CONTENT_TYPE))
: DEFAULT_CONTENT_TYPE_MEDIA_TYPE;
body = bodyType.createBody(mediaType, bodyContents);
} else {
body = null;
}
// fill HTTP method and body
requestBuilder.method(method, body);
// fill headers
for (Map.Entry<String, String> header : headers.entrySet()) {
requestBuilder.addHeader(header.getKey(), header.getValue());
}
if (userAgent != null) {
requestBuilder.header(OAuthConstants.USER_AGENT_HEADER_NAME, userAgent);
}
// create a new call
return client.newCall(requestBuilder.build());
}
private enum BodyType {
BYTE_ARRAY {
@Override
RequestBody createBody(MediaType mediaType, Object bodyContents) {
return RequestBody.create(mediaType, (byte[]) bodyContents);
}
},
STRING {
@Override
RequestBody createBody(MediaType mediaType, Object bodyContents) {
return RequestBody.create(mediaType, (String) bodyContents);
}
},
FILE {
@Override
RequestBody createBody(MediaType mediaType, Object bodyContents) {
return RequestBody.create(mediaType, (File) bodyContents);
}
};
abstract RequestBody createBody(MediaType mediaType, Object bodyContents);
}
static Response convertResponse(okhttp3.Response okHttpResponse) {
final Headers headers = okHttpResponse.headers();
final Map<String, String> headersMap = new HashMap<>();
for (String name : headers.names()) {
headersMap.put(name, headers.get(name));
}
return new Response(okHttpResponse.code(), okHttpResponse.message(), headersMap,
okHttpResponse.body().byteStream());
}
}