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

com.github.lontime.exthttp.provider.AbstractProvider Maven / Gradle / Ivy

There is a newer version: 1.4.0
Show newest version
package com.github.lontime.exthttp.provider;

import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import com.github.lontime.base.commonj.components.AbstractComponentLifecycle;
import com.github.lontime.base.commonj.components.ComponentInterfaceHelper;
import com.github.lontime.base.commonj.langs.Prioritized;
import com.github.lontime.base.commonj.utils.CollectionHelper;
import com.github.lontime.exthttp.common.ConfigKey;
import com.github.lontime.exthttp.common.Response;
import com.github.lontime.exthttp.configuration.ClientOption;
import com.github.lontime.exthttp.configuration.ConnectionOption;
import com.github.lontime.exthttp.configuration.OptionResolver;
import com.github.lontime.exthttp.configuration.WiretapSpec;
import com.github.lontime.exthttp.container.DisposableServerWrapper;
import com.github.lontime.exthttp.container.HttpInterceptorInterface;
import com.github.lontime.exthttp.container.ServerContainer;
import com.github.lontime.extjson.JSONInstance;
import com.github.lontime.extjson.TypeReference;
import io.netty.channel.ChannelOption;
import io.netty.channel.epoll.EpollChannelOption;
import io.netty.channel.socket.nio.NioChannelOption;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.cookie.Cookie;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.util.AttributeKey;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.netty.ByteBufFlux;
import reactor.netty.http.Http11SslContextSpec;
import reactor.netty.http.Http2SslContextSpec;
import reactor.netty.http.HttpProtocol;
import reactor.netty.http.client.HttpClient;
import reactor.netty.http.client.HttpClientResponse;
import reactor.netty.resources.ConnectionProvider;
import reactor.netty.tcp.DefaultSslContextSpec;
import reactor.util.function.Tuple2;
import reactor.util.function.Tuples;

import static reactor.netty.resources.ConnectionProvider.LEASING_STRATEGY_LIFO;

/**
 * AbstractProvider.
 * @author lontime
 * @since 1.0
 */
public abstract class AbstractProvider extends AbstractComponentLifecycle implements Provider {

    private Map clientMap = new ConcurrentHashMap<>();

    private Map connectionMap = new ConcurrentHashMap<>();

    private final ServerContainer serverContainer;

    public AbstractProvider() {
        this.serverContainer = new ServerContainer();
    }

    @Override
    public void initialize() {
        serverContainer.initialize();
    }

    @Override
    public void start() {
        serverContainer.start();
    }

    @Override
    public void stop() {
        serverContainer.stop();
    }

    @Override
    public void destroy() {
        serverContainer.destroy();
    }

    @Override
    public HttpClient get(String name) {
        return clientMap.computeIfAbsent(name, this::loadHttpClient);
    }

    @Override
    public  Mono> asyncPostForObject(String name, HttpHeaders headers, String uri, Object body, Class clazz) {
        return asyncRequestForObject(name, HttpMethod.POST, headers, uri, body, clazz);
    }

    @Override
    public  Mono> asyncDeleteForObject(String name, HttpHeaders headers, String uri, Object body, Class clazz) {
        return asyncRequestForObject(name, HttpMethod.DELETE, headers, uri, body, clazz);
    }

    @Override
    public  Mono> asyncPatchForObject(String name, HttpHeaders headers, String uri, Object body, Class clazz) {
        return asyncRequestForObject(name, HttpMethod.PATCH, headers, uri, body, clazz);
    }

    @Override
    public  Mono> asyncGetForObject(String name, HttpHeaders headers, String uri, Class clazz) {
        return asyncRequestForObject(name, HttpMethod.GET, headers, uri, null, clazz);
    }

    @Override
    public  Mono> asyncPostForObject(String name, HttpHeaders headers, String uri, Object body, TypeReference clazz) {
        return asyncRequestForObject(name, HttpMethod.POST, headers, uri, body, clazz);
    }

    @Override
    public  Mono> asyncDeleteForObject(String name, HttpHeaders headers, String uri, Object body, TypeReference clazz) {
        return asyncRequestForObject(name, HttpMethod.DELETE, headers, uri, body, clazz);
    }

    @Override
    public  Mono> asyncPatchForObject(String name, HttpHeaders headers, String uri, Object body, TypeReference clazz) {
        return asyncRequestForObject(name, HttpMethod.PATCH, headers, uri, body, clazz);
    }

    @Override
    public  Mono> asyncGetForObject(String name, HttpHeaders headers, String uri, TypeReference clazz) {
        return asyncRequestForObject(name, HttpMethod.GET, headers, uri, null, clazz);
    }

