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

com.ning.http.client.providers.grizzly.ConnectionManager Maven / Gradle / Ivy

There is a newer version: 0.2.4
Show newest version
/*
 * Copyright (c) 2012-2015 Sonatype, Inc. All rights reserved.
 *
 * This program is licensed to you under the Apache License Version 2.0,
 * and you may not use this file except in compliance with the Apache License Version 2.0.
 * You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the Apache License Version 2.0 is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
 */
package com.ning.http.client.providers.grizzly;

import com.ning.http.client.AsyncHttpClientConfig;
import com.ning.http.client.ProxyServer;
import com.ning.http.client.Request;
import com.ning.http.client.uri.Uri;
import com.ning.http.util.ProxyUtils;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Locale;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import javax.net.ssl.HostnameVerifier;
import org.glassfish.grizzly.CompletionHandler;
import org.glassfish.grizzly.Connection;
import org.glassfish.grizzly.Grizzly;
import org.glassfish.grizzly.attributes.Attribute;
import org.glassfish.grizzly.impl.FutureImpl;
import org.glassfish.grizzly.nio.transport.TCPNIOConnectorHandler;
import org.glassfish.grizzly.nio.transport.TCPNIOTransport;
import org.glassfish.grizzly.utils.Futures;
import org.glassfish.grizzly.utils.IdleTimeoutFilter;

/**
 * Connection manager
 * 
 * @author Grizzly team
 */
class ConnectionManager {
    private static final Attribute DO_NOT_CACHE =
            Grizzly.DEFAULT_ATTRIBUTE_BUILDER.createAttribute(
                    ConnectionManager.class.getName());
    
    private final ConnectionPool pool;
    private final TCPNIOConnectorHandler connectionHandler;
    private final ConnectionMonitor connectionMonitor;
    private final GrizzlyAsyncHttpProvider provider;
    private final AsyncHttpClientConfig config;

    // -------------------------------------------------------- Constructors
    ConnectionManager(final GrizzlyAsyncHttpProvider provider,
            final TCPNIOTransport transport,
            final GrizzlyAsyncHttpProviderConfig providerConfig) {
        
        this.provider = provider;
        config = provider.getClientConfig();
        
        ConnectionPool connectionPool;
        if (config.isAllowPoolingConnections()) {
            ConnectionPool providedPool = providerConfig != null 
                    ? providerConfig.getConnectionPool()
                    : null;
            connectionPool = providedPool != null
                    ? providedPool
                    : new GrizzlyConnectionPool(config);
        } else {
            connectionPool = new NonCachingPool();
        }
        pool = connectionPool;
        connectionHandler = TCPNIOConnectorHandler.builder(transport).build();
        final int maxConns = config.getMaxConnections();
        connectionMonitor = new ConnectionMonitor(maxConns);
    }

    // ----------------------------------------------------- Private Methods
    static void markConnectionAsDoNotCache(final Connection c) {
        DO_NOT_CACHE.set(c, Boolean.TRUE);
    }

    static boolean isConnectionCacheable(final Connection c) {
        final Boolean canCache = DO_NOT_CACHE.get(c);
        return (canCache != null) ? canCache : false;
    }

    void getConnectionAsync(final Request request,
            final GrizzlyResponseFuture requestFuture,
            final CompletionHandler connectHandler)
            throws IOException, ExecutionException, InterruptedException {
        
        final ProxyServer proxy = ProxyUtils.getProxyServer(config, request);
        
        Connection c = pool.poll(getPartitionId(request, proxy));
        if (c == null) {
            if (!connectionMonitor.acquire()) {
                throw new IOException("Max connections exceeded");
            }
            openConnectionAsync(request, proxy, requestFuture, connectHandler);
        } else {
//            provider.touchConnection(c, request);
            connectHandler.completed(c);
        }
    }

    Connection openConnectionSync(final Request request,
            final GrizzlyResponseFuture requestFuture)
            throws IOException, ExecutionException,
            InterruptedException, TimeoutException {
        
        final Connection c = openConnectionSync0(request,
                ProxyUtils.getProxyServer(config, request), requestFuture);
        DO_NOT_CACHE.set(c, Boolean.TRUE);
        return c;
    }

    private void openConnectionAsync(final Request request,
            final ProxyServer proxy,
            final GrizzlyResponseFuture requestFuture,
            final CompletionHandler connectHandler)
            throws IOException, ExecutionException, InterruptedException {
        
        final Uri uri = request.getUri();
        String host = (proxy != null) ? proxy.getHost() : uri.getHost();
        int port = (proxy != null) ? proxy.getPort() : uri.getPort();
        CompletionHandler completionHandler =
                createConnectionCompletionHandler(request, requestFuture,
                        connectHandler);
        
        final HostnameVerifier verifier = config.getHostnameVerifier();
        if (Utils.isSecure(uri) && verifier != null) {
            completionHandler =
                    HostnameVerifierListener.wrapWithHostnameVerifierHandler(
                            completionHandler, verifier, uri.getHost());
        }
        
        connectionHandler.connect(
                new InetSocketAddress(host, getPort(uri, port)),
                new InetSocketAddress(request.getLocalAddress(), 0),
                completionHandler);
    }

    private Connection openConnectionSync0(final Request request,
            final ProxyServer proxy,
            final GrizzlyResponseFuture requestFuture)
            throws IOException, ExecutionException,
            InterruptedException, TimeoutException {
        
        final Uri uri = request.getUri();
        String host = (proxy != null) ? proxy.getHost() : uri.getHost();
        int port = (proxy != null) ? proxy.getPort() : uri.getPort();
        int cTimeout = config.getConnectTimeout();
        FutureImpl future = Futures.createSafeFuture();
        CompletionHandler ch = Futures.toCompletionHandler(future,
                createConnectionCompletionHandler(request, requestFuture, null));

        connectionHandler.connect(
                new InetSocketAddress(host, getPort(uri, port)), ch);
        
        return cTimeout > 0
                ? future.get(cTimeout, TimeUnit.MILLISECONDS)
                : future.get();
    }

    boolean returnConnection(final Request request, final Connection c) {
        final boolean result = DO_NOT_CACHE.get(c) == null &&
                pool.offer(getPartitionId(request,
                        ProxyUtils.getProxyServer(config, request)), c);
        if (result) {
            if (provider.resolver != null) {
                provider.resolver.setTimeoutMillis(c, IdleTimeoutFilter.FOREVER);
            }
        }
        return result;
    }

    boolean canReturnConnection(final Connection c) {
        return DO_NOT_CACHE.get(c) != null || pool.canCacheConnection();
    }

    void destroy() {
        pool.destroy();
    }

    private CompletionHandler createConnectionCompletionHandler(
            final Request request, final GrizzlyResponseFuture future,
            final CompletionHandler wrappedHandler) {
        
        return new CompletionHandler() {
            public void cancelled() {
                if (wrappedHandler != null) {
                    wrappedHandler.cancelled();
                } else {
                    future.cancel(true);
                }
            }

            public void failed(final Throwable throwable) {
                if (wrappedHandler != null) {
                    wrappedHandler.failed(throwable);
                } else {
                    future.abort(throwable);
                }
            }

            public void completed(final Connection connection) {
//                provider.touchConnection(connection, request);
                if (wrappedHandler != null) {
                    connection.addCloseListener(connectionMonitor);
                    wrappedHandler.completed(connection);
                }
            }

            public void updated(Connection result) {
                if (wrappedHandler != null) {
                    wrappedHandler.updated(result);
                }
            }
        };
    }

    private static String getPartitionId(Request request,
            ProxyServer proxyServer) {
        return request.getConnectionPoolPartitioning()
                .getPartitionId(request.getUri(), proxyServer);
    }

    private static int getPort(final Uri uri, final int p) {
        int port = p;
        if (port == -1) {
            final String protocol = uri.getScheme().toLowerCase(Locale.ENGLISH);
            if ("http".equals(protocol) || "ws".equals(protocol)) {
                port = 80;
            } else if ("https".equals(protocol) || "wss".equals(protocol)) {
                port = 443;
            } else {
                throw new IllegalArgumentException("Unknown protocol: " + protocol);
            }
        }
        return port;
    }
    
    // ------------------------------------------------------ Nested Classes
    private static class ConnectionMonitor implements Connection.CloseListener {

        private final Semaphore connections;
        // ------------------------------------------------------------ Constructors

        ConnectionMonitor(final int maxConnections) {
            if (maxConnections != -1) {
                connections = new Semaphore(maxConnections);
            } else {
                connections = null;
            }
        }
        // ----------------------------------- Methods from Connection.CloseListener

        public boolean acquire() {
            return connections == null || connections.tryAcquire();
        }

        @Override
        public void onClosed(Connection connection, Connection.CloseType closeType) throws IOException {
            if (connections != null) {
                connections.release();
            }
        }
    } // END ConnectionMonitor
    
    
    private static final class NonCachingPool implements ConnectionPool {


        // ---------------------------------------- Methods from ConnectionsPool


        public boolean offer(String uri, Connection connection) {
            return false;
        }

        public Connection poll(String uri) {
            return null;
        }

        public boolean removeAll(Connection connection) {
            return false;
        }

        public boolean canCacheConnection() {
            return true;
        }

        public void destroy() {
            // no-op
        }

    } // END NonCachingPool
    
} // END ConnectionManager




© 2015 - 2025 Weber Informatics LLC | Privacy Policy