All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.jpmorganchase.fusion.http.JdkClient Maven / Gradle / Ivy

package io.github.jpmorganchase.fusion.http;

import java.io.*;
import java.lang.invoke.MethodHandles;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import lombok.Builder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Builder
public class JdkClient implements Client {

    private static final Logger logger =
            LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
    private static final String DEFAULT_ERROR = "{\"error\": \"Unable to perform requested action\"}";
    public static final String METHOD_GET = "GET";
    public static final String METHOD_POST = "POST";
    public static final String METHOD_PUT = "PUT";
    public static final String METHOD_DELETE = "DELETE";
    private final Proxy proxy;

    @Override
    public HttpResponse get(String path, Map headers) {
        return executeMethod(METHOD_GET, path, headers);
    }

    @Override
    public HttpResponse getInputStream(String path, Map headers) {
        return executeMethod(METHOD_GET, path, headers, null, false, HttpConnectionInputStream::new);
    }

    @Override
    public HttpResponse post(String path, Map headers, String body) {
        return executeMethod(METHOD_POST, path, headers, body);
    }

    @Override
    public HttpResponse put(String path, Map headers, InputStream body) {
        if (body == null) {
            throw new ClientException("No request body specified for PUT operation");
        }
        return executeMethod(METHOD_PUT, path, headers, body, true, this::getResponseBody);
    }

    @Override
    public HttpResponse delete(String path, Map headers, String body) {
        return executeMethod(METHOD_DELETE, path, headers, body);
    }

    private HttpResponse executeMethod(String method, String path, Map headers) {
        return executeMethod(method, path, headers, null);
    }

    private HttpResponse executeMethod(String method, String path, Map headers, String body) {
        InputStream bodyAsStream =
                body != null ? new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8)) : null;
        return executeMethod(method, path, headers, bodyAsStream, true, this::getResponseBody);
    }

    private  HttpResponse executeMethod(
            String method,
            String path,
            Map headers,
            InputStream body,
            boolean closeConnection,
            Function resultMapper) {
        URL url = parseUrl(path);
        HttpURLConnection connection = openConnection(url);
        headers.forEach(connection::setRequestProperty);
        connection.setRequestProperty("User-Agent", UserAgentGenerator.getUserAgentString(this.getClass()));

        try {
            int httpCode;
            logRequest(connection, method);
            if (body != null) {
                httpCode = executeRequestWithBody(connection, method, body);
            } else {
                httpCode = executeRequest(connection, method);
            }

            HttpResponse response = HttpResponse.builder()
                    .body(resultMapper.apply(connection))
                    .headers(connection.getHeaderFields())
                    .statusCode(httpCode)
                    .build();
            logger.debug("Response: {}", response);
            return response;
        } finally {
            if (closeConnection) {
                connection.disconnect();
            }
        }
    }

    private URL parseUrl(String path) {
        try {
            return new URL(path);
        } catch (MalformedURLException e) {
            throw new ClientException(String.format("Malformed URL path received: %s", path), e);
        }
    }

    private HttpURLConnection openConnection(URL url) {
        try {
            return (HttpURLConnection) url.openConnection(proxy);
        } catch (IOException e) {
            throw new ClientException("Error establishing HTTP connection", e);
        }
    }

    private String getResponseBody(HttpURLConnection connection) {
        BufferedReader reader =
                new BufferedReader(new InputStreamReader(getResponseStream(connection), StandardCharsets.UTF_8));
        StringWriter out = new StringWriter(connection.getContentLength() > 0 ? connection.getContentLength() : 2048);

        try {
            String line;
            while ((line = reader.readLine()) != null) {
                out.append(line);
            }
            reader.close();
        } catch (IOException e) {
            throw new ClientException("Failed to read data from response", e);
        }
        return out.toString();
    }

    private InputStream getResponseStream(HttpURLConnection connection) {
        try {
            int httpCode = connection.getResponseCode();
            if (100 <= httpCode && httpCode <= 399) {
                return connection.getInputStream();
            } else {
                return errorStream(connection);
            }
        } catch (IOException e) {
            throw new ClientException("Failed to get InputStream from response", e);
        }
    }

    private InputStream errorStream(final HttpURLConnection connection) {
        if (null == connection.getErrorStream()) {
            return new ByteArrayInputStream(DEFAULT_ERROR.getBytes(StandardCharsets.UTF_8));
        }
        return connection.getErrorStream();
    }

    private int executeRequest(HttpURLConnection connection, String httpMethod) {
        try {
            connection.setRequestMethod(httpMethod);
            return connection.getResponseCode();
        } catch (IOException e) {
            throw new ClientException("Error performing HTTP operation", e);
        }
    }

    private int executeRequestWithBody(HttpURLConnection connection, String method, InputStream body) {
        try {
            connection.setDoOutput(true);
            connection.setRequestMethod(method);
            OutputStream os = connection.getOutputStream();
            byte[] buf = new byte[8192];
            int length;
            while ((length = body.read(buf)) != -1) {
                os.write(buf, 0, length);
            }
            body.close();
            return connection.getResponseCode();
        } catch (IOException e) {
            throw new ClientException("Failed to send request data", e);
        }
    }

    private void logRequest(HttpURLConnection connection, String method) {
        logger.debug("Executing {} request for URL: {}", method, connection.getURL());
    }

    public static JdkClientBuilder builder() {
        return new CustomJdkClientBuilder();
    }

    public static class JdkClientBuilder {

        String url;
        int port;
        Proxy proxy = Proxy.NO_PROXY;

        public JdkClientBuilder url(String url) {
            this.url = url;
            return this;
        }

        public JdkClientBuilder port(int port) {
            this.port = port;
            return this;
        }

        public JdkClientBuilder noProxy() {
            this.proxy = Proxy.NO_PROXY;
            return this;
        }
    }

    private static class CustomJdkClientBuilder extends JdkClientBuilder {
        @Override
        public JdkClient build() {

            if (Objects.nonNull(url)) {
                this.proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(url, port));
            }

            return super.build();
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy