View Javadoc

1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  
28  package org.apache.http.impl.client;
29  
30  import java.io.IOException;
31  import java.net.Socket;
32  
33  import javax.net.ssl.SSLSession;
34  
35  import org.apache.http.ConnectionReuseStrategy;
36  import org.apache.http.HttpEntity;
37  import org.apache.http.HttpException;
38  import org.apache.http.HttpHost;
39  import org.apache.http.HttpRequest;
40  import org.apache.http.HttpRequestInterceptor;
41  import org.apache.http.HttpResponse;
42  import org.apache.http.ProtocolVersion;
43  import org.apache.http.auth.AuthSchemeRegistry;
44  import org.apache.http.auth.AuthScope;
45  import org.apache.http.auth.AuthState;
46  import org.apache.http.auth.Credentials;
47  import org.apache.http.client.params.AuthPolicy;
48  import org.apache.http.client.params.HttpClientParams;
49  import org.apache.http.client.protocol.ClientContext;
50  import org.apache.http.client.protocol.RequestClientConnControl;
51  import org.apache.http.client.protocol.RequestProxyAuthentication;
52  import org.apache.http.conn.HttpRoutedConnection;
53  import org.apache.http.conn.routing.HttpRoute;
54  import org.apache.http.entity.BufferedHttpEntity;
55  import org.apache.http.impl.DefaultConnectionReuseStrategy;
56  import org.apache.http.impl.DefaultHttpClientConnection;
57  import org.apache.http.impl.auth.BasicSchemeFactory;
58  import org.apache.http.impl.auth.DigestSchemeFactory;
59  import org.apache.http.impl.auth.KerberosSchemeFactory;
60  import org.apache.http.impl.auth.NTLMSchemeFactory;
61  import org.apache.http.impl.auth.SPNegoSchemeFactory;
62  import org.apache.http.message.BasicHttpRequest;
63  import org.apache.http.params.BasicHttpParams;
64  import org.apache.http.params.HttpParams;
65  import org.apache.http.params.HttpProtocolParams;
66  import org.apache.http.protocol.BasicHttpContext;
67  import org.apache.http.protocol.ExecutionContext;
68  import org.apache.http.protocol.HttpContext;
69  import org.apache.http.protocol.HttpProcessor;
70  import org.apache.http.protocol.HttpRequestExecutor;
71  import org.apache.http.protocol.ImmutableHttpProcessor;
72  import org.apache.http.protocol.RequestContent;
73  import org.apache.http.protocol.RequestTargetHost;
74  import org.apache.http.protocol.RequestUserAgent;
75  import org.apache.http.util.EntityUtils;
76  
77  public class ProxyClient {
78  
79      private final HttpProcessor httpProcessor;
80      private final HttpRequestExecutor requestExec;
81      private final ProxyAuthenticationStrategy proxyAuthStrategy;
82      private final HttpAuthenticator authenticator;
83      private final AuthState proxyAuthState;
84      private final AuthSchemeRegistry authSchemeRegistry;
85      private final ConnectionReuseStrategy reuseStrategy;
86      private final HttpParams params;
87  
88      public ProxyClient(final HttpParams params) {
89          super();
90          if (params == null) {
91              throw new IllegalArgumentException("HTTP parameters may not be null");
92          }
93          this.httpProcessor = new ImmutableHttpProcessor(new HttpRequestInterceptor[] {
94                  new RequestContent(),
95                  new RequestTargetHost(),
96                  new RequestClientConnControl(),
97                  new RequestUserAgent(),
98                  new RequestProxyAuthentication()
99          } );
100         this.requestExec = new HttpRequestExecutor();
101         this.proxyAuthStrategy = new ProxyAuthenticationStrategy();
102         this.authenticator = new HttpAuthenticator();
103         this.proxyAuthState = new AuthState();
104         this.authSchemeRegistry = new AuthSchemeRegistry();
105         this.authSchemeRegistry.register(AuthPolicy.BASIC, new BasicSchemeFactory());
106         this.authSchemeRegistry.register(AuthPolicy.DIGEST, new DigestSchemeFactory());
107         this.authSchemeRegistry.register(AuthPolicy.NTLM, new NTLMSchemeFactory());
108         this.authSchemeRegistry.register(AuthPolicy.SPNEGO, new SPNegoSchemeFactory());
109         this.authSchemeRegistry.register(AuthPolicy.KERBEROS, new KerberosSchemeFactory());
110         this.reuseStrategy = new DefaultConnectionReuseStrategy();
111         this.params = params;
112     }
113 
114     public ProxyClient() {
115         this(new BasicHttpParams());
116     }
117 
118     public HttpParams getParams() {
119         return this.params;
120     }
121 
122     public AuthSchemeRegistry getAuthSchemeRegistry() {
123         return this.authSchemeRegistry;
124     }
125 
126     public Socket tunnel(
127             final HttpHost proxy,
128             final HttpHost target,
129             final Credentials credentials) throws IOException, HttpException {
130         ProxyConnection conn = new ProxyConnection(new HttpRoute(proxy));
131         HttpContext context = new BasicHttpContext();
132         HttpResponse response = null;
133 
134         for (;;) {
135             if (!conn.isOpen()) {
136                 Socket socket = new Socket(proxy.getHostName(), proxy.getPort());
137                 conn.bind(socket, this.params);
138             }
139             String host = target.getHostName();
140             int port = target.getPort();
141             if (port < 0) {
142                 port = 80;
143             }
144 
145             StringBuilder buffer = new StringBuilder(host.length() + 6);
146             buffer.append(host);
147             buffer.append(':');
148             buffer.append(Integer.toString(port));
149 
150             String authority = buffer.toString();
151             ProtocolVersion ver = HttpProtocolParams.getVersion(this.params);
152             HttpRequest connect = new BasicHttpRequest("CONNECT", authority, ver);
153             connect.setParams(this.params);
154 
155             BasicCredentialsProvider credsProvider = new BasicCredentialsProvider();
156             credsProvider.setCredentials(new AuthScope(proxy), credentials);
157 
158             // Populate the execution context
159             context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, target);
160             context.setAttribute(ExecutionContext.HTTP_PROXY_HOST, proxy);
161             context.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
162             context.setAttribute(ExecutionContext.HTTP_REQUEST, connect);
163             context.setAttribute(ClientContext.PROXY_AUTH_STATE, this.proxyAuthState);
164             context.setAttribute(ClientContext.CREDS_PROVIDER, credsProvider);
165             context.setAttribute(ClientContext.AUTHSCHEME_REGISTRY, this.authSchemeRegistry);
166 
167             this.requestExec.preProcess(connect, this.httpProcessor, context);
168 
169             response = this.requestExec.execute(connect, conn, context);
170 
171             response.setParams(this.params);
172             this.requestExec.postProcess(response, this.httpProcessor, context);
173 
174             int status = response.getStatusLine().getStatusCode();
175             if (status < 200) {
176                 throw new HttpException("Unexpected response to CONNECT request: " +
177                         response.getStatusLine());
178             }
179 
180             if (HttpClientParams.isAuthenticating(this.params)) {
181                 if (this.authenticator.isAuthenticationRequested(proxy, response,
182                         this.proxyAuthStrategy, this.proxyAuthState, context)) {
183                     if (this.authenticator.authenticate(proxy, response,
184                             this.proxyAuthStrategy, this.proxyAuthState, context)) {
185                         // Retry request
186                         if (this.reuseStrategy.keepAlive(response, context)) {
187                             // Consume response content
188                             HttpEntity entity = response.getEntity();
189                             EntityUtils.consume(entity);
190                         } else {
191                             conn.close();
192                         }
193                     } else {
194                         break;
195                     }
196                 } else {
197                     break;
198                 }
199             }
200         }
201 
202         int status = response.getStatusLine().getStatusCode();
203 
204         if (status > 299) {
205 
206             // Buffer response content
207             HttpEntity entity = response.getEntity();
208             if (entity != null) {
209                 response.setEntity(new BufferedHttpEntity(entity));
210             }
211 
212             conn.close();
213             throw new TunnelRefusedException("CONNECT refused by proxy: " +
214                     response.getStatusLine(), response);
215         }
216         return conn.getSocket();
217     }
218 
219     static class ProxyConnection extends DefaultHttpClientConnection implements HttpRoutedConnection {
220 
221         private final HttpRoute route;
222 
223         ProxyConnection(final HttpRoute route) {
224             super();
225             this.route = route;
226         }
227 
228         public HttpRoute getRoute() {
229             return this.route;
230         }
231 
232         public boolean isSecure() {
233             return false;
234         }
235 
236         public SSLSession getSSLSession() {
237             return null;
238         }
239 
240         @Override
241         public Socket getSocket() {
242             return super.getSocket();
243         }
244 
245     }
246 
247 }