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

com.github.davidmoten.odata.client.internal.ApacheHttpClientHttpService Maven / Gradle / Ivy

The newest version!
package com.github.davidmoten.odata.client.internal;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Supplier;

import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.config.RequestConfig.Builder;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPatch;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.github.davidmoten.guavamini.Preconditions;
import com.github.davidmoten.odata.client.ClientException;
import com.github.davidmoten.odata.client.HttpMethod;
import com.github.davidmoten.odata.client.HttpRequestOptions;
import com.github.davidmoten.odata.client.HttpResponse;
import com.github.davidmoten.odata.client.HttpService;
import com.github.davidmoten.odata.client.Path;
import com.github.davidmoten.odata.client.RequestHeader;

public class ApacheHttpClientHttpService implements HttpService {

    private static final Logger log = LoggerFactory.getLogger(ApacheHttpClientHttpService.class);

    private final Path basePath;
    private final CloseableHttpClient client;
    private final BiFunction, List> requestHeadersModifier;

    public ApacheHttpClientHttpService(Path basePath, Supplier clientSupplier,
            BiFunction, List> requestHeadersModifier) {
        this.basePath = basePath;
        this.client = clientSupplier.get();
        this.requestHeadersModifier = requestHeadersModifier;
    }

    public ApacheHttpClientHttpService(Path basePath) {
        this(basePath, () -> HttpClientBuilder.create().useSystemProperties().build(),
                (url, m) -> m);
    }
    
    @Override
    public HttpResponse submit(HttpMethod method, String url, List requestHeaders,
            InputStream content, int length, HttpRequestOptions options) {
        return getResponse(requestHeaders, toRequestBase(method, url), content, length, options);
    }
   
    @Override
    public Path getBasePath() {
        return basePath;
    }

    private static URL toUrl(HttpRequestBase request) {
        try {
            return request.getURI().toURL();
        } catch (MalformedURLException e) {
            throw new RuntimeException(e);
        }
    }

    private HttpResponse getResponse(List requestHeaders, HttpRequestBase request,
            InputStream content, int length, HttpRequestOptions options) {
        Preconditions.checkNotNull(options);
        log.debug("{} from url {}", request.getMethod(), request.getURI());
        log.debug("requestHeaders={}", requestHeaders);
        for (RequestHeader header : requestHeadersModifier.apply(toUrl(request), requestHeaders)) {
            request.addHeader(header.name(), header.value());
        }
        try {
            if (content != null && request instanceof HttpEntityEnclosingRequest) {
                ((HttpEntityEnclosingRequest) request)
                        .setEntity(new InputStreamEntity(content, length));
                log.debug("content={}", content);
            }
            RequestConfig config = com.github.davidmoten.odata.client.Util.nvl(request.getConfig(),
                    RequestConfig.DEFAULT);
            Builder builder = RequestConfig //
                    .copy(config);
            options.requestConnectTimeoutMs()
                    .ifPresent(x -> builder.setConnectTimeout(x.intValue()));
            options.requestReadTimeoutMs().ifPresent(x -> builder.setSocketTimeout(x.intValue()));
            config = builder.build();
            request.setConfig(config);
            log.debug("executing request");
            try (CloseableHttpResponse response = client.execute(request)) {
                int statusCode = response.getStatusLine().getStatusCode();
                log.debug("executed request, code={}", statusCode);
                HttpEntity entity = response.getEntity();
                final byte[] bytes;
                if (entity == null) {
                    bytes = null;
                } else {
                    bytes = Util.read(entity.getContent());
                }
                if (log.isDebugEnabled()) {
                    log.debug("response text=\n{}",
                            bytes == null ? "null" : new String(bytes, StandardCharsets.UTF_8));
                }
                return new HttpResponse(statusCode, bytes);
            }
        } catch (IOException e) {
            throw new ClientException(e);
        }
    }

    @Override
    public InputStream getStream(HttpMethod method, String url, List requestHeaders,
            HttpRequestOptions options) {
        HttpRequestBase b = toRequestBase(method, url);
        return getStream(requestHeaders, b, null, 0, options);
    }

    private static HttpRequestBase toRequestBase(HttpMethod method, String url) {
        if (method == HttpMethod.GET) {
            return new HttpGet(url);
        } else if (method == HttpMethod.DELETE) {
            return new HttpDelete(url);
        } else if (method == HttpMethod.PATCH) {
            return new HttpPatch(url);
        } else if (method == HttpMethod.PUT) {
            return new HttpPut(url);
        } else if (method == HttpMethod.POST) {
            return new HttpPost(url);
        } else {
            throw new UnsupportedOperationException(method.toString() + " not recognized");
        }
    }

    public InputStream getStream(List requestHeaders, HttpRequestBase request,
            InputStream content, int length, HttpRequestOptions options) {
        Preconditions.checkNotNull(options);
        log.debug("{} from url {}", request.getMethod(), request.getURI());
        log.debug("requestHeaders={}", requestHeaders);
        boolean contentLengthSet = false;
        for (RequestHeader header : requestHeadersModifier.apply(toUrl(request), requestHeaders)) {
            request.addHeader(header.name(), header.value());
            if ("Content-Length".equals(header.name())) {
                contentLengthSet = true;
            }
        }
        if (content != null && !contentLengthSet) {
            request.addHeader("Content-Length", Integer.toString(length));
        }
        CloseableHttpResponse response = null;
        try {
            if (content != null && request instanceof HttpEntityEnclosingRequest) {
                ((HttpEntityEnclosingRequest) request)
                        .setEntity(new InputStreamEntity(content, length));
                log.debug("content={}", content);
            }
            RequestConfig config = com.github.davidmoten.odata.client.Util.nvl(request.getConfig(),
                    RequestConfig.DEFAULT);
            Builder builder = RequestConfig //
                    .copy(config);
            options.requestConnectTimeoutMs()
                    .ifPresent(x -> builder.setConnectTimeout(x.intValue()));
            options.requestReadTimeoutMs().ifPresent(x -> builder.setSocketTimeout(x.intValue()));
            config = builder.build();
            request.setConfig(config);
            log.debug("executing request");
            response = client.execute(request);
            int statusCode = response.getStatusLine().getStatusCode();
            log.debug("executed request, code={}", statusCode);
            InputStream is = response.getEntity().getContent();
            // ensure response is closed when input stream is closed
            InputStream in = new InputStreamWithCloseable(is, response);
            if (!isOk(statusCode)) {
                try {
                    String msg = Util.readString(in, StandardCharsets.UTF_8);
                    throw new ClientException(statusCode,
                            "getStream returned HTTP " + statusCode + "\n" //
                                    + "url=" + request.getURI() + "\n" //
                                    + "headers=" + requestHeaders + "\n" //
                                    + "response:\n" //
                                    + msg);
                } finally {
                    in.close();
                }
            } else {
                return in;
            }
        } catch (IOException e) {
            // ensure that response is closed on exception to avoid memory leak
            if (response != null) {
                try {
                    response.close();
                } catch (IOException e1) {
                    log.warn(e1.getMessage(), e);
                }
            }
            throw new ClientException(e);
        }
    }

    @Override
    public void close() throws Exception {
        log.info("closing client");
        client.close();
        log.info("closed client");
    }

    private static boolean isOk(int statusCode) {
        return statusCode >= 200 && statusCode < 300;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy