package com.github.scribejava.core.httpclient.jdk;
import com.github.scribejava.core.exceptions.OAuthException;
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.Response;
import com.github.scribejava.core.model.Verb;
import java.io.File;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
public class JDKHttpClient implements HttpClient {
private final JDKHttpClientConfig config;
public JDKHttpClient(JDKHttpClientConfig clientConfig) {
config = clientConfig;
}
@Override
public void close() throws IOException {
}
@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) {
try {
final T response = converter.convert(execute(userAgent, headers, httpVerb, completeUrl, bodyContents));
callback.onCompleted(response);
return new JDKHttpFuture<>(response);
} catch (InterruptedException | ExecutionException | IOException e) {
callback.onThrowable(e);
return new JDKHttpFuture<>(e);
}
}
@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) {
try {
final T response = converter.convert(execute(userAgent, headers, httpVerb, completeUrl, bodyContents));
if (callback != null) {
callback.onCompleted(response);
}
return new JDKHttpFuture<>(response);
} catch (InterruptedException | ExecutionException | IOException e) {
if (callback != null) {
callback.onThrowable(e);
}
return new JDKHttpFuture<>(e);
}
}
@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) {
throw new UnsupportedOperationException("JDKHttpClient do not support File payload for the moment");
}
@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 {
throw new UnsupportedOperationException("JDKHttpClient do not support File payload for the moment");
}
private Response doExecute(String userAgent, Map<String, String> headers, Verb httpVerb, String completeUrl,
BodyType bodyType, Object bodyContents) throws IOException {
final HttpURLConnection connection = (HttpURLConnection) new URL(completeUrl).openConnection();
connection.setInstanceFollowRedirects(config.isFollowRedirects());
connection.setRequestMethod(httpVerb.name());
if (config.getConnectTimeout() != null) {
connection.setConnectTimeout(config.getConnectTimeout());
}
if (config.getReadTimeout() != null) {
connection.setReadTimeout(config.getReadTimeout());
}
addHeaders(connection, headers, userAgent);
if (httpVerb.isPermitBody()) {
bodyType.setBody(connection, bodyContents);
}
try {
connection.connect();
final int responseCode = connection.getResponseCode();
return new Response(responseCode, connection.getResponseMessage(), parseHeaders(connection),
responseCode >= 200 && responseCode < 400 ? connection.getInputStream()
: connection.getErrorStream());
} catch (UnknownHostException e) {
throw new OAuthException("The IP address of a host could not be determined.", e);
}
}
private enum BodyType {
BYTE_ARRAY {
@Override
void setBody(HttpURLConnection connection, Object bodyContents) throws IOException {
addBody(connection, (byte[]) bodyContents);
}
},
STRING {
@Override
void setBody(HttpURLConnection connection, Object bodyContents) throws IOException {
addBody(connection, ((String) bodyContents).getBytes());
}
};
abstract void setBody(HttpURLConnection connection, Object bodyContents) throws IOException;
}
private static Map<String, String> parseHeaders(HttpURLConnection conn) {
final Map<String, String> headers = new HashMap<>();
for (Map.Entry<String, List<String>> entry : conn.getHeaderFields().entrySet()) {
final String key = entry.getKey();
if ("Content-Encoding".equalsIgnoreCase(key)) {
headers.put("Content-Encoding", entry.getValue().get(0));
} else {
headers.put(key, entry.getValue().get(0));
}
}
return headers;
}
private static void addHeaders(HttpURLConnection connection, Map<String, String> headers, String userAgent) {
for (Map.Entry<String, String> entry : headers.entrySet()) {
connection.setRequestProperty(entry.getKey(), entry.getValue());
}
if (userAgent != null) {
connection.setRequestProperty(OAuthConstants.USER_AGENT_HEADER_NAME, userAgent);
}
}
private static void addBody(HttpURLConnection connection, byte[] content) throws IOException {
connection.setRequestProperty(CONTENT_LENGTH, String.valueOf(content.length));
if (connection.getRequestProperty(CONTENT_TYPE) == null) {
connection.setRequestProperty(CONTENT_TYPE, DEFAULT_CONTENT_TYPE);
}
connection.setDoOutput(true);
connection.getOutputStream().write(content);
}
}