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:
parent
6a6ee3737c
commit
4f83ddb3e5
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue