ProxyClient.java
  1  /*
  2   * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/ProxyClient.java,v 1.5 2004/12/20 11:39:04 olegk Exp $
  3   * $Revision: 480424 $
  4   * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $
  5   *
  6   * ====================================================================
  7   *
  8   *  Licensed to the Apache Software Foundation (ASF) under one or more
  9   *  contributor license agreements.  See the NOTICE file distributed with
 10   *  this work for additional information regarding copyright ownership.
 11   *  The ASF licenses this file to You under the Apache License, Version 2.0
 12   *  (the "License"); you may not use this file except in compliance with
 13   *  the License.  You may obtain a copy of the License at
 14   *
 15   *      http://www.apache.org/licenses/LICENSE-2.0
 16   *
 17   *  Unless required by applicable law or agreed to in writing, software
 18   *  distributed under the License is distributed on an "AS IS" BASIS,
 19   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 20   *  See the License for the specific language governing permissions and
 21   *  limitations under the License.
 22   * ====================================================================
 23   *
 24   * This software consists of voluntary contributions made by many
 25   * individuals on behalf of the Apache Software Foundation.  For more
 26   * information on the Apache Software Foundation, please see
 27   * <http://www.apache.org/>.
 28   *
 29   */
 30  
 31  package org.apache.commons.httpclient;
 32  
 33  import java.io.IOException;
 34  import java.net.Socket;
 35  
 36  import org.apache.commons.httpclient.params.HttpClientParams;
 37  import org.apache.commons.httpclient.params.HttpConnectionManagerParams;
 38  import org.apache.commons.httpclient.params.HttpParams;
 39  
 40  /**
 41   * A client that provides {@link java.net.Socket sockets} for communicating through HTTP proxies
 42   * via the HTTP CONNECT method.  This is primarily needed for non-HTTP protocols that wish to 
 43   * communicate via an HTTP proxy.
 44   * 
 45   * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
 46   * @author Michael Becke
 47   * 
 48   * @since 3.0
 49   * 
 50   * @version $Revision: 480424 $
 51   */
 52  public class ProxyClient {
 53  
 54      // ----------------------------------------------------- Instance Variables
 55  
 56      /**
 57       * The {@link HttpState HTTP state} associated with this ProxyClient.
 58       */
 59      private HttpState state = new HttpState();
 60      
 61      /**
 62       * The {@link HttpClientParams collection of parameters} associated with this ProxyClient.
 63       */
 64      private HttpClientParams params = null; 
 65  
 66      /** 
 67       * The {@link HostConfiguration host configuration} associated with
 68       * the ProxyClient
 69       */
 70      private HostConfiguration hostConfiguration = new HostConfiguration();
 71      
 72      /**
 73       * Creates an instance of ProxyClient using default {@link HttpClientParams parameter set}.
 74       * 
 75       * @see HttpClientParams
 76       */
 77      public ProxyClient() {
 78          this(new HttpClientParams());
 79      }
 80  
 81      /**
 82       * Creates an instance of ProxyClient using the given 
 83       * {@link HttpClientParams parameter set}.
 84       * 
 85       * @param params The {@link HttpClientParams parameters} to use.
 86       * 
 87       * @see HttpClientParams
 88       */
 89      public ProxyClient(HttpClientParams params) {
 90          super();
 91          if (params == null) {
 92              throw new IllegalArgumentException("Params may not be null");  
 93          }
 94          this.params = params;
 95      }
 96  
 97      // ------------------------------------------------------------- Properties
 98  
 99      /**
100       * Returns {@link HttpState HTTP state} associated with the ProxyClient.
101       *
102       * @see #setState(HttpState)
103       * @return the shared client state
104       */
105      public synchronized HttpState getState() {
106          return state;
107      }
108  
109      /**
110       * Assigns {@link HttpState HTTP state} for the ProxyClient.
111       *
112       * @see #getState()
113       * @param state the new {@link HttpState HTTP state} for the client
114       */
115      public synchronized void setState(HttpState state) {
116          this.state = state;
117      }
118  
119      /**
120       * Returns the {@link HostConfiguration host configuration} associated with the 
121       * ProxyClient.
122       * 
123       * @return {@link HostConfiguration host configuration}
124       */
125      public synchronized HostConfiguration getHostConfiguration() {
126          return hostConfiguration;
127      }
128  
129      /**
130       * Assigns the {@link HostConfiguration host configuration} to use with the
131       * ProxyClient.
132       * 
133       * @param hostConfiguration The {@link HostConfiguration host configuration} to set
134       */
135      public synchronized void setHostConfiguration(HostConfiguration hostConfiguration) {
136          this.hostConfiguration = hostConfiguration;
137      }
138  
139      /**
140       * Returns {@link HttpClientParams HTTP protocol parameters} associated with this ProxyClient.
141       * 
142       * @see HttpClientParams
143       */
144      public synchronized HttpClientParams getParams() {
145          return this.params;
146      }
147  
148      /**
149       * Assigns {@link HttpClientParams HTTP protocol parameters} for this ProxyClient.
150       * 
151       * @see HttpClientParams
152       */
153      public synchronized void setParams(final HttpClientParams params) {
154          if (params == null) {
155              throw new IllegalArgumentException("Parameters may not be null");
156          }
157          this.params = params;
158      }
159  
160      /**
161       * Creates a socket that is connected, via the HTTP CONNECT method, to a proxy.
162       * 
163       * <p>
164       * Even though HTTP CONNECT proxying is generally used for HTTPS tunneling, the returned
165       * socket will not have been wrapped in an SSL socket.
166       * </p>
167       * 
168       * <p>
169       * Both the proxy and destination hosts must be set via the 
170       * {@link #getHostConfiguration() host configuration} prior to calling this method.
171       * </p>
172       * 
173       * @return the connect response
174       * 
175       * @throws IOException
176       * @throws HttpException
177       * 
178       * @see #getHostConfiguration()
179       */
180      public ConnectResponse connect() throws IOException, HttpException {
181          
182          HostConfiguration hostconf = getHostConfiguration();
183          if (hostconf.getProxyHost() == null) {
184              throw new IllegalStateException("proxy host must be configured");
185          }
186          if (hostconf.getHost() == null) {
187              throw new IllegalStateException("destination host must be configured");
188          }
189          if (hostconf.getProtocol().isSecure()) {
190              throw new IllegalStateException("secure protocol socket factory may not be used");
191          }
192          
193          ConnectMethod method = new ConnectMethod(getHostConfiguration());
194          method.getParams().setDefaults(getParams());
195          
196          DummyConnectionManager connectionManager = new DummyConnectionManager();
197          connectionManager.setConnectionParams(getParams());
198          
199          HttpMethodDirector director = new HttpMethodDirector(
200              connectionManager,
201              hostconf,
202              getParams(),
203              getState()
204          );
205          
206          director.executeMethod(method);
207          
208          ConnectResponse response = new ConnectResponse();
209          response.setConnectMethod(method);
210          
211          // only set the socket if the connect was successful
212          if (method.getStatusCode() == HttpStatus.SC_OK) {
213              response.setSocket(connectionManager.getConnection().getSocket());
214          } else {
215              connectionManager.getConnection().close();
216          }
217          
218          return response;
219      }
220  
221      /**
222       * Contains the method used to execute the connect along with the created socket.
223       */
224      public static class ConnectResponse {
225          
226          private ConnectMethod connectMethod;
227          
228          private Socket socket;
229          
230          private ConnectResponse() {}
231          
232          /**
233           * Gets the method that was used to execute the connect.  This method is useful for 
234           * analyzing the proxy's response when a connect fails.
235           * 
236           * @return the connectMethod.
237           */
238          public ConnectMethod getConnectMethod() {
239              return connectMethod;
240          }
241          /**
242           * @param connectMethod The connectMethod to set.
243           */
244          private void setConnectMethod(ConnectMethod connectMethod) {
245              this.connectMethod = connectMethod;
246          }
247          /**
248           * Gets the socket connected and authenticated (if appropriate) to the configured
249           * HTTP proxy, or <code>null</code> if a connection could not be made.  It is the
250           * responsibility of the user to close this socket when it is no longer needed.
251           * 
252           * @return the socket.
253           */
254          public Socket getSocket() {
255              return socket;
256          }
257          /**
258           * @param socket The socket to set.
259           */
260          private void setSocket(Socket socket) {
261              this.socket = socket;
262          }
263      }
264      
265      /**
266       * A connection manager that creates a single connection.  Meant to be used only once.
267       */
268      static class DummyConnectionManager implements HttpConnectionManager {
269  
270          private HttpConnection httpConnection;
271          
272          private HttpParams connectionParams;
273          
274          public void closeIdleConnections(long idleTimeout) {
275          }
276  
277          public HttpConnection getConnection() {
278              return httpConnection;
279          }
280  
281          public void setConnectionParams(HttpParams httpParams) {
282              this.connectionParams = httpParams;
283          }
284  
285          public HttpConnection getConnectionWithTimeout(
286              HostConfiguration hostConfiguration, long timeout) {
287  
288              httpConnection = new HttpConnection(hostConfiguration);
289              httpConnection.setHttpConnectionManager(this);
290              httpConnection.getParams().setDefaults(connectionParams);
291              return httpConnection;
292          }        
293          
294          /**
295           * @deprecated
296           */
297          public HttpConnection getConnection(HostConfiguration hostConfiguration, long timeout)
298              throws HttpException {
299              return getConnectionWithTimeout(hostConfiguration, timeout);
300          }
301          
302          public HttpConnection getConnection(HostConfiguration hostConfiguration) {
303              return getConnectionWithTimeout(hostConfiguration, -1);
304          }
305      
306          public void releaseConnection(HttpConnection conn) {
307          }
308  
309          public HttpConnectionManagerParams getParams() {
310              return null;
311          }
312  
313          public void setParams(HttpConnectionManagerParams params) {
314          }
315      }
316  }