blob: 5b7abff2205cad929f9c240c7025b6498dc6c142 [file] [log] [blame]
package org.apache.ahc.proxy;
import org.apache.ahc.AsyncHttpClient;
import org.apache.ahc.HttpIoHandler;
import org.apache.ahc.auth.UsernamePasswordCredentials;
import org.apache.ahc.codec.HttpRequestMessage;
import org.apache.ahc.codec.HttpResponseMessage;
import org.apache.mina.common.filterchain.IoFilterAdapter;
import org.apache.mina.common.session.IoSession;
import org.apache.mina.common.write.WriteRequest;
import org.apache.mina.filter.ssl.SslFilter;
public class ProxyFilter extends IoFilterAdapter {
public static final String PROXY_AUTHORIZATION_HEADER = "Proxy-Authorization";
private volatile boolean connectHandshakeComplete;
private final SslFilter sslFilter;
/**
* Create a proxy filter for a plain (http request)
* to a proxy server.
*/
public ProxyFilter() {
this(null);
}
/**
* Create a proxy filter that works in conjunction with
* an SslFilter for a secure connection.
*
* @param sslFilter The SSL filter to use for the secure connection. If
* null, a plain connection will be used.
*/
public ProxyFilter(SslFilter sslFilter) {
this.sslFilter = sslFilter;
}
@Override
/**
* Process a message send event to the proxy. This
* will add any required proxy authentication headers
* to the request.
*
* @param nextFilter
* @param session
* @param message
*
* @exception Exception
*/
public void messageSent(NextFilter nextFilter, IoSession session,
WriteRequest message) throws Exception {
HttpRequestMessage request = (HttpRequestMessage)message.getMessage();
ProxyConfiguration proxyConfig = request.getProxyConfiguration();
if (proxyConfig != null &&
proxyConfig.getProxyUser() != null &&
proxyConfig.getProxyPassword() != null &&
proxyConfig.getAuthScheme() != null) { // can proxy config ever be null?
// add the proxy authorization header
UsernamePasswordCredentials cred =
new UsernamePasswordCredentials(proxyConfig.getProxyUser(),
proxyConfig.getProxyPassword());
String authHeader = proxyConfig.getAuthScheme().authenticate(cred, request);
request.setHeader(PROXY_AUTHORIZATION_HEADER, authHeader);
}
// always forward
super.messageSent(nextFilter, session, message);
}
/**
* Returns the HttpRequestMessage instance currently
* being processed by the IoSession.
*
* @param session The current session using the filter.
*
* @return The request message being processed.
*/
private HttpRequestMessage getRequest(IoSession session) {
HttpRequestMessage request =
(HttpRequestMessage)session.getAttribute(HttpIoHandler.CURRENT_REQUEST);
return request;
}
@Override
/**
* Process the messageReceived() filter response. this
* will handle any proxy handshaking and SSL connection
* creation required.
*
* @param nextFilter The next filter in the chain.
* @param session The IoSession this operation is associated with.
* @param message The message object under construction.
*
* @exception Exception
*/
public void messageReceived(NextFilter nextFilter, IoSession session,
Object message) throws Exception {
if (needConnectHandshake()) {
// we need to complete the connect handshake
handleConnectResponse(session, message);
} else {
// the connection is established, just allow this
// to flow down the chain.
super.messageReceived(nextFilter, session, message);
}
}
/**
* Tests if we need to perform the SSL tunneling handshake.
* If this is an https request, we need to handle the
* tunneling here. If we've already established this,
* then the processing can continue with the next filter.
*
* @return true if we need to establish the handshake.
*/
private boolean needConnectHandshake() {
return (sslFilter != null && !connectHandshakeComplete);
}
/**
* Handle the response from a CONNECT request sent to
* the proxy server. If we got a good CONNECT request
* back, we add the SSL filter to the chain and
* write the original request back to the proxy
* server.
*
* @param session The current session.
* @param message The message object representing the request.
*/
private void handleConnectResponse(IoSession session, Object message) {
HttpResponseMessage response = (HttpResponseMessage)message;
int status = response.getStatusCode();
if (status == 200) {
// layer the SSL socket by inserting the SSL filter
session.getFilterChain().addBefore(AsyncHttpClient.PROTOCOL_FILTER, "SSL", sslFilter);
connectHandshakeComplete = true; // handshake is done
HttpRequestMessage request = getRequest(session);
// write the original request intended for the remote target
session.write(request);
} else {
session.close();
}
}
}