    private  Mono> asyncRequestForObject(String name, HttpMethod method, HttpHeaders headers, String uri,
                                              Object body, Class clazz) {
        return request(name, method, headers, uri, body).map(s -> {
            final T newBody = JSONInstance.get().parse(s.getT2(), clazz);
            return buildResponse(s.getT1(), newBody);
        });
    }

    private  Mono> asyncRequestForObject(String name, HttpMethod method, HttpHeaders headers, String uri,
                                              Object body, TypeReference reference) {
        return request(name, method, headers, uri, body).map(s -> {
            final T newBody = JSONInstance.get().parse(s.getT2(), reference);
            return buildResponse(s.getT1(), newBody);
        });
    }

    private Mono> request(String name, HttpMethod method, HttpHeaders headers, String uri, Object body) {
        if (!headers.contains(HttpHeaderNames.CONTENT_TYPE)) {
            headers.add(HttpHeaderNames.CONTENT_TYPE, "application/json; charset=UTF-8");
        }
        if (body == null) {
            return get(name).headers(h -> h.add(headers))
                    .request(method).uri(uri)
                    .responseSingle((res, content) -> content.asString().map(s -> Tuples.of(res, s)));
        }
        final String string;
        if (body instanceof String) {
            string = (String) body;
        } else {
            string = JSONInstance.get().toJSONString(body);
        }
        final HttpClient httpClient = get(name);
        return httpClient.headers(h -> h.add(headers))
                .request(method).uri(uri)
                .send(ByteBufFlux.fromString(Flux.just(string)))
                .responseSingle((res, content) -> content.asString().map(s -> Tuples.of(res, s)));
    }

    private  Response buildResponse(HttpClientResponse clientResponse, T body) {
        final HttpHeaders entries = clientResponse.responseHeaders();
        final Map map = new HashMap<>(Math.max(entries.size(), 4));
        for (Map.Entry entry : entries) {
            map.put(entry.getKey(), entry.getValue());
        }
        return Response.builder().status(clientResponse.status().code())
                .headers(map).body(body).build();
    }

    @Override
    public DisposableServerWrapper getServer(String name) {
        return serverContainer.getServer(name);
    }

    @Override
    public void close() {
        reentrantStopAndRemoveHooklet();
    }

    private HttpClient loadHttpClient(String name) {
        final ClientOption option = OptionResolver.getInstance().getClientOption(name);
        final ConnectionProvider connection = getConnection(option.getConnectionName());
        HttpClient httpClient = (connection == null) ? HttpClient.create() : HttpClient.create(connection);
        if (CollectionHelper.isNotEmpty(option.getProtocols())) {
            final int size = option.getProtocols().size();
            final HttpProtocol[] protocols = new HttpProtocol[size];
            for (int i = 0; i < size; i++) {
                protocols[i] = option.getProtocols().get(0);
            }
            httpClient = httpClient.protocol(protocols);
        }
        if (option.getBaseUrl() != null) {
            httpClient = httpClient.baseUrl(option.getBaseUrl());
        }
        if (option.getHost() != null) {
            httpClient = httpClient.host(option.getHost());
        }
        if (option.getPort() != null) {
            httpClient = httpClient.port(option.getPort());
        }
        if (option.getKeepAlive() != null) {
            httpClient = httpClient.keepAlive(option.getKeepAlive());
        }
        if (option.getCompress() != null) {
            httpClient = httpClient.compress(option.getCompress());
        }
        if (option.getDisableRetry() != null) {
            httpClient = httpClient.disableRetry(option.getDisableRetry());
        }
        if (option.getSecure() != null && option.getSecure()) {
            final DefaultSslContextSpec defaultSslContextSpec = DefaultSslContextSpec.forClient()
                    .configure(builder -> builder.trustManager(InsecureTrustManagerFactory.INSTANCE));
            httpClient = httpClient.secure(spec -> spec.sslContext(defaultSslContextSpec));
        }
        if (option.getHttp11Secure() != null && option.getHttp11Secure()) {
            final Http11SslContextSpec http11SslContextSpec = Http11SslContextSpec.forClient()
                            .configure(builder -> builder.trustManager(InsecureTrustManagerFactory.INSTANCE));
            httpClient = httpClient.secure(spec -> spec.sslContext(http11SslContextSpec));
        }
        if (option.getHttp2Secure() != null && option.getHttp2Secure()) {
            final Http2SslContextSpec http2SslContextSpec = Http2SslContextSpec.forClient()
                    .configure(builder -> builder.trustManager(InsecureTrustManagerFactory.INSTANCE));
            httpClient = httpClient.secure(spec -> spec.sslContext(http2SslContextSpec));
        }
        if (option.getNoSSL() != null && option.getNoSSL()) {
            httpClient = httpClient.noSSL();
        }
        if (option.getNoProxy() != null && option.getNoProxy()) {
            httpClient = httpClient.noProxy();
        }
        if (option.getWiretapEnabled() != null) {
            httpClient = httpClient.wiretap(option.getWiretapEnabled());
        }
        if (option.getResponseTimeout() != null) {
            httpClient = httpClient.responseTimeout(option.getResponseTimeout());
        }
        if (option.getFollowRedirect() != null) {
            if (option.getRedirect() != null) {
                httpClient = httpClient.followRedirect(option.getFollowRedirect(), r -> {
                    final ClientOption.Redirect redirectOpts = option.getRedirect();
                    if (redirectOpts.getResponseTimeout() != null) {
                        r.responseTimeout(redirectOpts.getResponseTimeout());
                    }
                    if (CollectionHelper.isNotEmpty(redirectOpts.getHeaders())) {
                        redirectOpts.getHeaders().forEach((k, v) -> r.addHeader(k, v));
                    }
                    if (redirectOpts.getIsFollowRedirect() != null && redirectOpts.getIsFollowRedirect()) {
                        r.isFollowRedirect();
                    }
                    if (redirectOpts.getCookie() != null) {
                        r.addCookie(redirectOpts.getCookie());
                    }
                });
            } else {
                httpClient = httpClient.followRedirect(option.getFollowRedirect());
            }
        }
        if (option.getWiretapSpec() != null) {
            final WiretapSpec wiretapSpec = option.getWiretapSpec();
            httpClient = httpClient.wiretap(wiretapSpec.getCategory(), wiretapSpec.getLevel(),
                    wiretapSpec.getFormat(), wiretapSpec.getCharset());
        }
        if (CollectionHelper.isNotEmpty(option.getHeaders())) {
            for (Map.Entry entry : option.getHeaders().entrySet()) {
                final ConfigKey configKey = entry.getKey();
                httpClient = httpClient.headers(h -> h.set(configKey.getKey(), configKey.parseValue(entry.getValue())));
            }
        }
        if (CollectionHelper.isNotEmpty(option.getCookies())) {
            for (Cookie cookie : option.getCookies()) {
                httpClient = httpClient.cookie(cookie);
            }
        }
        if (option.getCookieEncoder() != null && option.getCookieDecoder() != null) {
            httpClient = httpClient.cookieCodec(option.getCookieEncoder().toCookieEncoder(), option.getCookieDecoder().toCookieDecoder());
        } else if (option.getCookieEncoder() != null) {
            httpClient = httpClient.cookieCodec(option.getCookieEncoder().toCookieEncoder());
        }
        if (option.getResponseDecoder() != null) {
            httpClient = httpClient.httpResponseDecoder(s -> {
                final ClientOption.ResponseDecoder decoder = option.getResponseDecoder();
                if (decoder.getParseHttpAfterConnectRequest() != null) {
                    s.parseHttpAfterConnectRequest(decoder.getParseHttpAfterConnectRequest());
                }
                if (decoder.getInitialBufferSize() != null) {
                    s.initialBufferSize(decoder.getInitialBufferSize());
                }
                if (decoder.getMaxChunkSize() != null) {
                    s.maxChunkSize(decoder.getMaxChunkSize());
                }
                if (decoder.getMaxHeaderSize() != null) {
                    s.maxHeaderSize(decoder.getMaxHeaderSize());
                }
                if (decoder.getMaxInitialLineLength() != null) {
                    s.maxInitialLineLength(decoder.getMaxInitialLineLength());
                }
                if (decoder.getFailOnMissingResponse() != null) {
                    s.failOnMissingResponse(decoder.getFailOnMissingResponse());
                }
                if (decoder.getAllowDuplicateContentLengths() != null) {
                    s.allowDuplicateContentLengths(decoder.getAllowDuplicateContentLengths());
                }
                if (decoder.getValidateHeaders() != null) {
                    s.validateHeaders(decoder.getValidateHeaders());
                }
                if (decoder.getH2cMaxContentLength() != null) {
                    s.h2cMaxContentLength(decoder.getH2cMaxContentLength());
                }
                return s;
            });
        }
        if (CollectionHelper.isNotEmpty(option.getAttrs())) {
            for (Map.Entry entry : option.getNioChannelOptions().entrySet()) {
                final ConfigKey configKey = entry.getKey();
                httpClient = httpClient.attr(AttributeKey.valueOf(configKey.getKey()), configKey.parseValue(entry.getValue()));
            }
        }

        if (CollectionHelper.isNotEmpty(option.getChannelOptions())) {
            for (Map.Entry entry : option.getChannelOptions().entrySet()) {
                final ConfigKey configKey = entry.getKey();
                httpClient = httpClient.option(ChannelOption.valueOf(configKey.getKey()), configKey.parseValue(entry.getValue()));
            }
        }
        if (CollectionHelper.isNotEmpty(option.getEpollChannelOptions())) {
            for (Map.Entry entry : option.getEpollChannelOptions().entrySet()) {
                final ConfigKey configKey = entry.getKey();
                httpClient = httpClient.option(EpollChannelOption.valueOf(configKey.getKey()), configKey.parseValue(entry.getValue()));
            }
        }
        if (CollectionHelper.isNotEmpty(option.getNioChannelOptions())) {
            for (Map.Entry entry : option.getNioChannelOptions().entrySet()) {
                final ConfigKey configKey = entry.getKey();
                httpClient = httpClient.option(NioChannelOption.valueOf(configKey.getKey()), configKey.parseValue(entry.getValue()));
            }
        }

        if (option.getProxy() != null) {
            final ClientOption.Proxy proxy = option.getProxy();
            httpClient = httpClient.proxy(p -> p.type(proxy.getType())
                    .address(InetSocketAddress.createUnresolved(proxy.getHost(), proxy.getPort())));
        }

        final List interfaceUnsorted = ComponentInterfaceHelper.get(HttpInterceptorInterface.class, name);
        if (CollectionHelper.isNotEmpty(interfaceUnsorted)) {
            final List interfaces = interfaceUnsorted.stream()
                    .sorted(Prioritized.COMPARATOR).collect(Collectors.toList());
            httpClient = httpClient.doOnResolve(c -> interfaces.forEach(s -> s.onResolve(c)))
                    .doAfterResolve((c, address) -> interfaces.forEach(s -> s.afterResolve(c, address)))
                    .doOnResolveError((c, throwable) -> interfaces.forEach(s -> s.OnResolveError(c, throwable)))
                    .doOnRequest((r, c) -> interfaces.forEach(s -> s.onRequest(r, c)))
                    .doAfterRequest((r, c) -> interfaces.forEach(s -> s.afterRequest(r, c)))
                    .doOnRequestError((r, throwable) -> interfaces.forEach(s -> s.onRequestError(r, throwable)))
                    .doOnResponse((r, c) -> interfaces.forEach(s -> s.onResponse(r, c)))
                    .doOnResponseError((r, throwable) -> interfaces.forEach(s -> s.onResponseError(r, throwable)))
                    .doAfterResponseSuccess((r, c) -> interfaces.forEach(s -> s.afterResponseSuccess(r, c)));
        }

        if (option.getWarmup() != null && option.getWarmup()) {
            httpClient.warmup().block();
        }
        return httpClient;
    }

    public ConnectionProvider getConnection(String name) {
        if (name == null) {
            return null;
        }
        return connectionMap.computeIfAbsent(name, this::loadConnection);
    }

    private ConnectionProvider loadConnection(String name) {
        final ConnectionOption connectionOption = OptionResolver.getInstance().getConnectionOption(name);
        final ConnectionOption.Pool pool = connectionOption.getPool();
        final ConnectionProvider.Builder builder = ConnectionProvider.builder(connectionOption.getPool().getName());
        if (pool.getMaxConnections() != null) {
            builder.maxConnections(pool.getMaxConnections());
        }
        if (pool.getPendingAcquireMaxCount() != null) {
            builder.pendingAcquireMaxCount(pool.getPendingAcquireMaxCount());
        }
        if (pool.getPendingAcquireTimeout() != null) {
            builder.pendingAcquireTimeout(pool.getPendingAcquireTimeout());
        }
        if (pool.getMaxIdleTime() != null) {
            builder.maxIdleTime(pool.getMaxIdleTime());
        }
        if (pool.getMaxLifeTime() != null) {
            builder.maxLifeTime(pool.getMaxLifeTime());
        }
        if (pool.getEvictionInterval() != null) {
            builder.evictInBackground(pool.getEvictionInterval());
        }
        if (LEASING_STRATEGY_LIFO.equals(pool.getLeasingStrategy())) {
            builder.fifo();
        }
        if (pool.getDisposeTimeout() != null) {
            builder.disposeTimeout(pool.getDisposeTimeout());
        }
        if (pool.getInactivePoolDisposeInterval() != null && pool.getPoolInactivity() != null) {
            builder.disposeInactivePoolsInBackground(pool.getInactivePoolDisposeInterval(), pool.getPoolInactivity());
        }
        return builder.build();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy