/*
 * ====================================================================
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 *
 */

package org.apache.http.impl.client;

import java.io.IOException;
import java.net.Socket;

import org.apache.http.ConnectionReuseStrategy;
import org.apache.http.HttpEntity;
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.auth.AUTH;
import org.apache.http.auth.AuthSchemeRegistry;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.AuthState;
import org.apache.http.auth.Credentials;
import org.apache.http.client.config.AuthSchemes;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.params.HttpClientParamConfig;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.client.protocol.RequestClientConnControl;
import org.apache.http.config.ConnectionConfig;
import org.apache.http.conn.HttpConnectionFactory;
import org.apache.http.conn.ManagedHttpClientConnection;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.routing.RouteInfo.LayerType;
import org.apache.http.conn.routing.RouteInfo.TunnelType;
import org.apache.http.entity.BufferedHttpEntity;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.impl.auth.BasicSchemeFactory;
import org.apache.http.impl.auth.DigestSchemeFactory;
import org.apache.http.impl.auth.HttpAuthenticator;
import org.apache.http.impl.auth.KerberosSchemeFactory;
import org.apache.http.impl.auth.NTLMSchemeFactory;
import org.apache.http.impl.auth.SPNegoSchemeFactory;
import org.apache.http.impl.conn.ManagedHttpClientConnectionFactory;
import org.apache.http.impl.execchain.TunnelRefusedException;
import org.apache.http.message.BasicHttpRequest;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParamConfig;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpCoreContext;
import org.apache.http.protocol.HttpProcessor;
import org.apache.http.protocol.HttpRequestExecutor;
import org.apache.http.protocol.ImmutableHttpProcessor;
import org.apache.http.protocol.RequestTargetHost;
import org.apache.http.protocol.RequestUserAgent;
import org.apache.http.util.Args;
import org.apache.http.util.EntityUtils;

ProxyClient can be used to establish a tunnel via an HTTP proxy.
/** * ProxyClient can be used to establish a tunnel via an HTTP proxy. */
@SuppressWarnings("deprecation") public class ProxyClient { private final HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory; private final ConnectionConfig connectionConfig; private final RequestConfig requestConfig; private final HttpProcessor httpProcessor; private final HttpRequestExecutor requestExec; private final ProxyAuthenticationStrategy proxyAuthStrategy; private final HttpAuthenticator authenticator; private final AuthState proxyAuthState; private final AuthSchemeRegistry authSchemeRegistry; private final ConnectionReuseStrategy reuseStrategy;
Since:4.3
/** * @since 4.3 */
public ProxyClient( final HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory, final ConnectionConfig connectionConfig, final RequestConfig requestConfig) { super(); this.connFactory = connFactory != null ? connFactory : ManagedHttpClientConnectionFactory.INSTANCE; this.connectionConfig = connectionConfig != null ? connectionConfig : ConnectionConfig.DEFAULT; this.requestConfig = requestConfig != null ? requestConfig : RequestConfig.DEFAULT; this.httpProcessor = new ImmutableHttpProcessor( new RequestTargetHost(), new RequestClientConnControl(), new RequestUserAgent()); this.requestExec = new HttpRequestExecutor(); this.proxyAuthStrategy = new ProxyAuthenticationStrategy(); this.authenticator = new HttpAuthenticator(); this.proxyAuthState = new AuthState(); this.authSchemeRegistry = new AuthSchemeRegistry(); this.authSchemeRegistry.register(AuthSchemes.BASIC, new BasicSchemeFactory()); this.authSchemeRegistry.register(AuthSchemes.DIGEST, new DigestSchemeFactory()); this.authSchemeRegistry.register(AuthSchemes.NTLM, new NTLMSchemeFactory()); this.authSchemeRegistry.register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory()); this.authSchemeRegistry.register(AuthSchemes.KERBEROS, new KerberosSchemeFactory()); this.reuseStrategy = new DefaultConnectionReuseStrategy(); }
Deprecated:(4.3) use ProxyClient(HttpConnectionFactory<HttpRoute,ManagedHttpClientConnection>, ConnectionConfig, RequestConfig)
/** * @deprecated (4.3) use {@link ProxyClient#ProxyClient(HttpConnectionFactory, ConnectionConfig, RequestConfig)} */
@Deprecated public ProxyClient(final HttpParams params) { this(null, HttpParamConfig.getConnectionConfig(params), HttpClientParamConfig.getRequestConfig(params)); }
Since:4.3
/** * @since 4.3 */
public ProxyClient(final RequestConfig requestConfig) { this(null, null, requestConfig); } public ProxyClient() { this(null, null, null); }
Deprecated:(4.3) do not use.
/** * @deprecated (4.3) do not use. */
@Deprecated public HttpParams getParams() { return new BasicHttpParams(); }
Deprecated:(4.3) do not use.
/** * @deprecated (4.3) do not use. */
@Deprecated public AuthSchemeRegistry getAuthSchemeRegistry() { return this.authSchemeRegistry; } public Socket tunnel( final HttpHost proxy, final HttpHost target, final Credentials credentials) throws IOException, HttpException { Args.notNull(proxy, "Proxy host"); Args.notNull(target, "Target host"); Args.notNull(credentials, "Credentials"); HttpHost host = target; if (host.getPort() <= 0) { host = new HttpHost(host.getHostName(), 80, host.getSchemeName()); } final HttpRoute route = new HttpRoute( host, this.requestConfig.getLocalAddress(), proxy, false, TunnelType.TUNNELLED, LayerType.PLAIN); final ManagedHttpClientConnection conn = this.connFactory.create( route, this.connectionConfig); final HttpContext context = new BasicHttpContext(); HttpResponse response; final HttpRequest connect = new BasicHttpRequest( "CONNECT", host.toHostString(), HttpVersion.HTTP_1_1); final BasicCredentialsProvider credsProvider = new BasicCredentialsProvider(); credsProvider.setCredentials(new AuthScope(proxy), credentials); // Populate the execution context context.setAttribute(HttpCoreContext.HTTP_TARGET_HOST, target); context.setAttribute(HttpCoreContext.HTTP_CONNECTION, conn); context.setAttribute(HttpCoreContext.HTTP_REQUEST, connect); context.setAttribute(HttpClientContext.HTTP_ROUTE, route); context.setAttribute(HttpClientContext.PROXY_AUTH_STATE, this.proxyAuthState); context.setAttribute(HttpClientContext.CREDS_PROVIDER, credsProvider); context.setAttribute(HttpClientContext.AUTHSCHEME_REGISTRY, this.authSchemeRegistry); context.setAttribute(HttpClientContext.REQUEST_CONFIG, this.requestConfig); this.requestExec.preProcess(connect, this.httpProcessor, context); for (;;) { if (!conn.isOpen()) { final Socket socket = new Socket(proxy.getHostName(), proxy.getPort()); conn.bind(socket); } this.authenticator.generateAuthResponse(connect, this.proxyAuthState, context); response = this.requestExec.execute(connect, conn, context); final int status = response.getStatusLine().getStatusCode(); if (status < 200) { throw new HttpException("Unexpected response to CONNECT request: " + response.getStatusLine()); } if (this.authenticator.isAuthenticationRequested(proxy, response, this.proxyAuthStrategy, this.proxyAuthState, context)) { if (this.authenticator.handleAuthChallenge(proxy, response, this.proxyAuthStrategy, this.proxyAuthState, context)) { // Retry request if (this.reuseStrategy.keepAlive(response, context)) { // Consume response content final HttpEntity entity = response.getEntity(); EntityUtils.consume(entity); } else { conn.close(); } // discard previous auth header connect.removeHeaders(AUTH.PROXY_AUTH_RESP); } else { break; } } else { break; } } final int status = response.getStatusLine().getStatusCode(); if (status > 299) { // Buffer response content final HttpEntity entity = response.getEntity(); if (entity != null) { response.setEntity(new BufferedHttpEntity(entity)); } conn.close(); throw new TunnelRefusedException("CONNECT refused by proxy: " + response.getStatusLine(), response); } return conn.getSocket(); } }