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

io.gravitee.fetcher.http.HttpFetcher Maven / Gradle / Ivy

The newest version!
/*
 * Copyright © 2015 The Gravitee team (http://gravitee.io)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.gravitee.fetcher.http;

import io.gravitee.common.http.HttpHeaders;
import io.gravitee.common.http.HttpStatusCode;
import io.gravitee.common.utils.UUID;
import io.gravitee.fetcher.api.Fetcher;
import io.gravitee.fetcher.api.FetcherConfiguration;
import io.gravitee.fetcher.api.FetcherException;
import io.gravitee.fetcher.api.Resource;
import io.gravitee.node.api.Node;
import io.gravitee.node.api.utils.NodeUtils;
import io.vertx.core.Handler;
import io.vertx.core.Promise;
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.*;
import io.vertx.core.net.ProxyOptions;
import io.vertx.core.net.ProxyType;
import java.io.ByteArrayInputStream;
import java.net.URI;
import java.util.concurrent.CompletableFuture;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.support.CronExpression;

/**
 * @author David BRASSELY (david.brassely at graviteesource.com)
 * @author Nicolas GERAUD (nicolas.geraud at graviteesource.com)
 * @author GraviteeSource Team
 */
public class HttpFetcher implements Fetcher {

    private static final Logger logger = LoggerFactory.getLogger(HttpFetcher.class);

    private static final String HTTPS_SCHEME = "https";

    private final HttpFetcherConfiguration httpFetcherConfiguration;

    @Value("${httpClient.timeout:10000}")
    private int httpClientTimeout;

    @Value("${httpClient.proxy.type:HTTP}")
    private String httpClientProxyType;

    @Value("${httpClient.proxy.http.host:#{systemProperties['http.proxyHost'] ?: 'localhost'}}")
    private String httpClientProxyHttpHost;

    @Value("${httpClient.proxy.http.port:#{systemProperties['http.proxyPort'] ?: 3128}}")
    private int httpClientProxyHttpPort;

    @Value("${httpClient.proxy.http.username:#{null}}")
    private String httpClientProxyHttpUsername;

    @Value("${httpClient.proxy.http.password:#{null}}")
    private String httpClientProxyHttpPassword;

    @Value("${httpClient.proxy.https.host:#{systemProperties['https.proxyHost'] ?: 'localhost'}}")
    private String httpClientProxyHttpsHost;

    @Value("${httpClient.proxy.https.port:#{systemProperties['https.proxyPort'] ?: 3128}}")
    private int httpClientProxyHttpsPort;

    @Value("${httpClient.proxy.https.username:#{null}}")
    private String httpClientProxyHttpsUsername;

    @Value("${httpClient.proxy.https.password:#{null}}")
    private String httpClientProxyHttpsPassword;

    @Autowired
    private Vertx vertx;

    @Autowired
    private Node node;

    public HttpFetcher(HttpFetcherConfiguration httpFetcherConfiguration) {
        this.httpFetcherConfiguration = httpFetcherConfiguration;
    }

    @Override
    public Resource fetch() throws FetcherException {
        if (
            httpFetcherConfiguration.isAutoFetch() &&
            (httpFetcherConfiguration.getFetchCron() == null || httpFetcherConfiguration.getFetchCron().isEmpty())
        ) {
            throw new FetcherException("Some required configuration attributes are missing.", null);
        }

        if (httpFetcherConfiguration.isAutoFetch() && httpFetcherConfiguration.getFetchCron() != null) {
            try {
                CronExpression.parse(httpFetcherConfiguration.getFetchCron());
            } catch (IllegalArgumentException e) {
                throw new FetcherException("Cron expression is invalid", e);
            }
        }

        try {
            Buffer buffer = fetchContent().get();
            if (buffer == null) {
                throw new FetcherException("Unable to fetch Http content '" + httpFetcherConfiguration.getUrl() + "': no content", null);
            }
            final Resource resource = new Resource();
            resource.setContent(new ByteArrayInputStream(buffer.getBytes()));
            return resource;
        } catch (Exception ex) {
            throw new FetcherException("Unable to fetch Http content (" + ex.getMessage() + ")", ex);
        }
    }

    @Override
    public FetcherConfiguration getConfiguration() {
        return httpFetcherConfiguration;
    }

    private CompletableFuture fetchContent() {
        Promise promise = Promise.promise();

        URI requestUri = URI.create(httpFetcherConfiguration.getUrl());
        boolean ssl = HTTPS_SCHEME.equalsIgnoreCase(requestUri.getScheme());

        final HttpClientOptions options = new HttpClientOptions()
            .setSsl(ssl)
            .setTrustAll(true)
            .setMaxPoolSize(1)
            .setKeepAlive(false)
            .setTcpKeepAlive(false)
            .setConnectTimeout(httpClientTimeout);

        if (httpFetcherConfiguration.isUseSystemProxy()) {
            ProxyOptions proxyOptions = new ProxyOptions();
            proxyOptions.setType(ProxyType.valueOf(httpClientProxyType));
            if (HTTPS_SCHEME.equals(requestUri.getScheme())) {
                proxyOptions.setHost(httpClientProxyHttpsHost);
                proxyOptions.setPort(httpClientProxyHttpsPort);
                proxyOptions.setUsername(httpClientProxyHttpsUsername);
                proxyOptions.setPassword(httpClientProxyHttpsPassword);
            } else {
                proxyOptions.setHost(httpClientProxyHttpHost);
                proxyOptions.setPort(httpClientProxyHttpPort);
                proxyOptions.setUsername(httpClientProxyHttpUsername);
                proxyOptions.setPassword(httpClientProxyHttpPassword);
            }
            options.setProxyOptions(proxyOptions);
        }

        final HttpClient httpClient = vertx.createHttpClient(options);

        final int port = requestUri.getPort() != -1 ? requestUri.getPort() : (HTTPS_SCHEME.equals(requestUri.getScheme()) ? 443 : 80);

        try {
            String relativeUri = (requestUri.getRawQuery() == null)
                ? requestUri.getRawPath()
                : requestUri.getRawPath() + '?' + requestUri.getRawQuery();

            final RequestOptions reqOptions = new RequestOptions()
                .setMethod(HttpMethod.GET)
                .setPort(port)
                .setHost(requestUri.getHost())
                .setURI(relativeUri)
                .putHeader(HttpHeaders.USER_AGENT, NodeUtils.userAgent(node))
                .putHeader("X-Gravitee-Request-Id", UUID.toString(UUID.random()))
                .setTimeout(httpClientTimeout);

            httpClient
                .request(reqOptions)
                .onFailure(
                    new Handler() {
                        @Override
                        public void handle(Throwable throwable) {
                            promise.fail(throwable);

                            // Close client
                            httpClient.close();
                        }
                    }
                )
                .onSuccess(
                    new Handler() {
                        @Override
                        public void handle(HttpClientRequest request) {
                            request.response(asyncResponse -> {
                                if (asyncResponse.failed()) {
                                    promise.fail(asyncResponse.cause());

                                    // Close client
                                    httpClient.close();
                                } else {
                                    HttpClientResponse response = asyncResponse.result();
                                    if (response.statusCode() == HttpStatusCode.OK_200) {
                                        response.bodyHandler(buffer -> {
                                            promise.complete(buffer);

                                            // Close client
                                            httpClient.close();
                                        });
                                    } else {
                                        promise.complete(null);

                                        // Close client
                                        httpClient.close();
                                    }
                                }
                            });

                            request.exceptionHandler(throwable -> {
                                try {
                                    promise.fail(throwable);

                                    // Close client
                                    httpClient.close();
                                } catch (IllegalStateException ise) {
                                    // Do not take care about exception when closing client
                                }
                            });

                            request.end();
                        }
                    }
                );
        } catch (Exception ex) {
            logger.error("Unable to fetch content using HTTP", ex);
            promise.fail(ex);

            // Close client
            httpClient.close();
        }

        return promise.future().toCompletionStage().toCompletableFuture();
    }

    public void setVertx(Vertx vertx) {
        this.vertx = vertx;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy