package org.codehaus.xfire.transport.http;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import javax.activation.DataHandler;
import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpState;
import org.apache.commons.httpclient.HttpVersion;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.NTCredentials;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.httpclient.params.HttpClientParams;
import org.codehaus.xfire.MessageContext;
import org.codehaus.xfire.XFireException;
import org.codehaus.xfire.attachments.Attachments;
import org.codehaus.xfire.attachments.JavaMailAttachments;
import org.codehaus.xfire.attachments.SimpleAttachment;
import org.codehaus.xfire.attachments.StreamedAttachments;
import org.codehaus.xfire.exchange.InMessage;
import org.codehaus.xfire.exchange.OutMessage;
import org.codehaus.xfire.soap.SoapConstants;
import org.codehaus.xfire.transport.Channel;
import org.codehaus.xfire.util.OutMessageDataSource;
import org.codehaus.xfire.util.STAXUtils;
/**
* Sends a http message via commons http client. To customize the
* HttpClient parameters, set the property HTTP_CLIENT_PARAMS
* on the MessageContext for your invocation.
*
* @author Dan Diephouse
* @since Oct 26, 2004
*/
public class CommonsHttpMessageSender extends AbstractMessageSender
{
private PostMethod postMethod;
private HttpClient client;
private HttpState state;
private static final String GZIP_CONTENT_ENCODING = "gzip";
public static final String DISABLE_KEEP_ALIVE = "disable-keep-alive";
public static final String HTTP_CLIENT_PARAMS = "httpClient.params";
public static final String USER_AGENT =
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; XFire Client +http://xfire.codehaus.org)";
public static final String HTTP_PROXY_HOST = "http.proxyHost";
public static final String HTTP_PROXY_PORT = "http.proxyPort";
public static final String HTTP_STATE = "httpClient.httpstate";
public static final String HTTP_CLIENT = "httpClient";
/** Enable GZIP on request and response. */
public static final String GZIP_ENABLED = "gzip.enabled";
/** Request GZIP encoded responses. */
public static final String GZIP_RESPONSE_ENABLED = "gzip.response.enabled";
/** GZIP the requests. */
public static final String GZIP_REQUEST_ENABLED = "gzip.request.enabled";
private InputStream msgIs;
public CommonsHttpMessageSender(OutMessage message, MessageContext context)
{
super(message, context);
}
public void open()
throws IOException, XFireException
{
MessageContext context = getMessageContext();
createClient();
// Pull the HttpState from the context if possible. Otherwise create
// one in the ThreadLocal
state = getHttpState();
postMethod = new PostMethod(getUri());
if (Boolean.valueOf((String) context.getContextualProperty(DISABLE_KEEP_ALIVE)).booleanValue()) {
postMethod.setRequestHeader("Connection", "Close");
}
// set the username and password if present
String username = (String) context.getContextualProperty(Channel.USERNAME);
if (username != null)
{
String password = (String) context.getContextualProperty(Channel.PASSWORD);
client.getParams().setAuthenticationPreemptive(true);
int domainIndex = username.indexOf('\\');
if (domainIndex > 0 && username.length() > domainIndex + 1) {
state.setCredentials(
AuthScope.ANY,
new NTCredentials(
username.substring(0, domainIndex),
password,
"localhost", // TODO: resolve local host name
username.substring(domainIndex+1)));
} else {
state.setCredentials(
AuthScope.ANY,
new UsernamePasswordCredentials(
username,
password));
}
}
if (getSoapAction() != null)
{
postMethod.setRequestHeader("SOAPAction", getQuotedSoapAction());
}
OutMessage message = getMessage();
boolean mtomEnabled = Boolean.valueOf((String) context.getContextualProperty(SoapConstants.MTOM_ENABLED)).booleanValue();
Attachments atts = message.getAttachments();
if (mtomEnabled || atts != null)
{
if (atts == null)
{
atts = new JavaMailAttachments();
message.setAttachments(atts);
}
OutMessageDataSource source = new OutMessageDataSource(context, message);
DataHandler soapHandler = new DataHandler(source);
atts.setSoapContentType(HttpChannel.getSoapMimeType(message, false));
atts.setSoapMessage(new SimpleAttachment(source.getName(), soapHandler));
postMethod.setRequestHeader("Content-Type", atts.getContentType());
}
else
{
postMethod.setRequestHeader("Content-Type", HttpChannel.getSoapMimeType(getMessage(), true));
}
if (isGzipResponseEnabled(context))
{
postMethod.setRequestHeader("Accept-Encoding", GZIP_CONTENT_ENCODING);
}
if (isGzipRequestEnabled(context))
{
postMethod.setRequestHeader("Content-Encoding", GZIP_CONTENT_ENCODING);
}
}
private synchronized void createClient()
{
MessageContext context = getMessageContext();
client = (HttpClient) ((HttpChannel) getMessage().getChannel()).getProperty(HTTP_CLIENT);
if (client == null)
{
client = new HttpClient();
client.setHttpConnectionManager(new MultiThreadedHttpConnectionManager());
((HttpChannel) getMessage().getChannel()).setProperty(HTTP_CLIENT, client);
HttpClientParams params = (HttpClientParams) context.getContextualProperty(HTTP_CLIENT_PARAMS);
if (params == null)
{
params = client.getParams();
client.getParams().setParameter("http.useragent", USER_AGENT);
client.getParams().setBooleanParameter("http.protocol.expect-continue", true);
client.getParams().setVersion(HttpVersion.HTTP_1_1);
}
else
{
client.setParams(params);
}
// Setup the proxy settings
String proxyHost = (String) context.getContextualProperty(HTTP_PROXY_HOST);
if (proxyHost == null)
{
proxyHost = System.getProperty(HTTP_PROXY_HOST);
}
if (proxyHost != null)
{
String portS = (String) context.getContextualProperty(HTTP_PROXY_PORT);
if (portS == null)
{
portS = System.getProperty(HTTP_PROXY_PORT);
}
int port = 80;
if (portS != null)
port = Integer.parseInt(portS);
client.getHostConfiguration().setProxy(proxyHost, port);
}
}
}
static boolean isGzipRequestEnabled(MessageContext context)
{
if (isGzipEnabled(context)) return true;
Object gzipReqEnabled = context.getContextualProperty(GZIP_REQUEST_ENABLED);
return (gzipReqEnabled != null && gzipReqEnabled.toString().toLowerCase().equals("true"));
}
static boolean isGzipEnabled(MessageContext context)
{
Object gzipEnabled = context.getContextualProperty(GZIP_ENABLED);
return (gzipEnabled != null && gzipEnabled.toString().toLowerCase().equals("true"));
}
static boolean isGzipResponseEnabled(MessageContext context)
{
if (isGzipEnabled(context)) return true;
Object gzipResEnabled = context.getContextualProperty(GZIP_RESPONSE_ENABLED);
return (gzipResEnabled != null && gzipResEnabled.toString().toLowerCase().equals("true"));
}
public void send()
throws HttpException, IOException, XFireException
{
RequestEntity requestEntity;
/**
* Lots of HTTP servers don't handle chunking correctly, so its turned off by default.
*/
boolean chunkingOn = Boolean.valueOf((String) getMessageContext()
.getContextualProperty(HttpTransport.CHUNKING_ENABLED)).booleanValue();
if (!chunkingOn)
{
requestEntity = getByteArrayRequestEntity();
}
else
{
requestEntity = new OutMessageRequestEntity(getMessage(), getMessageContext());
}
getMethod().setRequestEntity(requestEntity);
client.executeMethod(null, postMethod, state);
}
public int getStatusCode(){
return postMethod.getStatusCode();
}
/**
* @return
*/
public boolean hasResponse()
{
String ct = postMethod.getResponseHeader("Content-Type").getValue();
return ct != null && ct.length() > 0;
}
public HttpState getHttpState()
{
HttpState state = (HttpState) ((HttpChannel) getMessage().getChannel()).getProperty(HTTP_STATE);
if (state == null)
{
state = new HttpState();
((HttpChannel) getMessage().getChannel()).setProperty(HTTP_STATE, state);
}
return state;
}
private RequestEntity getByteArrayRequestEntity()
throws IOException, XFireException
{
OutMessage message = getMessage();
MessageContext context = getMessageContext();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
OutputStream os = bos;
if (isGzipRequestEnabled(context))
{
os = new GZIPOutputStream(os);
}
Attachments atts = message.getAttachments();
if (atts != null)
{
atts.write(os);
}
else
{
HttpChannel.writeWithoutAttachments(context, message, os);
}
os.close();
return new ByteArrayRequestEntity(bos.toByteArray());
}
public InMessage getInMessage()
throws IOException
{
String ct = postMethod.getResponseHeader("Content-Type").getValue();
InputStream in = postMethod.getResponseBodyAsStream();
Header hce = postMethod.getResponseHeader("Content-Encoding");
if (hce != null && hce.getValue().equals(GZIP_CONTENT_ENCODING))
{
in = new GZIPInputStream(in);
}
if (ct.toLowerCase().indexOf("multipart/related") != -1)
{
Attachments atts = new StreamedAttachments(in, ct);
msgIs = atts.getSoapMessage().getDataHandler().getInputStream();
InMessage msg = new InMessage(STAXUtils.createXMLStreamReader(msgIs, getEncoding(),getMessageContext()), getUri());
msg.setAttachments(atts);
return msg;
}
else
{
return new InMessage(STAXUtils.createXMLStreamReader(in, getEncoding(),getMessageContext()), getUri());
}
}
public PostMethod getMethod()
{
return this.postMethod;
}
public void close()
throws XFireException
{
if (msgIs != null)
try
{
msgIs.close();
}
catch (IOException e)
{
throw new XFireException("Could not close connection.", e);
}
if (postMethod != null)
postMethod.releaseConnection();
}
}