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 org.apache.http.ConnectionReuseStrategy;
34  import org.apache.http.HttpEntity;
35  import org.apache.http.HttpException;
36  import org.apache.http.HttpHost;
37  import org.apache.http.HttpRequest;
38  import org.apache.http.HttpRequestInterceptor;
39  import org.apache.http.HttpResponse;
40  import org.apache.http.HttpVersion;
41  import org.apache.http.auth.AUTH;
42  import org.apache.http.auth.AuthSchemeRegistry;
43  import org.apache.http.auth.AuthScope;
44  import org.apache.http.auth.AuthState;
45  import org.apache.http.auth.Credentials;
46  import org.apache.http.client.config.AuthSchemes;
47  import org.apache.http.client.config.RequestConfig;
48  import org.apache.http.client.params.HttpClientParamConfig;
49  import org.apache.http.client.protocol.ClientContext;
50  import org.apache.http.client.protocol.RequestClientConnControl;
51  import org.apache.http.config.ConnectionConfig;
52  import org.apache.http.conn.HttpConnectionFactory;
53  import org.apache.http.conn.ManagedHttpClientConnection;
54  import org.apache.http.conn.routing.HttpRoute;
55  import org.apache.http.conn.routing.RouteInfo.LayerType;
56  import org.apache.http.conn.routing.RouteInfo.TunnelType;
57  import org.apache.http.entity.BufferedHttpEntity;
58  import org.apache.http.impl.DefaultConnectionReuseStrategy;
59  import org.apache.http.impl.auth.BasicSchemeFactory;
60  import org.apache.http.impl.auth.DigestSchemeFactory;
61  import org.apache.http.impl.auth.KerberosSchemeFactory;
62  import org.apache.http.impl.auth.NTLMSchemeFactory;
63  import org.apache.http.impl.auth.SPNegoSchemeFactory;
64  import org.apache.http.impl.conn.ManagedHttpClientConnectionFactory;
65  import org.apache.http.message.BasicHttpRequest;
66  import org.apache.http.params.BasicHttpParams;
67  import org.apache.http.params.HttpParamConfig;
68  import org.apache.http.params.HttpParams;
69  import org.apache.http.protocol.BasicHttpContext;
70  import org.apache.http.protocol.ExecutionContext;
71  import org.apache.http.protocol.HttpContext;
72  import org.apache.http.protocol.HttpProcessor;
73  import org.apache.http.protocol.HttpRequestExecutor;
74  import org.apache.http.protocol.ImmutableHttpProcessor;
75  import org.apache.http.protocol.RequestUserAgent;
76  import org.apache.http.util.Args;
77  import org.apache.http.util.EntityUtils;
78  
79  @SuppressWarnings("deprecation")
80  public class ProxyClient {
81  
82      private final HttpConnectionFactory<ManagedHttpClientConnection> connFactory;
83      private final ConnectionConfig connectionConfig;
84      private final RequestConfig requestConfig;
85      private final HttpProcessor httpProcessor;
86      private final HttpRequestExecutor requestExec;
87      private final ProxyAuthenticationStrategy proxyAuthStrategy;
88      private final HttpAuthenticator authenticator;
89      private final AuthState proxyAuthState;
90      private final AuthSchemeRegistry authSchemeRegistry;
91      private final ConnectionReuseStrategy reuseStrategy;
92  
93      /**
94       * @since 4.3
95       */
96      public ProxyClient(
97              final HttpConnectionFactory<ManagedHttpClientConnection> connFactory,
98              final ConnectionConfig connectionConfig,
99              final RequestConfig requestConfig) {
100         super();
101         this.connFactory = connFactory != null ? connFactory : ManagedHttpClientConnectionFactory.INSTANCE;
102         this.connectionConfig = connectionConfig != null ? connectionConfig : ConnectionConfig.DEFAULT;
103         this.requestConfig = requestConfig != null ? requestConfig : RequestConfig.DEFAULT;
104         this.httpProcessor = new ImmutableHttpProcessor(new HttpRequestInterceptor[] {
105                 new RequestClientConnControl(),
106                 new RequestUserAgent()
107         } );
108         this.requestExec = new HttpRequestExecutor();
109         this.proxyAuthStrategy = new ProxyAuthenticationStrategy();
110         this.authenticator = new HttpAuthenticator();
111         this.proxyAuthState = new AuthState();
112         this.authSchemeRegistry = new AuthSchemeRegistry();
113         this.authSchemeRegistry.register(AuthSchemes.BASIC, new BasicSchemeFactory());
114         this.authSchemeRegistry.register(AuthSchemes.DIGEST, new DigestSchemeFactory());
115         this.authSchemeRegistry.register(AuthSchemes.NTLM, new NTLMSchemeFactory());
116         this.authSchemeRegistry.register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory());
117         this.authSchemeRegistry.register(AuthSchemes.KERBEROS, new KerberosSchemeFactory());
118         this.reuseStrategy = new DefaultConnectionReuseStrategy();
119     }
120 
121     /**
122      * @deprecated (4.3) use {@link ProxyClient#ProxyClient(HttpConnectionFactory, ConnectionConfig, RequestConfig)}
123      * @param params
124      */
125     @Deprecated
126     public ProxyClient(final HttpParams params) {
127         this(null,
128                 HttpParamConfig.getConnectionConfig(params),
129                 HttpClientParamConfig.getRequestConfig(params));
130     }
131 
132     /**
133      * @since 4.3
134      */
135     public ProxyClient(final RequestConfig requestConfig) {
136         this(null, null, requestConfig);
137     }
138 
139     public ProxyClient() {
140         this(null, null, null);
141     }
142 
143     /**
144      * @deprecated (4.3) do not use.
145      */
146     @Deprecated
147     public HttpParams getParams() {
148         return new BasicHttpParams();
149     }
150 
151     /**
152      * @deprecated (4.3) do not use.
153      */
154     @Deprecated
155     public AuthSchemeRegistry getAuthSchemeRegistry() {
156         return this.authSchemeRegistry;
157     }
158 
159     public Socket tunnel(
160             final HttpHost proxy,
161             final HttpHost target,
162             final Credentials credentials) throws IOException, HttpException {
163         Args.notNull(proxy, "Proxy host");
164         Args.notNull(target, "Target host");
165         Args.notNull(credentials, "Credentials");
166         HttpHost host = target;
167         if (host.getPort() <= 0) {
168             host = new HttpHost(host.getHostName(), 80, host.getSchemeName());
169         }
170         final HttpRoute route = new HttpRoute(
171                 host,
172                 this.requestConfig.getLocalAddress(),
173                 proxy, false, TunnelType.TUNNELLED, LayerType.PLAIN);
174 
175         final ManagedHttpClientConnection conn = this.connFactory.create(this.connectionConfig);
176         final HttpContext context = new BasicHttpContext();
177         HttpResponse response = null;
178 
179         final HttpRequest connect = new BasicHttpRequest(
180                 "CONNECT", host.toHostString(), HttpVersion.HTTP_1_1);
181 
182         final BasicCredentialsProvider credsProvider = new BasicCredentialsProvider();
183         credsProvider.setCredentials(new AuthScope(proxy), credentials);
184 
185         // Populate the execution context
186         context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, target);
187         context.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
188         context.setAttribute(ExecutionContext.HTTP_REQUEST, connect);
189         context.setAttribute(ClientContext.ROUTE, route);
190         context.setAttribute(ClientContext.PROXY_AUTH_STATE, this.proxyAuthState);
191         context.setAttribute(ClientContext.CREDS_PROVIDER, credsProvider);
192         context.setAttribute(ClientContext.AUTHSCHEME_REGISTRY, this.authSchemeRegistry);
193         context.setAttribute(ClientContext.REQUEST_CONFIG, this.requestConfig);
194 
195         this.requestExec.preProcess(connect, this.httpProcessor, context);
196 
197         for (;;) {
198             if (!conn.isOpen()) {
199                 final Socket socket = new Socket(proxy.getHostName(), proxy.getPort());
200                 conn.bind(socket);
201             }
202 
203             this.authenticator.generateAuthResponse(connect, this.proxyAuthState, context);
204 
205             response = this.requestExec.execute(connect, conn, context);
206 
207             final int status = response.getStatusLine().getStatusCode();
208             if (status < 200) {
209                 throw new HttpException("Unexpected response to CONNECT request: " +
210                         response.getStatusLine());
211             }
212             if (this.authenticator.isAuthenticationRequested(proxy, response,
213                     this.proxyAuthStrategy, this.proxyAuthState, context)) {
214                 if (this.authenticator.handleAuthChallenge(proxy, response,
215                         this.proxyAuthStrategy, this.proxyAuthState, context)) {
216                     // Retry request
217                     if (this.reuseStrategy.keepAlive(response, context)) {
218                         // Consume response content
219                         final HttpEntity entity = response.getEntity();
220                         EntityUtils.consume(entity);
221                     } else {
222                         conn.close();
223                     }
224                     // discard previous auth header
225                     connect.removeHeaders(AUTH.PROXY_AUTH_RESP);
226                 } else {
227                     break;
228                 }
229             } else {
230                 break;
231             }
232         }
233 
234         final int status = response.getStatusLine().getStatusCode();
235 
236         if (status > 299) {
237 
238             // Buffer response content
239             final HttpEntity entity = response.getEntity();
240             if (entity != null) {
241                 response.setEntity(new BufferedHttpEntity(entity));
242             }
243 
244             conn.close();
245             throw new TunnelRefusedException("CONNECT refused by proxy: " +
246                     response.getStatusLine(), response);
247         }
248         return conn.getSocket();
249     }
250 
251 }