HTTPCLIENT-1132: ProxyClient implementation

git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1179817 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Oleg Kalnichevski 2011-10-06 20:16:59 +00:00
parent 6a6ee3737c
commit 4f83ddb3e5
2 changed files with 319 additions and 0 deletions

View File

@ -0,0 +1,74 @@
/*
* ====================================================================
* 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.examples.client;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.Socket;
import org.apache.http.HttpHost;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.impl.client.ProxyClient;
import org.apache.http.protocol.HTTP;
/**
* Example code for using {@link ProxyClient} in order to establish a tunnel through an HTTP proxy.
*/
public class ProxyTunnelDemo {
public final static void main(String[] args) throws Exception {
ProxyClient proxyClient = new ProxyClient();
HttpHost target = new HttpHost("www.yahoo.com", 80);
HttpHost proxy = new HttpHost("localhost", 8888);
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials("user", "pwd");
Socket socket = proxyClient.tunnel(proxy, target, credentials);
try {
Writer out = new OutputStreamWriter(socket.getOutputStream(),
HTTP.DEFAULT_CONTENT_CHARSET);
out.write("GET / HTTP/1.1\r\n");
out.write("Host: " + target.toHostString() + "\r\n");
out.write("Agent: whatever\r\n");
out.write("Connection: close\r\n");
out.write("\r\n");
out.flush();
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream(),
HTTP.DEFAULT_CONTENT_CHARSET));
String line = null;
while ((line = in.readLine()) != null) {
System.out.println(line);
}
} finally {
socket.close();
}
}
}

View File

@ -0,0 +1,245 @@
/*
* ====================================================================
* 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 javax.net.ssl.SSLSession;
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.HttpRequestInterceptor;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion;
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.params.AuthPolicy;
import org.apache.http.client.params.HttpClientParams;
import org.apache.http.client.protocol.ClientContext;
import org.apache.http.client.protocol.RequestClientConnControl;
import org.apache.http.client.protocol.RequestProxyAuthentication;
import org.apache.http.conn.HttpRoutedConnection;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.entity.BufferedHttpEntity;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.impl.DefaultHttpClientConnection;
import org.apache.http.impl.auth.BasicSchemeFactory;
import org.apache.http.impl.auth.DigestSchemeFactory;
import org.apache.http.impl.auth.NTLMSchemeFactory;
import org.apache.http.impl.auth.NegotiateSchemeFactory;
import org.apache.http.message.BasicHttpRequest;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpProcessor;
import org.apache.http.protocol.HttpRequestExecutor;
import org.apache.http.protocol.ImmutableHttpProcessor;
import org.apache.http.protocol.RequestContent;
import org.apache.http.protocol.RequestTargetHost;
import org.apache.http.protocol.RequestUserAgent;
import org.apache.http.util.EntityUtils;
public class ProxyClient {
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;
private final HttpParams params;
public ProxyClient(final HttpParams params) {
super();
if (params == null) {
throw new IllegalArgumentException("HTTP parameters may not be null");
}
this.httpProcessor = new ImmutableHttpProcessor(new HttpRequestInterceptor[] {
new RequestContent(),
new RequestTargetHost(),
new RequestClientConnControl(),
new RequestUserAgent(),
new RequestProxyAuthentication()
} );
this.requestExec = new HttpRequestExecutor();
this.proxyAuthStrategy = new ProxyAuthenticationStrategy();
this.authenticator = new HttpAuthenticator();
this.proxyAuthState = new AuthState();
this.authSchemeRegistry = new AuthSchemeRegistry();
this.authSchemeRegistry.register(AuthPolicy.BASIC, new BasicSchemeFactory());
this.authSchemeRegistry.register(AuthPolicy.DIGEST, new DigestSchemeFactory());
this.authSchemeRegistry.register(AuthPolicy.NTLM, new NTLMSchemeFactory());
this.authSchemeRegistry.register(AuthPolicy.SPNEGO, new NegotiateSchemeFactory());
this.reuseStrategy = new DefaultConnectionReuseStrategy();
this.params = params;
}
public ProxyClient() {
this(new BasicHttpParams());
}
public HttpParams getParams() {
return this.params;
}
public AuthSchemeRegistry getAuthSchemeRegistry() {
return this.authSchemeRegistry;
}
public Socket tunnel(
final HttpHost proxy,
final HttpHost target,
final Credentials credentials) throws IOException, HttpException {
ProxyConnection conn = new ProxyConnection(new HttpRoute(proxy));
HttpContext context = new BasicHttpContext();
HttpResponse response = null;
for (;;) {
if (!conn.isOpen()) {
Socket socket = new Socket(proxy.getHostName(), proxy.getPort());
conn.bind(socket, this.params);
}
String host = target.getHostName();
int port = target.getPort();
if (port < 0) {
port = 80;
}
StringBuilder buffer = new StringBuilder(host.length() + 6);
buffer.append(host);
buffer.append(':');
buffer.append(Integer.toString(port));
String authority = buffer.toString();
ProtocolVersion ver = HttpProtocolParams.getVersion(this.params);
HttpRequest connect = new BasicHttpRequest("CONNECT", authority, ver);
connect.setParams(this.params);
BasicCredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(new AuthScope(proxy), credentials);
// Populate the execution context
context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, target);
context.setAttribute(ExecutionContext.HTTP_PROXY_HOST, proxy);
context.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
context.setAttribute(ExecutionContext.HTTP_REQUEST, connect);
context.setAttribute(ClientContext.PROXY_AUTH_STATE, this.proxyAuthState);
context.setAttribute(ClientContext.CREDS_PROVIDER, credsProvider);
context.setAttribute(ClientContext.AUTHSCHEME_REGISTRY, this.authSchemeRegistry);
this.requestExec.preProcess(connect, this.httpProcessor, context);
response = this.requestExec.execute(connect, conn, context);
response.setParams(this.params);
this.requestExec.postProcess(response, this.httpProcessor, context);
int status = response.getStatusLine().getStatusCode();
if (status < 200) {
throw new HttpException("Unexpected response to CONNECT request: " +
response.getStatusLine());
}
if (HttpClientParams.isAuthenticating(this.params)) {
if (this.authenticator.isAuthenticationRequested(response,
this.proxyAuthStrategy, this.proxyAuthState, context)) {
if (this.authenticator.authenticate(proxy, response,
this.proxyAuthStrategy, this.proxyAuthState, context)) {
// Retry request
if (this.reuseStrategy.keepAlive(response, context)) {
// Consume response content
HttpEntity entity = response.getEntity();
EntityUtils.consume(entity);
} else {
conn.close();
}
} else {
break;
}
} else {
break;
}
}
}
int status = response.getStatusLine().getStatusCode();
if (status > 299) {
// Buffer response content
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();
}
static class ProxyConnection extends DefaultHttpClientConnection implements HttpRoutedConnection {
private final HttpRoute route;
ProxyConnection(final HttpRoute route) {
super();
this.route = route;
}
public HttpRoute getRoute() {
return this.route;
}
public boolean isSecure() {
return false;
}
public SSLSession getSSLSession() {
return null;
}
@Override
public Socket getSocket() {
return super.getSocket();
}
}
}