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

com.bandwidth.http.client.OkClient Maven / Gradle / Ivy

Go to download

The official client SDK for Bandwidth's Voice, Messaging, MFA, and WebRTC APIs

There is a newer version: 12.0.1
Show newest version
/*
 * BandwidthLib
 *
 * This file was automatically generated by APIMATIC v3.0 ( https://www.apimatic.io ).
 */

package com.bandwidth.http.client;

import com.bandwidth.ApiHelper;
import com.bandwidth.http.Headers;
import com.bandwidth.http.request.HttpBodyRequest;
import com.bandwidth.http.request.HttpMethod;
import com.bandwidth.http.request.HttpRequest;
import com.bandwidth.http.request.MultipartFileWrapper;
import com.bandwidth.http.request.MultipartWrapper;
import com.bandwidth.http.response.HttpResponse;
import com.bandwidth.http.response.HttpStringResponse;
import com.bandwidth.utilities.FileWrapper;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.AbstractMap.SimpleEntry;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

/**
 * HTTP Client class to send HTTP Requests and read the responses.
 */
public class OkClient implements HttpClient {
    private static final Object syncObject = new Object();
    private static volatile okhttp3.OkHttpClient defaultOkHttpClient;

    /**
     * Private instance of the okhttp3.OkHttpClient.
     */
    private okhttp3.OkHttpClient client;

    /**
     * Default constructor.
     * @param  httpClientConfig  The specified http client configuration.
     */
    public OkClient(ReadonlyHttpClientConfiguration httpClientConfig) {
        okhttp3.OkHttpClient httpClientInstance = httpClientConfig.getHttpClientInstance();
        if (httpClientInstance != null) {
            if (httpClientConfig.shouldOverrideHttpClientConfigurations()) {
                applyHttpClientConfigurations(httpClientInstance, httpClientConfig);
            } else {
                this.client = httpClientInstance;
            }
        } else {
            applyHttpClientConfigurations(getDefaultOkHttpClient(), httpClientConfig);
        }
    }

    /**
     * Applies the httpClientConfigurations on okhttp3.OkHttpClient.
     */
    private void applyHttpClientConfigurations(okhttp3.OkHttpClient client,
            ReadonlyHttpClientConfiguration httpClientConfig) {
        okhttp3.OkHttpClient.Builder clientBuilder = client.newBuilder();
        clientBuilder.readTimeout(httpClientConfig.getTimeout(), TimeUnit.SECONDS)
                .writeTimeout(httpClientConfig.getTimeout(), TimeUnit.SECONDS)
                .connectTimeout(httpClientConfig.getTimeout(), TimeUnit.SECONDS);
        // If retries are allowed then RetryInterceptor must be registered
        if (httpClientConfig.getNumberOfRetries() > 0) {
            clientBuilder.callTimeout(httpClientConfig.getMaximumRetryWaitTime(), TimeUnit.SECONDS)
                    .addInterceptor(new RetryInterceptor(httpClientConfig));
        } else {
            clientBuilder.callTimeout(httpClientConfig.getTimeout(), TimeUnit.SECONDS);
        }

        clientBuilder.addInterceptor(new HttpRedirectInterceptor(true));
        this.client = clientBuilder.build();
    }

    /**
     * Getter for the default static instance of the okhttp3.OkHttpClient.
     */
    private okhttp3.OkHttpClient getDefaultOkHttpClient() {
        if (defaultOkHttpClient == null) {
            synchronized (syncObject) {
                if (defaultOkHttpClient == null) {
                    defaultOkHttpClient = new okhttp3.OkHttpClient.Builder()
                            .retryOnConnectionFailure(false)
                            .callTimeout(0, TimeUnit.SECONDS)
                            .build();
                }
            }
        }
        return defaultOkHttpClient;
    }

    /**
     * Shutdown the underlying OkHttpClient instance. 
     */
    public static void shutdown() {
        if (defaultOkHttpClient != null) {
            defaultOkHttpClient.dispatcher().executorService().shutdown();
            defaultOkHttpClient.connectionPool().evictAll();
        }
    }

    /**
     * Execute a given HttpRequest to get string/binary response back.
     * @param   httpRequest        The given HttpRequest to execute.
     * @param   hasBinaryResponse  Whether the response is binary or string.
     * @return  CompletableFuture of HttpResponse after execution.
     */
    public CompletableFuture executeAsync(final HttpRequest httpRequest,
            boolean hasBinaryResponse) {
        okhttp3.Request okHttpRequest = convertRequest(httpRequest);
        
        RetryInterceptor retryInterceptor = getRetryInterceptor();
        if (retryInterceptor != null) {
            retryInterceptor.addRequestEntry(okHttpRequest);
        }

        final CompletableFuture callBack = new CompletableFuture<>();
        client.newCall(okHttpRequest).enqueue(new okhttp3.Callback() {

            public void onFailure(okhttp3.Call call, IOException e) {
                publishResponse(null, httpRequest, callBack, e, hasBinaryResponse);
            }

            public void onResponse(okhttp3.Call call, okhttp3.Response okHttpResponse) {
                publishResponse(okHttpResponse, httpRequest, callBack, null, hasBinaryResponse);
            }
        });

        return callBack;
    }

    /**
     * Execute a given HttpRequest to get string/binary response back.
     * @param   httpRequest        The given HttpRequest to execute.
     * @param   hasBinaryResponse  Whether the response is binary or string.
     * @return  The converted http response.
     * @throws  IOException exception to be thrown while converting response.
     */
    public HttpResponse execute(HttpRequest httpRequest, boolean hasBinaryResponse)
            throws IOException {
        okhttp3.Request okHttpRequest = convertRequest(httpRequest);
        
        RetryInterceptor retryInterceptor = getRetryInterceptor();
        if (retryInterceptor != null) {
            retryInterceptor.addRequestEntry(okHttpRequest);
        }

        okhttp3.Response okHttpResponse = null;
        okHttpResponse = client.newCall(okHttpRequest).execute();
        return convertResponse(httpRequest, okHttpResponse, hasBinaryResponse);
    }

    /**
     * Returns RetryInterceptor instance registered with client.
     * @return The RetryInterceptor instance.
     */
    private RetryInterceptor getRetryInterceptor() {
        return (RetryInterceptor) this.client.interceptors().stream()
                .filter(interceptor -> interceptor instanceof RetryInterceptor).findFirst()
                .orElse(null);
    }

    /**
     * Publishes success or failure result as HttpResponse from a HttpRequest.
     * @param okHttpResponse The okhttp response to publish.
     * @param httpRequest The internal http request.
     * @param completionBlock The success and failure code block reference to invoke the delegate.
     * @param error The reported errors for getting the http response.
     * @param hasBinaryResponse Whether the response is binary or string.
     * @return The converted http response.
     */
    private HttpResponse publishResponse(okhttp3.Response okHttpResponse, 
            HttpRequest httpRequest, CompletableFuture completionBlock,
            Throwable error, boolean hasBinaryResponse) {
        HttpResponse httpResponse = null;
        try {
            httpResponse = convertResponse(httpRequest, okHttpResponse, hasBinaryResponse);

            // if there are no errors, pass on to the callback function
            if (error == null && httpResponse != null) {
                completionBlock.complete(httpResponse);
            } else {
                completionBlock.completeExceptionally(error);
            }
        } catch (IOException e) {
            completionBlock.completeExceptionally(e);
        }
        return httpResponse;
    }

    /**
     * Converts a given OkHttp response into our internal http response model.
     * @param   response           The given OkHttp response.
     * @param   hasBinaryResponse  Whether the response is binary or string.
     * @return  The converted http response.
     * @throws  IOException exception to be thrown while converting response.
     */
    protected static HttpResponse convertResponse(HttpRequest request, okhttp3.Response response,
            boolean hasBinaryResponse) throws IOException {
        HttpResponse httpResponse = null;

        if (response != null) {

            okhttp3.ResponseBody responseBody = response.body();

            Headers headers = new Headers(response.headers().toMultimap());

            if (hasBinaryResponse) {
                InputStream responseStream = responseBody.byteStream();
                httpResponse = new HttpResponse(response.code(), headers, responseStream);
            } else {
                String responseString = responseBody.string();
                InputStream responseStream = new ByteArrayInputStream(
                        responseString.getBytes());
                httpResponse = new HttpStringResponse(response.code(), headers, responseStream,
                        responseString);

                responseBody.close();
                response.close();
            }
        }

        return httpResponse;
    }

    /**
     * Converts a given internal http request into an okhttp request model.
     * @param   httpRequest     The given http request in internal format.
     * @return  The converted okhttp request
     */
    private okhttp3.Request convertRequest(HttpRequest httpRequest) {
        okhttp3.RequestBody requestBody;

        if (httpRequest instanceof HttpBodyRequest) {

            // set request media type
            String contentType;
            Object body = ((HttpBodyRequest) httpRequest).getBody();

            // set request body
            if (body instanceof FileWrapper) {
                FileWrapper file = (FileWrapper) body;

                if (file.getContentType() != null && !file.getContentType().isEmpty()) {
                    contentType = file.getContentType();
                    httpRequest.getHeaders().add("content-type", contentType);
                } else if (httpRequest.getHeaders().has("content-type")) {
                    contentType = httpRequest.getHeaders().value("content-type");
                } else {
                    contentType = "application/octet-stream";
                    httpRequest.getHeaders().add("content-type", contentType);
                }

                if (file.getFile() != null) {
                    requestBody = okhttp3.RequestBody.create(okhttp3.MediaType.parse(contentType),
                            ((FileWrapper) body).getFile());
                } else {
                    requestBody = okhttp3.RequestBody.create(okhttp3.MediaType.parse(contentType),
                            ((FileWrapper) body).getFileStream());
                }

            } else {
                // set request body
                if (!httpRequest.getHeaders().has("content-type")) {
                    httpRequest.getHeaders().add("content-type", "application/json; charset=UTF-8");
                }

                contentType = httpRequest.getHeaders().value("content-type");
                // set request body
                requestBody = okhttp3.RequestBody.create(okhttp3.MediaType.parse(contentType),
                        ((String) body).getBytes());
            }
        } else {

            List> parameters = httpRequest.getParameters();
            boolean multipartRequest = false;

            // set request fields
            if (parameters != null && parameters.size() > 0) {
                // check if a request is a multipart request
                for (SimpleEntry param : parameters) {
                    if ((param.getValue() instanceof MultipartFileWrapper)
                            || (param.getValue() instanceof MultipartWrapper)) {
                        multipartRequest = true;
                        break;
                    }
                }

                if (multipartRequest) {
                    // make a multipart request if a file is being sent
                    requestBody = createMultipartRequestBody(httpRequest);
                } else {
                    okhttp3.FormBody.Builder formBuilder = new okhttp3.FormBody.Builder();
                    for (SimpleEntry param : parameters) {
                        formBuilder.add(param.getKey(),
                                (param.getValue() == null) ? "" : param.getValue().toString());
                    }
                    requestBody = formBuilder.build();
                }
            } else if (httpRequest.getHttpMethod().equals(HttpMethod.GET)) {
                requestBody = null;
            } else {
                requestBody = okhttp3.RequestBody.create(null, new byte[0]);
            }
        }

        // set request headers
        okhttp3.Headers.Builder requestHeaders = new okhttp3.Headers.Builder();
        if (httpRequest.getHeaders() != null) {
            requestHeaders = createRequestHeaders(httpRequest.getHeaders());
        }

        StringBuilder urlBuilder = new StringBuilder(httpRequest.getQueryUrl());

        // set query parameters
        ApiHelper.appendUrlWithQueryParameters(urlBuilder, httpRequest.getQueryParameters());

        // validate and preprocess url
        String url = ApiHelper.cleanUrl(urlBuilder);

        // build the request
        okhttp3.Request okHttpRequest = new okhttp3.Request.Builder()
                .method(httpRequest.getHttpMethod().toString(), requestBody)
                .headers(requestHeaders.build()).url(url)
                .build();

        return okHttpRequest;
    }
    
    private static okhttp3.RequestBody createMultipartRequestBody(HttpRequest httpRequest) {
        okhttp3.MultipartBody.Builder multipartBuilder = new okhttp3.MultipartBody.Builder()
                .setType(okhttp3.MultipartBody.FORM);
        
        for (SimpleEntry param : httpRequest.getParameters()) {
            if (param.getValue() instanceof MultipartFileWrapper) {
                MultipartFileWrapper wrapperObj = (MultipartFileWrapper) param.getValue();
                okhttp3.MediaType mediaType;
                if (wrapperObj.getFileWrapper().getContentType() != null
                        && !wrapperObj.getFileWrapper().getContentType().isEmpty()) {
                    mediaType =
                            okhttp3.MediaType.parse(wrapperObj.getFileWrapper().getContentType());
                } else {
                    mediaType =
                            okhttp3.MediaType.parse(wrapperObj.getHeaders().value("content-type"));
                }

                okhttp3.RequestBody body = okhttp3.RequestBody.create(mediaType,
                        wrapperObj.getFileWrapper().getFile());
                Headers fileWrapperHeaders = new Headers(wrapperObj.getHeaders());
                fileWrapperHeaders.remove("content-type");
                okhttp3.Headers.Builder fileWrapperHeadersBuilder =
                        createRequestHeaders(fileWrapperHeaders);
                
                fileWrapperHeadersBuilder.add("Content-Disposition", "form-data; name="
                        + appendQuotedStringAndEncodeEscapeCharacters(param.getKey()) 
                        + "; filename=" + appendQuotedStringAndEncodeEscapeCharacters(
                                wrapperObj.getFileWrapper().getFile().getName()));
                multipartBuilder.addPart(fileWrapperHeadersBuilder.build(), body);
            } else if (param.getValue() instanceof MultipartWrapper) {
                MultipartWrapper wrapperObject = (MultipartWrapper) param.getValue();
                okhttp3.RequestBody body = okhttp3.RequestBody.create(
                    okhttp3.MediaType.parse(wrapperObject.getHeaders().value("content-type")),
                    wrapperObject.getByteArray());
                Headers wrapperHeaders = new Headers(wrapperObject.getHeaders());
                wrapperHeaders.remove("content-type");
                okhttp3.Headers.Builder wrapperHeadersBuilder =
                        createRequestHeaders(wrapperHeaders);

                wrapperHeadersBuilder.add("Content-Disposition", "form-data; name="
                        + appendQuotedStringAndEncodeEscapeCharacters(param.getKey()));
                multipartBuilder.addPart(wrapperHeadersBuilder.build(), body);
            } else {
                multipartBuilder.addFormDataPart(param.getKey(),
                        (param.getValue() == null) ? "" : param.getValue().toString());
            }
        }
        return multipartBuilder.build();
    }

    private static okhttp3.Headers.Builder createRequestHeaders(Headers headers) {
        okhttp3.Headers.Builder requestHeaders = new okhttp3.Headers.Builder();
        for (Entry> kv : headers.asMultimap().entrySet()) {
            for (String value : kv.getValue()) {
                requestHeaders.add(kv.getKey(), value);
            }
        }
        return requestHeaders;
    }

    private static String appendQuotedStringAndEncodeEscapeCharacters(String key) {
        String target = "\"";
        for (int i = 0, len = key.length(); i < len; i++) {
            char ch = key.charAt(i);
            switch (ch) {
                case '\n':
                    target += "%0A";
                    break;
                case '\r':
                    target += "%0D";
                    break;
                case '"':
                    target += "%22";
                    break;
                default:
                    target += ch;
                    break;
            }
        }
        target += '"';
        return target;
    }

    /**
     * Create a simple HTTP GET request.
     */
    public HttpRequest get(StringBuilder queryUrlBuilder, Headers headers, 
            Map queryParams, List> parameters) {
        return new HttpRequest(HttpMethod.GET, queryUrlBuilder, headers, queryParams, parameters);
    }

    /**
     * Create a simple HTTP HEAD request.
     */
    public HttpRequest head(StringBuilder queryUrlBuilder, Headers headers, 
            Map queryParams, List> parameters) {
        return new HttpRequest(HttpMethod.HEAD, queryUrlBuilder, headers, queryParams, parameters);
    }

    /**
     * Create an HTTP POST request with parameters.
     */
    public HttpRequest post(StringBuilder queryUrlBuilder, Headers headers, 
            Map queryParams, List> parameters) {
        return new HttpRequest(HttpMethod.POST, queryUrlBuilder, headers, queryParams, parameters);
    }

    /**
     * Create an HTTP POST request with body.
     */
    public HttpBodyRequest postBody(StringBuilder queryUrlBuilder, Headers headers, 
            Map queryParams, Object body) {
        return new HttpBodyRequest(HttpMethod.POST, queryUrlBuilder, headers, queryParams, body);
    }


    /**
     * Create an HTTP PUT request with parameters.
     */
    public HttpRequest put(StringBuilder queryUrlBuilder, Headers headers, 
            Map queryParams, List> parameters) {
        return new HttpRequest(HttpMethod.PUT, queryUrlBuilder, headers, queryParams, parameters);
    }

    /**
     * Create an HTTP PUT request with body.
     */
    public HttpBodyRequest putBody(StringBuilder queryUrlBuilder, Headers headers, 
            Map queryParams, Object body) {
        return new HttpBodyRequest(HttpMethod.PUT, queryUrlBuilder, headers, queryParams, body);
    }

    /**
     * Create an HTTP PATCH request with parameters.
     */
    public HttpRequest patch(StringBuilder queryUrlBuilder, Headers headers, 
            Map queryParams, List> parameters) {
        return new HttpRequest(HttpMethod.PATCH, queryUrlBuilder, headers, queryParams, parameters);
    }

    /**
     * Create an HTTP PATCH request with body.
     */
    public HttpBodyRequest patchBody(StringBuilder queryUrlBuilder, Headers headers, 
            Map queryParams, Object body) {
        return new HttpBodyRequest(HttpMethod.PATCH, queryUrlBuilder, headers, queryParams, body);
    }

    /**
     * Create an HTTP DELETE request with parameters.
     */
    public HttpRequest delete(StringBuilder queryUrl, Headers headers, 
            Map queryParams, List> parameters) {
        return new HttpRequest(HttpMethod.DELETE, queryUrl, headers, queryParams, parameters);
    }

    /**
     * Create an HTTP DELETE request with body.
     */
    public HttpBodyRequest deleteBody(StringBuilder queryUrlBuilder, Headers headers, 
            Map queryParams, Object body) {
        return new HttpBodyRequest(HttpMethod.DELETE, queryUrlBuilder, headers, queryParams, body);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy