View Javadoc

    1   /*
    2    *  Licensed to the Apache Software Foundation (ASF) under one
    3    *  or more contributor license agreements.  See the NOTICE file
    4    *  distributed with this work for additional information
    5    *  regarding copyright ownership.  The ASF licenses this file
    6    *  to you under the Apache License, Version 2.0 (the
    7    *  "License"); you may not use this file except in compliance
    8    *  with the License.  You may obtain a copy of the License at
    9    *
    10   *    http://www.apache.org/licenses/LICENSE-2.0
    11   *
    12   *  Unless required by applicable law or agreed to in writing,
    13   *  software distributed under the License is distributed on an
    14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    15   *  KIND, either express or implied.  See the License for the
    16   *  specific language governing permissions and limitations
    17   *  under the License.
    18   *
    19   */
    20  package org.apache.mina.proxy;
    21
    22  import java.net.InetSocketAddress;
    23  import java.net.SocketAddress;
    24  import java.util.concurrent.Executor;
    25
    26  import org.apache.mina.core.buffer.IoBuffer;
    27  import org.apache.mina.core.file.FileRegion;
    28  import org.apache.mina.core.filterchain.IoFilter;
    29  import org.apache.mina.core.future.ConnectFuture;
    30  import org.apache.mina.core.future.DefaultConnectFuture;
    31  import org.apache.mina.core.service.AbstractIoConnector;
    32  import org.apache.mina.core.service.DefaultTransportMetadata;
    33  import org.apache.mina.core.service.IoHandler;
    34  import org.apache.mina.core.service.TransportMetadata;
    35  import org.apache.mina.core.session.IoSession;
    36  import org.apache.mina.core.session.IoSessionConfig;
    37  import org.apache.mina.core.session.IoSessionInitializer;
    38  import org.apache.mina.proxy.filter.ProxyFilter;
    39  import org.apache.mina.proxy.handlers.socks.SocksProxyRequest;
    40  import org.apache.mina.proxy.session.ProxyIoSession;
    41  import org.apache.mina.proxy.session.ProxyIoSessionInitializer;
    42  import org.apache.mina.transport.socket.DefaultSocketSessionConfig;
    43  import org.apache.mina.transport.socket.SocketConnector;
    44  import org.apache.mina.transport.socket.SocketSessionConfig;
    45
    46  /**
    47   * ProxyConnector.java - Decorator for {@link SocketConnector} to provide proxy support,
    48   * as suggested by MINA list discussions.
    49   * <p>
    50   * Operates by intercepting connect requests and replacing the endpoint address with the
    51   * proxy address, then adding a {@link ProxyFilter} as the first {@link IoFilter} which
    52   * performs any necessary handshaking with the proxy before allowing data to flow
    53   * normally. During the handshake, any outgoing write requests are buffered.
    54   *
    55   * @see        http://www.nabble.com/Meta-Transport%3A-an-idea-on-implementing-reconnection-and-proxy-td12969001.html
    56   * @see        http://issues.apache.org/jira/browse/DIRMINA-415
    57   *
    58   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
    59   * @since MINA 2.0.0-M3
    60   */
    61  public class ProxyConnector extends AbstractIoConnector {
    62      private static final TransportMetadata METADATA = new DefaultTransportMetadata("proxy", "proxyconnector", false,
    63              true, InetSocketAddress.class, SocketSessionConfig.class, IoBuffer.class, FileRegion.class);
    64
    65      /**
    66       * Wrapped connector to use for outgoing TCP connections.
    67       */
    68      private SocketConnector connector = null;
    69
    70      /**
    71       * Proxy filter instance.
    72       */
    73      private final ProxyFilter proxyFilter = new ProxyFilter();
    74
    75      /**
    76       * The {@link ProxyIoSession} in use.
    77       */
    78      private ProxyIoSession proxyIoSession;
    79
    80      /**
    81       * This future will notify it's listeners when really connected to the target
    82       */
    83      private DefaultConnectFuture future;
    84
    85      /**
    86       * Creates a new proxy connector.
    87       */
    88      public ProxyConnector() {
    89          super(new DefaultSocketSessionConfig(), null);
    90      }
    91
    92      /**
    93       * Creates a new proxy connector.
    94       *
    95       * @param connector Connector used to establish proxy connections.
    96       */
    97      public ProxyConnector(final SocketConnector connector) {
    98          this(connector, new DefaultSocketSessionConfig(), null);
    99      }
    100
    101     /**
    102      * Creates a new proxy connector.
    103      * @see AbstractIoConnector(IoSessionConfig, Executor).
    104      */
    105     public ProxyConnector(final SocketConnector connector, IoSessionConfig config, Executor executor) {
    106         super(config, executor);
    107         setConnector(connector);
    108     }
    109
    110     /**
    111      * {@inheritDoc}
    112      */
    113     @Override
    114     public IoSessionConfig getSessionConfig() {
    115         return connector.getSessionConfig();
    116     }
    117
    118     /**
    119      * Returns the {@link ProxyIoSession} linked with this connector.
    120      */
    121     public ProxyIoSession getProxyIoSession() {
    122         return proxyIoSession;
    123     }
    124
    125     /**
    126      * Sets the proxy session object of this connector.
    127      * @param proxyIoSession the configuration of this connector.
    128      */
    129     public void setProxyIoSession(ProxyIoSession proxyIoSession) {
    130         if (proxyIoSession == null) {
    131             throw new IllegalArgumentException("proxySession object cannot be null");
    132         }
    133
    134         if (proxyIoSession.getProxyAddress() == null) {
    135             throw new IllegalArgumentException("proxySession.proxyAddress cannot be null");
    136         }
    137
    138         proxyIoSession.setConnector(this);
    139         setDefaultRemoteAddress(proxyIoSession.getProxyAddress());
    140         this.proxyIoSession = proxyIoSession;
    141     }
    142
    143     /**
    144      * Connects to the specified <code>address</code>.  If communication starts
    145      * successfully, events are fired to the connector's <code>handler</code>.
    146      *
    147      * @param remoteAddress the remote address to connect to
    148      * @param localAddress the local address
    149      * @param sessionInitializer the session initializer
    150      * @return {@link ConnectFuture} that will tell the result of the connection attempt
    151      */
    152     @SuppressWarnings("unchecked")
    153     @Override
    154     protected ConnectFuture connect0(final SocketAddress remoteAddress, final SocketAddress localAddress,
    155             final IoSessionInitializer<? extends ConnectFuture> sessionInitializer) {
    156         if (!proxyIoSession.isReconnectionNeeded()) {
    157             // First connection
    158             IoHandler handler = getHandler();
    159             if (!(handler instanceof AbstractProxyIoHandler)) {
    160                 throw new IllegalArgumentException("IoHandler must be an instance of AbstractProxyIoHandler");
    161             }
    162
    163             connector.setHandler(handler);
    164             future = new DefaultConnectFuture();
    165         }
    166
    167         ConnectFuture conFuture = connector.connect(proxyIoSession.getProxyAddress(), new ProxyIoSessionInitializer(
    168                 sessionInitializer, proxyIoSession));
    169
    170         // If proxy does not use reconnection like socks the connector's
    171         // future is returned. If we're in the middle of a reconnection
    172         // then we send back the connector's future which is only used
    173         // internally while <code>future</code> will be used to notify
    174         // the user of the connection state.
    175         if (proxyIoSession.getRequest() instanceof SocksProxyRequest || proxyIoSession.isReconnectionNeeded()) {
    176             return conFuture;
    177         }
    178
    179         return future;
    180     }
    181
    182     /**
    183      * Cancels the real connection when reconnection is in use.
    184      */
    185     public void cancelConnectFuture() {
    186         future.cancel();
    187     }
    188
    189     /**
    190      * Fires the connection event on the real future to notify the client.
    191      *
    192      * @param session the current session
    193      * @return the future holding the connected session
    194      */
    195     protected ConnectFuture fireConnected(final IoSession session) {
    196         future.setSession(session);
    197         return future;
    198     }
    199
    200     /**
    201      * Get the {@link SocketConnector} to be used for connections
    202      * to the proxy server.
    203      */
    204     public final SocketConnector getConnector() {
    205         return connector;
    206     }
    207
    208     /**
    209      * Sets the {@link SocketConnector} to be used for connections
    210      * to the proxy server.
    211      *
    212      * @param connector the connector to use
    213      */
    214     private final void setConnector(final SocketConnector connector) {
    215         if (connector == null) {
    216             throw new IllegalArgumentException("connector cannot be null");
    217         }
    218
    219         this.connector = connector;
    220         String className = ProxyFilter.class.getName();
    221
    222         // Removes an old ProxyFilter instance from the chain
    223         if (connector.getFilterChain().contains(className)) {
    224             connector.getFilterChain().remove(className);
    225         }
    226
    227         // Insert the ProxyFilter as the first filter in the filter chain builder
    228         connector.getFilterChain().addFirst(className, proxyFilter);
    229     }
    230
    231     /**
    232      * {@inheritDoc}
    233      */
    234     @Override
    235     protected void dispose0() throws Exception {
    236         if (connector != null) {
    237             connector.dispose();
    238         }
    239     }
    240
    241     /**
    242      * {@inheritDoc}
    243      */
    244     public TransportMetadata getTransportMetadata() {
    245         return METADATA;
    246     }
    247 }