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

org.wildfly.url.http.HttpClientURLConnection Maven / Gradle / Ivy

/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2018 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * 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 org.wildfly.url.http;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.net.UnknownHostException;
import java.security.GeneralSecurityException;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpOptions;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpTrace;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.utils.DateUtils;
import org.apache.http.conn.ManagedHttpClientConnection;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpCoreContext;
import org.wildfly.security.SecurityFactory;
import org.wildfly.security.auth.client.AuthenticationContext;

/**
 * {@link URLConnection} handling HTTP/HTTPS using Apache HTTP client
 *
 * @author Jan Kalina 
 */
class HttpClientURLConnection extends HttpsURLConnection {

    private final HttpHost proxy;
    private CloseableHttpClient client;
    private CloseableHttpResponse response;
    private ByteArrayOutputStream outputStream;
    private SSLSession sslSession;

    HttpClientURLConnection(URL url, Proxy proxy) throws IOException {
        super(url);
        this.proxy = convertProxy(proxy);
    }

    private HttpHost convertProxy(Proxy proxy) throws UnknownHostException {
        if (proxy == null || proxy.type() == Proxy.Type.DIRECT) {
            return null;
        }
        if (proxy.type() == Proxy.Type.HTTP) {
            if (proxy.address() instanceof InetSocketAddress) {
                InetSocketAddress address = (InetSocketAddress) proxy.address();
                if (address.getAddress() == null) {
                    throw new UnknownHostException("Unable resolve proxy address");
                }
                return new HttpHost(address.getAddress(), address.getPort(), "http");
            }
        }
        throw new UnsupportedOperationException("Unsupported type of proxy.");
    }

    private HttpUriRequest getRequest(URI uri) {
        switch (getRequestMethod()) {
            case "GET": return new HttpGet(uri);
            case "POST": return new HttpPost(uri);
            case "HEAD": return new HttpHead(uri);
            case "OPTIONS": return new HttpOptions(uri);
            case "PUT": return new HttpPut(uri);
            case "DELETE": return new HttpDelete(uri);
            case "TRACE": return new HttpTrace(uri);
            default: throw new IllegalStateException("Unsupported HTTP request method");
        }
    }

    private void doRequest() throws IOException {
        URI uri;
        try {
            uri = getURL().toURI();
        } catch (URISyntaxException e) {
            throw new IOException("Unable to construct URI from URL", e);
        }
        HttpUriRequest request = getRequest(uri);

        // request headers
        if (getIfModifiedSince() != 0) {
            request.setHeader("If-Modified-Since", DateUtils.formatDate(new Date(getIfModifiedSince())));
        }
        for (Map.Entry> prop : getRequestProperties().entrySet()) {
            for (String value : prop.getValue()) {
                request.addHeader(prop.getKey(), value);
            }
        }

        if (outputStream != null) {
            if (request instanceof HttpEntityEnclosingRequestBase) { // POST or PUT
                ((HttpEntityEnclosingRequestBase) request).setEntity(new ByteArrayEntity(outputStream.toByteArray()));
            } else {
                throw new IllegalStateException("Used HTTP request method does not support OutputStream");
            }
        }

        RequestConfig config = RequestConfig.custom()
                .setConnectTimeout(getConnectTimeout())
                .setSocketTimeout(getReadTimeout())
                .setRedirectsEnabled(getInstanceFollowRedirects())
                .setProxy(proxy)
                .build();

        HttpClientBuilder builder = HttpClientBuilder.create()
                .setDefaultCredentialsProvider(ElytronCredentialsProvider.INSTANCE)
                .setDefaultRequestConfig(config);

        if (uri.getScheme().equalsIgnoreCase("https")) {
            SSLSocketFactory socketFactory = getSSLSocketFactory();

            if (socketFactory != getDefaultSSLSocketFactory()) {
                HostnameVerifier hostnameVerifier = getHostnameVerifier();
                if (hostnameVerifier == getDefaultHostnameVerifier()) {
                    hostnameVerifier = null; // use HttpClient default
                }
                builder.setSSLSocketFactory(new SSLConnectionSocketFactory(socketFactory, hostnameVerifier));
            } else {
                try {
                    SecurityFactory sslContextFactory = ElytronCredentialsProvider.client
                            .getSSLContextFactory(uri, AuthenticationContext.captureCurrent(), null, null);
                    builder.setSSLContext(sslContextFactory.create());
                } catch (GeneralSecurityException e) {
                    throw new IOException(e);
                }
            }

            client = builder.build();
            HttpContext context = new BasicHttpContext();
            response = client.execute(request, context);

            ManagedHttpClientConnection routedConnection = (ManagedHttpClientConnection)
                    context.getAttribute(HttpCoreContext.HTTP_CONNECTION);
            sslSession = routedConnection.getSSLSession();
        } else {
            client = builder.build();
            response = client.execute(request);
        }
    }

