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.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.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.XFireRuntimeException;
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 = "gzip";
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";
/** Request GZIP encoded responses. */
public static final String GZIP_RESPONSE = "gzip.response";
/** GZIP the requests. */
public static final String GZIP_REQUEST = "gzip.requests";
private InputStream msgIs;
public CommonsHttpMessageSender(OutMessage message, MessageContext context)
{
super(message, context);
}
public void open()
throws IOException, XFireException
{
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);
}
}
// Pull the HttpState from the context if possible. Otherwise create
// one in the ThreadLocal
state = getHttpState();
postMethod = new PostMethod(getUri());
// 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);
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));
}
Object o = context.getContextualProperty(GZIP_RESPONSE);
if(null != o)
{
if(((Boolean)o).booleanValue())
{
postMethod.setRequestHeader("Accept-Encoding", GZIP);
}
}
o = context.getContextualProperty(GZIP_REQUEST);
if(null != o)
{
if(((Boolean)o).booleanValue())
{
postMethod.setRequestHeader("Content-Encoding", GZIP);
}
}
}
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;
boolean bGZIP = false;
Object o = context.getContextualProperty(GZIP_REQUEST);
if(null != o)
{
if(((Boolean)o).booleanValue())
{
bGZIP = true;
GZIPOutputStream gzOut = new GZIPOutputStream(bos);
os = gzOut;
}
}
Attachments atts = message.getAttachments();
if (atts != null)
{
atts.write(os);
}
else
{
HttpChannel.writeWithoutAttachments(context, message, os);
}
if(bGZIP)
{
((GZIPOutputStream)os).finish();
}
os.close();
return new ByteArrayRequestEntity(bos.toByteArray());
}
public InMessage getInMessage()
throws IOException
{
String ct = postMethod.getResponseHeader("Content-Type").getValue();
Header hce = postMethod.getResponseHeader("Content-Encoding");
InputStream in = null;
if(null != hce)
{
if(hce.getValue().equals(GZIP))
{
in = new GZIPInputStream(postMethod.getResponseBodyAsStream());
}
else
{
throw new XFireRuntimeException("Response contains Content-Encoding '" + hce.getValue() + "', which is not the requested type of '" + GZIP + "'.");
}
}
else
{
in = postMethod.getResponseBodyAsStream();
}
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();
}
}