Index: java/org/apache/commons/httpclient/HttpConnection.java =================================================================== retrieving revision 1.85 diff -u -r1.85 HttpConnection.java --- java/org/apache/commons/httpclient/HttpConnection.java 22 Feb 2004 18:08:45 -0000 1.85 +++ java/org/apache/commons/httpclient/HttpConnection.java 12 Apr 2004 02:35:19 -0000 @@ -199,6 +199,15 @@ } // ------------------------------------------ Attribute Setters and Getters + + /** + * Returns the connection socket. + * + * @return the socket. + */ + protected Socket getSocket() { + return this.socket; + } /** * Returns the host. Index: src/examples/ProxyTunnelDemo.java =================================================================== RCS file: src/examples/ProxyTunnelDemo.java diff -N src/examples/ProxyTunnelDemo.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/examples/ProxyTunnelDemo.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,96 @@ +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.net.Socket; + +import org.apache.commons.httpclient.ProxyClient; +import org.apache.commons.httpclient.UsernamePasswordCredentials; +import org.apache.commons.httpclient.auth.HttpAuthRealm; + +/* + * $Header$ + * $Revision$ + * $Date$ + * ==================================================================== + * + * Copyright 2002-2004 The Apache Software Foundation + * + * 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + * [Additional notices, if required by prior licensing conditions] + * + */ + +/** + * Example code for using {@link org.apache.commons.httpclient.ProxyClient}. + * + * @author Oleg Kalnichevski + * @author Michael Becke + */ +public class ProxyTunnelDemo { + + public static void main(String[] args) throws Exception { + + ProxyClient proxyclient = new ProxyClient(); + // set the host the proxy should create a connection to + // + // Note: By default port 80 will be used. Some proxies only allow conections + // to ports 443 and 8443. This is because the HTTP CONNECT method was intented + // to be used for tunneling HTTPS. + proxyclient.getHostConfiguration().setHost("www.yahoo.com"); + // set the proxy host and port + proxyclient.getHostConfiguration().setProxy("10.0.1.1", 3128); + // set the proxy credentials, only necessary for authenticating proxies + proxyclient.getState().setProxyCredentials( + new HttpAuthRealm("10.0.1.1", 3128, null), + new UsernamePasswordCredentials("proxy", "proxy")); + + // create the socket + ProxyClient.ConnectResponse response = proxyclient.connect(); + + if (response.getSocket() != null) { + Socket socket = response.getSocket(); + try { + // go ahead and do an HTTP GET using the socket + Writer out = new OutputStreamWriter( + socket.getOutputStream(), "ISO-8859-1"); + out.write("GET http://www.yahoo.com/ HTTP/1.1\r\n"); + out.write("Host: www.yahoo.com\r\n"); + out.write("Agent: whatever\r\n"); + out.write("\r\n"); + out.flush(); + BufferedReader in = new BufferedReader( + new InputStreamReader(socket.getInputStream(), "ISO-8859-1")); + String line = null; + while ((line = in.readLine()) != null) { + System.out.println(line); + } + } finally { + // be sure to close the socket when we're done + socket.close(); + } + } else { + // the proxy connect was not successful, check connect method for reasons why + System.out.println("Connect failed: " + response.getConnectMethod().getStatusLine()); + System.out.println(response.getConnectMethod().getResponseBodyAsString()); + } + } + +} Index: src/java/org/apache/commons/httpclient/ProxyClient.java =================================================================== RCS file: src/java/org/apache/commons/httpclient/ProxyClient.java diff -N src/java/org/apache/commons/httpclient/ProxyClient.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/java/org/apache/commons/httpclient/ProxyClient.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,308 @@ +/* + * $Header$ + * $Revision$ + * $Date$ + * + * ==================================================================== + * + * Copyright 1999-2004 The Apache Software Foundation + * + * 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + * [Additional notices, if required by prior licensing conditions] + * + */ + +package org.apache.commons.httpclient; + +import java.io.IOException; +import java.net.Socket; + +import org.apache.commons.httpclient.params.HttpClientParams; +import org.apache.commons.httpclient.params.HttpConnectionManagerParams; +import org.apache.commons.httpclient.params.HttpParams; + + +/** + * A client that provides {@link java.net.Socket sockets} for communicating through HTTP proxies + * via the HTTP CONNECT method. This is primarily needed for non-HTTP protocols that wish to + * communicate via an HTTP proxy. + * + * @author Oleg Kalnichevski + * @author Michael Becke + * + * @since 3.0 + * + * @version $Revision$ + */ +public class ProxyClient { + + // ----------------------------------------------------- Instance Variables + + /** + * The {@link HttpState HTTP state} associated with this ProxyClient. + */ + private HttpState state = new HttpState(); + + /** + * The {@link HttpClientParams collection of parameters} associated with this ProxyClient. + */ + private HttpClientParams params = null; + + /** + * The {@link HostConfiguration host configuration} associated with + * the ProxyClient + */ + private HostConfiguration hostConfiguration = new HostConfiguration(); + + /** + * Creates an instance of ProxyClient using default {@link HttpClientParams parameter set}. + * + * @see HttpClientParams + */ + public ProxyClient() { + this(new HttpClientParams()); + } + + /** + * Creates an instance of ProxyClient using the given + * {@link HttpClientParams parameter set}. + * + * @param params The {@link HttpClientParams parameters} to use. + * + * @see HttpClientParams + */ + public ProxyClient(HttpClientParams params) { + super(); + if (params == null) { + throw new IllegalArgumentException("Params may not be null"); + } + this.params = params; + } + + // ------------------------------------------------------------- Properties + + /** + * Returns {@link HttpState HTTP state} associated with the ProxyClient. + * + * @see #setState(HttpState) + * @return the shared client state + */ + public synchronized HttpState getState() { + return state; + } + + /** + * Assigns {@link HttpState HTTP state} for the ProxyClient. + * + * @see #getState() + * @param state the new {@link HttpState HTTP state} for the client + */ + public synchronized void setState(HttpState state) { + this.state = state; + } + + /** + * Returns the {@link HostConfiguration host configuration} associated with the + * ProxyClient. + * + * @return {@link HostConfiguration host configuration} + */ + public synchronized HostConfiguration getHostConfiguration() { + return hostConfiguration; + } + + /** + * Assigns the {@link HostConfiguration host configuration} to use with the + * ProxyClient. + * + * @param hostConfiguration The {@link HostConfiguration host configuration} to set + */ + public synchronized void setHostConfiguration(HostConfiguration hostConfiguration) { + this.hostConfiguration = hostConfiguration; + } + + /** + * Returns {@link HttpClientParams HTTP protocol parameters} associated with this ProxyClient. + * + * @see HttpClientParams + */ + public synchronized HttpClientParams getParams() { + return this.params; + } + + /** + * Assigns {@link HttpClientParams HTTP protocol parameters} for this ProxyClient. + * + * @see HttpClientParams + */ + public synchronized void setParams(final HttpClientParams params) { + if (params == null) { + throw new IllegalArgumentException("Parameters may not be null"); + } + this.params = params; + } + + /** + * Creates a socket that is connected, via the HTTP CONNECT method, to a proxy. + * + *

+ * Even though HTTP CONNECT proxying is generally used for HTTPS tunneling, the returned + * socket will not have been wrapped in an SSL socket. + *

+ * + *

+ * Both the proxy and destination hosts must be set via the + * {@link #getHostConfiguration() host configuration} prior to calling this method. + *

+ * + * @return the connect response + * + * @throws IOException + * @throws HttpException + * + * @see #getHostConfiguration() + */ + public ConnectResponse connect() throws IOException, HttpException { + + if (!getHostConfiguration().isProxySet()) { + throw new IllegalStateException("proxy host must be configured"); + } + if (!getHostConfiguration().isHostSet()) { + throw new IllegalStateException("destination host must be configured"); + } + + ConnectMethod method = new ConnectMethod(); + method.getParams().setDefaults(getParams()); + + DummyConnectionManager connectionManager = new DummyConnectionManager(); + connectionManager.setConnectionParams(getParams()); + + HttpMethodDirector director = new HttpMethodDirector( + connectionManager, + getHostConfiguration(), + getParams(), + getState() + ); + + director.executeMethod(method); + + ConnectResponse response = new ConnectResponse(); + response.setConnectMethod(method); + + // only set the socket if the connect was successful + if (method.getStatusCode() == HttpStatus.SC_OK) { + response.setSocket(connectionManager.getConnection().getSocket()); + } else { + connectionManager.getConnection().close(); + } + + return response; + } + + /** + * Contains the method used to execute the connect along with the created socket. + */ + public static class ConnectResponse { + + private ConnectMethod connectMethod; + + private Socket socket; + + private ConnectResponse() {} + + /** + * Gets the method that was used to execute the connect. This method is useful for + * analyzing the proxy's response when a connect fails. + * + * @return the connectMethod. + */ + public ConnectMethod getConnectMethod() { + return connectMethod; + } + /** + * @param connectMethod The connectMethod to set. + */ + private void setConnectMethod(ConnectMethod connectMethod) { + this.connectMethod = connectMethod; + } + /** + * Gets the socket connected and authenticated (if appropriate) to the configured + * HTTP proxy, or null if a connection could not be made. It is the + * responsibility of the user to close this socket when it is no longer needed. + * + * @return the socket. + */ + public Socket getSocket() { + return socket; + } + /** + * @param socket The socket to set. + */ + private void setSocket(Socket socket) { + this.socket = socket; + } + } + + /** + * A connection manager that creates a single connection. Meant to be used only once. + */ + class DummyConnectionManager implements HttpConnectionManager { + + private HttpConnection httpConnection; + + private HttpParams connectionParams; + + public HttpConnection getConnection() { + return httpConnection; + } + + public void setConnectionParams(HttpParams httpParams) { + this.connectionParams = httpParams; + } + + public HttpConnection getConnectionWithTimeout( + HostConfiguration hostConfiguration, long timeout) { + + httpConnection = new HttpConnection(hostConfiguration); + httpConnection.setHttpConnectionManager(this); + httpConnection.getParams().setDefaults(connectionParams); + return httpConnection; + } + + public HttpConnection getConnection(HostConfiguration hostConfiguration, long timeout) + throws HttpException { + return getConnectionWithTimeout(hostConfiguration, timeout); + } + + public HttpConnection getConnection(HostConfiguration hostConfiguration) { + return getConnectionWithTimeout(hostConfiguration, -1); + } + + public void releaseConnection(HttpConnection conn) { + } + + public HttpConnectionManagerParams getParams() { + return null; + } + + public void setParams(HttpConnectionManagerParams params) { + } + } +}