View Javadoc

1   /*
2    * $HeadURL: https://svn.apache.org/repos/asf/httpcomponents/oac.hc3x/trunk/src/java/org/apache/commons/httpclient/ProxyClient.java $
3    * $Revision: 1425331 $
4    * $Date: 2012-12-22 19:29:41 +0100 (Sat, 22 Dec 2012) $
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: 1425331 $
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 }