001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 *
019 */
020package org.apache.mina.example.proxy;
021
022import java.net.InetSocketAddress;
023import java.net.URL;
024import java.util.ArrayList;
025import java.util.HashMap;
026import java.util.List;
027
028import org.apache.mina.core.RuntimeIoException;
029import org.apache.mina.core.future.ConnectFuture;
030import org.apache.mina.core.session.IdleStatus;
031import org.apache.mina.core.session.IoSession;
032import org.apache.mina.filter.logging.LoggingFilter;
033import org.apache.mina.proxy.ProxyConnector;
034import org.apache.mina.proxy.handlers.ProxyRequest;
035import org.apache.mina.proxy.handlers.http.HttpAuthenticationMethods;
036import org.apache.mina.proxy.handlers.http.HttpProxyConstants;
037import org.apache.mina.proxy.handlers.http.HttpProxyRequest;
038import org.apache.mina.proxy.handlers.socks.SocksProxyConstants;
039import org.apache.mina.proxy.handlers.socks.SocksProxyRequest;
040import org.apache.mina.proxy.session.ProxyIoSession;
041import org.apache.mina.transport.socket.nio.NioSocketConnector;
042
043/**
044 * ProxyTestClient.java - Base test class for mina proxy
045 * 
046 * @author <a href="http://mina.apache.org">Apache MINA Project</a>
047 * @since MINA 2.0.0-M3
048 */
049public class ProxyTestClient {
050    
051    /**
052     * The global variables used when creating the HTTP proxy connection.
053     */
054    
055    /**
056     * The user login.
057     */
058    public final static String USER = "TED_KODS";
059
060    /**
061     * The user password.
062     */
063    public final static String PWD = "EDOUARD";
064
065    /**
066     * The domain name. (used in NTLM connections)
067     */
068    public final static String DOMAIN = "MYDOMAIN";
069
070    /**
071     * The workstation name. (used in NTLM connections)
072     */    
073    public final static String WORKSTATION = "MYWORKSTATION";
074
075    /**
076     * Set this variable to true in order to generate HTTP/1.1 requests.
077     */
078    private final static boolean USE_HTTP_1_1 = false;
079
080    /**
081     * Creates a connection to the endpoint through a proxy server using the specified
082     * authentication method.
083     * 
084     * Command line arguments: 
085     *       ProxyTestClient <proxy-hostname> <proxy-port> <url> <proxy-method> 
086     *       
087     * Note that <proxy-method> is OPTIONNAL a HTTP proxy connection will be used if not 
088     * specified.
089     * 
090     * Examples:
091     *          ProxyTestClient myproxy 8080 http://mina.apache.org SOCKS4
092     *          ProxyTestClient squidsrv 3128 http://mina.apache.org:80
093     *       
094     * @param args parse arguments to get proxy hostaname, proxy port, the url to connect to 
095     * and optionnaly the proxy authentication method 
096     * @throws Exception
097     */
098    public ProxyTestClient(String[] args) throws Exception {
099        if (args.length < 3) {
100            System.out
101                    .println(ProxyTestClient.class.getName()
102                            + " <proxy-hostname> <proxy-port> <url> <proxy-method> (<proxy-method> is OPTIONNAL)");
103            return;
104        }
105
106        // Create proxy connector.
107        NioSocketConnector socketConnector = new NioSocketConnector(Runtime
108                .getRuntime().availableProcessors() + 1);
109
110        ProxyConnector connector = new ProxyConnector(socketConnector);
111
112        // Set connect timeout.
113        connector.setConnectTimeoutMillis(5000);
114
115        URL url = new URL(args[2]);
116        int port = url.getPort() == -1 ? url.getDefaultPort() : url.getPort();
117
118        ProxyRequest req = null;
119
120        if (args.length == 4) {
121            if ("SOCKS4".equals(args[3])) {
122                req = new SocksProxyRequest(
123                        SocksProxyConstants.SOCKS_VERSION_4,
124                        SocksProxyConstants.ESTABLISH_TCPIP_STREAM,
125                        new InetSocketAddress(url.getHost(), port), USER);
126            } else if ("SOCKS4a".equals(args[3])) {
127                req = new SocksProxyRequest(
128                        SocksProxyConstants.ESTABLISH_TCPIP_STREAM, url
129                                .getHost(), port, USER);
130            } else if ("SOCKS5".equals(args[3])) {
131                req = new SocksProxyRequest(
132                        SocksProxyConstants.SOCKS_VERSION_5,
133                        SocksProxyConstants.ESTABLISH_TCPIP_STREAM,
134                        new InetSocketAddress(url.getHost(), port), USER);
135                ((SocksProxyRequest) req).setPassword(PWD);
136                ((SocksProxyRequest) req)
137                        .setServiceKerberosName(Socks5GSSAPITestServer.SERVICE_NAME);
138            } else {
139                req = createHttpProxyRequest(args[2]);
140            }
141        } else {
142            req = createHttpProxyRequest(args[2]);
143        }
144
145        ProxyIoSession proxyIoSession = new ProxyIoSession(
146                new InetSocketAddress(args[0], Integer.parseInt(args[1])), req);
147
148        // Tests modifying authentication order preferences. First algorithm in list available on server 
149        // will be used for authentication.
150        List<HttpAuthenticationMethods> l = new ArrayList<HttpAuthenticationMethods>();
151        l.add(HttpAuthenticationMethods.DIGEST);
152        l.add(HttpAuthenticationMethods.BASIC);
153        proxyIoSession.setPreferedOrder(l);
154
155        connector.setProxyIoSession(proxyIoSession);
156
157        socketConnector.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 5);
158
159        connector.getFilterChain().addLast("logger", new LoggingFilter());
160
161        // This command is sent when using a socks proxy to request a page from the web server.
162        String cmd = "GET " + url.toExternalForm() + " HTTP/1.0"
163                + HttpProxyConstants.CRLF + HttpProxyConstants.CRLF;
164
165        connector.setHandler(new ClientSessionHandler(cmd));
166
167        IoSession session;
168        for (;;) {
169            try {
170                ConnectFuture future = connector.connect();
171                future.awaitUninterruptibly();
172                session = future.getSession();
173                break;
174            } catch (RuntimeIoException e) {
175                System.err.println("Failed to connect. Retrying in 5 secs ...");
176                Thread.sleep(5000);
177            }
178        }
179
180        // Wait until done
181        if (session != null) {
182            session.getCloseFuture().awaitUninterruptibly();
183        }
184        connector.dispose();
185        System.exit(0);
186    }
187
188    /**
189     * Creates a {@link HttpProxyRequest} from the provided <i>uri</i> parameter.
190     * It uses the global variables defined at the top of the class to fill the 
191     * connection properties of the request. If the global variable <i>useHttp1_1</i> 
192     * is set to true, it will create a HTTP/1.1 request.
193     * 
194     * @param uri the requested uri to connect to through the HTTP proxy
195     * @return the fully initialized {@link HttpProxyRequest} object
196     */
197    private HttpProxyRequest createHttpProxyRequest(String uri) {
198        HttpProxyRequest req = new HttpProxyRequest(uri);
199        HashMap<String, String> props = new HashMap<String, String>();
200        props.put(HttpProxyConstants.USER_PROPERTY, USER);
201        props.put(HttpProxyConstants.PWD_PROPERTY, PWD);
202        props.put(HttpProxyConstants.DOMAIN_PROPERTY, DOMAIN);
203        props.put(HttpProxyConstants.WORKSTATION_PROPERTY, WORKSTATION);
204
205        req.setProperties(props);
206        if (USE_HTTP_1_1) {
207            req.setHttpVersion(HttpProxyConstants.HTTP_1_1);
208        }
209
210        return req;
211    }
212
213    /**
214     * {@inheritDoc}
215     */
216    public static void main(String[] args) throws Exception {
217        new ProxyTestClient(args);
218    }
219}