1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 package org.apache.commons.httpclient;
31
32 import java.io.IOException;
33 import java.net.Socket;
34
35 import org.apache.commons.httpclient.params.HttpClientParams;
36 import org.apache.commons.httpclient.params.HttpConnectionManagerParams;
37 import org.apache.commons.httpclient.params.HttpParams;
38
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: 354155 $
51 */
52 public class ProxyClient {
53
54
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
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 if (getHostConfiguration().getProxyHost() == null) {
183 throw new IllegalStateException("proxy host must be configured");
184 }
185 if (getHostConfiguration().getHost() == null) {
186 throw new IllegalStateException("destination host must be configured");
187 }
188
189 ConnectMethod method = new ConnectMethod();
190 method.getParams().setDefaults(getParams());
191
192 DummyConnectionManager connectionManager = new DummyConnectionManager();
193 connectionManager.setConnectionParams(getParams());
194
195 HttpMethodDirector director = new HttpMethodDirector(
196 connectionManager,
197 getHostConfiguration(),
198 getParams(),
199 getState()
200 );
201
202 director.executeMethod(method);
203
204 ConnectResponse response = new ConnectResponse();
205 response.setConnectMethod(method);
206
207
208 if (method.getStatusCode() == HttpStatus.SC_OK) {
209 response.setSocket(connectionManager.getConnection().getSocket());
210 } else {
211 connectionManager.getConnection().close();
212 }
213
214 return response;
215 }
216
217 /***
218 * Contains the method used to execute the connect along with the created socket.
219 */
220 public static class ConnectResponse {
221
222 private ConnectMethod connectMethod;
223
224 private Socket socket;
225
226 private ConnectResponse() {}
227
228 /***
229 * Gets the method that was used to execute the connect. This method is useful for
230 * analyzing the proxy's response when a connect fails.
231 *
232 * @return the connectMethod.
233 */
234 public ConnectMethod getConnectMethod() {
235 return connectMethod;
236 }
237 /***
238 * @param connectMethod The connectMethod to set.
239 */
240 private void setConnectMethod(ConnectMethod connectMethod) {
241 this.connectMethod = connectMethod;
242 }
243 /***
244 * Gets the socket connected and authenticated (if appropriate) to the configured
245 * HTTP proxy, or <code>null</code> if a connection could not be made. It is the
246 * responsibility of the user to close this socket when it is no longer needed.
247 *
248 * @return the socket.
249 */
250 public Socket getSocket() {
251 return socket;
252 }
253 /***
254 * @param socket The socket to set.
255 */
256 private void setSocket(Socket socket) {
257 this.socket = socket;
258 }
259 }
260
261 /***
262 * A connection manager that creates a single connection. Meant to be used only once.
263 */
264 static class DummyConnectionManager implements HttpConnectionManager {
265
266 private HttpConnection httpConnection;
267
268 private HttpParams connectionParams;
269
270 public void closeIdleConnections(long idleTimeout) {
271 }
272
273 public HttpConnection getConnection() {
274 return httpConnection;
275 }
276
277 public void setConnectionParams(HttpParams httpParams) {
278 this.connectionParams = httpParams;
279 }
280
281 public HttpConnection getConnectionWithTimeout(
282 HostConfiguration hostConfiguration, long timeout) {
283
284 httpConnection = new HttpConnection(hostConfiguration);
285 httpConnection.setHttpConnectionManager(this);
286 httpConnection.getParams().setDefaults(connectionParams);
287 return httpConnection;
288 }
289
290 /***
291 * @deprecated
292 */
293 public HttpConnection getConnection(HostConfiguration hostConfiguration, long timeout)
294 throws HttpException {
295 return getConnectionWithTimeout(hostConfiguration, timeout);
296 }
297
298 public HttpConnection getConnection(HostConfiguration hostConfiguration) {
299 return getConnectionWithTimeout(hostConfiguration, -1);
300 }
301
302 public void releaseConnection(HttpConnection conn) {
303 }
304
305 public HttpConnectionManagerParams getParams() {
306 return null;
307 }
308
309 public void setParams(HttpConnectionManagerParams params) {
310 }
311 }
312 }