    private void ensureResponse() {
        if (response == null) {
            try {
                doRequest();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    @Override
    public void connect() throws IOException {
        doRequest();
    }

    @Override
    public void disconnect() {
        if (client != null) {
            try {
                client.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    @Override
    public boolean usingProxy() {
        return proxy != null;
    }

    @Override
    public OutputStream getOutputStream() throws IOException {
        if (outputStream == null) {
            outputStream = new ByteArrayOutputStream();
        }
        return outputStream;
    }

    @Override
    public void setFixedLengthStreamingMode(int contentLength) {
        if (outputStream == null) {
            outputStream = new ByteArrayOutputStream(contentLength);
        }
    }

    @Override
    public void setFixedLengthStreamingMode(long contentLength) {
        if (contentLength > Integer.MAX_VALUE) {
            throw new IllegalArgumentException("Too long content length");
        }
        if (outputStream == null) {
            outputStream = new ByteArrayOutputStream((int) contentLength);
        }
    }

    @Override
    public InputStream getInputStream() throws IOException {
        if (response == null) {
            doRequest();
        }

        int responseCode = response.getStatusLine().getStatusCode();

        if (responseCode >= 400) {
            if (responseCode == HTTP_NOT_FOUND || responseCode == HTTP_GONE) {
                throw new FileNotFoundException(url.toString());
            } else {
                throw new IOException("Server returned HTTP response code: " + responseCode + " for URL: " + getURL().toString());
            }
        }

        if (responseCode == HTTP_NOT_MODIFIED) {
            return new ByteArrayInputStream(new byte[0]);
        }

        HttpEntity entity = response.getEntity();
        if (entity == null) {
            throw new IOException("Used HTTP request method does not provide InputStream");
        }
        return entity.getContent();
    }

    @Override
    public InputStream getErrorStream() {
        if (response == null || response.getStatusLine().getStatusCode() < 400) {
            return null;
        }
        HttpEntity entity = response.getEntity();
        if (entity != null) {
            try {
                return entity.getContent();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return null;
    }

    @Override
    public int getResponseCode() throws IOException {
        if (response == null) {
            doRequest();
        }

        return response.getStatusLine().getStatusCode();
    }

    @Override
    public String getResponseMessage() throws IOException {
        if (response == null) {
            doRequest();
        }

        return response.getStatusLine().getReasonPhrase();
    }

    @Override
    public String getHeaderField(String name) {
        ensureResponse();
        Header header = response.getLastHeader(name);
        if (header == null) return null;
        return header.getValue();
    }

    @Override
    public Map> getHeaderFields() {
        ensureResponse();
        Map> output = new HashMap<>();
        for (Header header : response.getAllHeaders()) {
            String name = header.getName();
            if (! output.containsKey(name)) {
                output.put(name, new ArrayList<>(1));
            }
            output.get(name).add(header.getValue());
        }
        return Collections.unmodifiableMap(output);
    }

    @Override
    public String getHeaderFieldKey(int n) {
        ensureResponse();
        Header[] headers = response.getAllHeaders();
        if (0 > n || n >= headers.length) return null;
        return headers[n].getName();
    }

    @Override
    public String getHeaderField(int n) {
        ensureResponse();
        Header[] headers = response.getAllHeaders();
        if (0 > n || n >= headers.length) return null;
        return headers[n].getValue();
    }

    @Override
    public String getCipherSuite() {
        if (response == null) {
            throw new IllegalStateException("connection not yet open");
        }
        if (sslSession == null) {
            throw new UnsupportedOperationException("not a SSL connection");
        }
        return sslSession.getCipherSuite();
    }

    @Override
    public Certificate[] getLocalCertificates() {
        if (response == null) {
            throw new IllegalStateException("connection not yet open");
        }
        if (sslSession == null) {
            throw new UnsupportedOperationException("not a SSL connection");
        }
        return sslSession.getLocalCertificates();
    }

    @Override
    public Certificate[] getServerCertificates() throws SSLPeerUnverifiedException {
        if (response == null) {
            throw new IllegalStateException("connection not yet open");
        }
        if (sslSession == null) {
            throw new UnsupportedOperationException("not a SSL connection");
        }
        return sslSession.getPeerCertificates();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy