org.apache.zeppelin.notebook.repo.zeppelinhub.rest.HttpProxyClient.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.zeppelin.notebook.repo.zeppelinhub.rest.HttpProxyClient.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 org.apache.zeppelin.notebook.repo.zeppelinhub.rest;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import javax.net.ssl.SSLContext;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
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.concurrent.FutureCallback;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ssl.BrowserCompatHostnameVerifier;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.impl.client.DefaultRedirectStrategy;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager;
import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
import org.apache.http.nio.conn.NoopIOSessionStrategy;
import org.apache.http.nio.conn.SchemeIOSessionStrategy;
import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy;
import org.apache.http.nio.reactor.ConnectingIOReactor;
import org.apache.http.nio.reactor.IOReactorException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This is http client class for the case of proxy usage
 * jetty-client has issue with https over proxy for 9.2.x
 *   https://github.com/eclipse/jetty.project/issues/408
 *   https://github.com/eclipse/jetty.project/issues/827
 *    
 */

public class HttpProxyClient {
    private static final Logger LOG = LoggerFactory.getLogger(HttpProxyClient.class);
    public static final String ZEPPELIN_TOKEN_HEADER = "X-Zeppelin-Token";

    private CloseableHttpAsyncClient client;
    private URI proxyUri;

    public static HttpProxyClient newInstance(URI proxyUri) {
        return new HttpProxyClient(proxyUri);
    }

    private HttpProxyClient(URI uri) {
        this.proxyUri = uri;

        client = getAsyncProxyHttpClient(proxyUri);
        client.start();
    }

    public URI getProxyUri() {
        return proxyUri;
    }

    private CloseableHttpAsyncClient getAsyncProxyHttpClient(URI proxyUri) {
        LOG.info("Creating async proxy http client");
        PoolingNHttpClientConnectionManager cm = getAsyncConnectionManager();
        HttpHost proxy = new HttpHost(proxyUri.getHost(), proxyUri.getPort());

        HttpAsyncClientBuilder clientBuilder = HttpAsyncClients.custom();
        if (cm != null) {
            clientBuilder = clientBuilder.setConnectionManager(cm);
        }

        if (proxy != null) {
            clientBuilder = clientBuilder.setProxy(proxy);
        }
        clientBuilder = setRedirects(clientBuilder);
        return clientBuilder.build();
    }

    private PoolingNHttpClientConnectionManager getAsyncConnectionManager() {
        ConnectingIOReactor ioReactor = null;
        PoolingNHttpClientConnectionManager cm = null;
        try {
            ioReactor = new DefaultConnectingIOReactor();
            // ssl setup
            SSLContext sslcontext = SSLContexts.createSystemDefault();
            X509HostnameVerifier hostnameVerifier = new BrowserCompatHostnameVerifier();
            @SuppressWarnings("deprecation")
            Registry<SchemeIOSessionStrategy> sessionStrategyRegistry = RegistryBuilder
                    .<SchemeIOSessionStrategy>create().register("http", NoopIOSessionStrategy.INSTANCE)
                    .register("https", new SSLIOSessionStrategy(sslcontext, hostnameVerifier)).build();

            cm = new PoolingNHttpClientConnectionManager(ioReactor, sessionStrategyRegistry);
        } catch (IOReactorException e) {
            LOG.error("Couldn't initialize multi-threaded async client ", e);
            return null;
        }
        return cm;
    }

    private HttpAsyncClientBuilder setRedirects(HttpAsyncClientBuilder clientBuilder) {
        clientBuilder.setRedirectStrategy(new DefaultRedirectStrategy() {
            /** Redirectable methods. */
            private String[] REDIRECT_METHODS = new String[] { HttpGet.METHOD_NAME, HttpPost.METHOD_NAME,
                    HttpPut.METHOD_NAME, HttpDelete.METHOD_NAME, HttpHead.METHOD_NAME };

            @Override
            protected boolean isRedirectable(String method) {
                for (String m : REDIRECT_METHODS) {
                    if (m.equalsIgnoreCase(method)) {
                        return true;
                    }
                }
                return false;
            }
        });
        return clientBuilder;
    }

    public String sendToZeppelinHub(HttpRequestBase request, boolean withResponse) throws IOException {
        return withResponse ? sendAndGetResponse(request) : sendWithoutResponseBody(request);
    }

    private String sendWithoutResponseBody(HttpRequestBase request) throws IOException {
        FutureCallback<HttpResponse> callback = getCallback(request);
        client.execute(request, callback);
        return StringUtils.EMPTY;
    }

    private String sendAndGetResponse(HttpRequestBase request) throws IOException {
        String data = StringUtils.EMPTY;
        try {
            HttpResponse response = client.execute(request, null).get(30, TimeUnit.SECONDS);
            int code = response.getStatusLine().getStatusCode();
            if (code == 200) {
                try (InputStream responseContent = response.getEntity().getContent()) {
                    data = IOUtils.toString(responseContent, "UTF-8");
                }
            } else {
                LOG.error("ZeppelinHub {} {} returned with status {} ", request.getMethod(), request.getURI(),
                        code);
                throw new IOException("Cannot perform " + request.getMethod() + " request to ZeppelinHub");
            }
        } catch (InterruptedException | ExecutionException | TimeoutException | NullPointerException e) {
            throw new IOException(e);
        }
        return data;
    }

    private FutureCallback<HttpResponse> getCallback(final HttpRequestBase request) {
        return new FutureCallback<HttpResponse>() {

            public void completed(final HttpResponse response) {
                request.releaseConnection();
                LOG.info("Note {} completed with {} status", request.getMethod(), response.getStatusLine());
            }

            public void failed(final Exception ex) {
                request.releaseConnection();
                LOG.error("Note {} failed with {} message", request.getMethod(), ex.getMessage());
            }

            public void cancelled() {
                request.releaseConnection();
                LOG.info("Note {} was canceled", request.getMethod());
            }
        };
    }

    public void stop() {
        try {
            client.close();
        } catch (Exception e) {
            LOG.error("Failed to close proxy client ", e);
        }
    }
}