/*
* Copyright 2001-2004 The Apache Software Foundation.
*
* Licensed 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.
*
* $Id: CommonsHTTPSender.java 988245 2010-08-23 18:39:35Z kwright $
*/
package org.apache.manifoldcf.crawler.connectors.meridio;
import org.apache.axis.AxisFault;
import org.apache.axis.Constants;
import org.apache.axis.Message;
import org.apache.axis.MessageContext;
import org.apache.axis.components.logger.LogFactory;
import org.apache.axis.components.net.CommonsHTTPClientProperties;
import org.apache.axis.components.net.CommonsHTTPClientPropertiesFactory;
import org.apache.axis.components.net.TransportClientProperties;
import org.apache.axis.components.net.TransportClientPropertiesFactory;
import org.apache.axis.transport.http.HTTPConstants;
import org.apache.axis.handlers.BasicHandler;
import org.apache.axis.soap.SOAP12Constants;
import org.apache.axis.soap.SOAPConstants;
import org.apache.axis.utils.JavaUtils;
import org.apache.axis.utils.Messages;
import org.apache.axis.utils.NetworkUtils;
import org.apache.commons.httpclient.Cookie;
import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HostConfiguration;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpConnectionManager;
import org.apache.commons.httpclient.HttpMethodBase;
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.cookie.CookiePolicy;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.commons.httpclient.protocol.ProtocolFactory;
import org.apache.commons.logging.Log;
import javax.xml.soap.MimeHeader;
import javax.xml.soap.MimeHeaders;
import javax.xml.soap.SOAPException;
import java.io.ByteArrayOutputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import org.apache.manifoldcf.crawler.connectors.meridio.meridiowrapper.MeridioWrapper;
import org.apache.manifoldcf.crawler.connectors.meridio.ConnectionConfig;
/**
* This class uses Jakarta Commons's HttpClient to call a SOAP server.
*
* @author Davanum Srinivas (dims@yahoo.com)
* History: By Chandra Talluri
* Modifications done for maintaining sessions. Cookies needed to be set on
* HttpState not on MessageContext, since ttpMethodBase overwrites the cookies
* from HttpState. Also we need to setCookiePolicy on HttpState to
* CookiePolicy.COMPATIBILITY else it is defaulting to RFC2109Spec and adding
* Version information to it and tomcat server not recognizing it
*/
public class CommonsHTTPSender extends BasicHandler {
/** Field log */
protected static Log log =
LogFactory.getLog(CommonsHTTPSender.class.getName());
/** Connection pool, and initialization lock */
protected static HttpConnectionManager connectionManager = null;
protected static Object lockObject = new Object();
protected CommonsHTTPClientProperties clientProperties;
boolean httpChunkStream = true; //Use HTTP chunking or not.
public CommonsHTTPSender() {
initialize();
}
protected void initialize() {
this.clientProperties = CommonsHTTPClientPropertiesFactory.create();
synchronized (lockObject)
{
if (connectionManager == null)
{
MultiThreadedHttpConnectionManager cm = new MultiThreadedHttpConnectionManager();
// I don't know where CommonsHTTPClientPropertiesFactory.create() gets its parameters, but it was too small
// by default. Since we control
// the pool sizes at a higher level, these should be pretty much wide open at this level
//cm.getParams().setDefaultMaxConnectionsPerHost(clientProperties.getMaximumConnectionsPerHost());
//cm.getParams().setMaxTotalConnections(clientProperties.getMaximumTotalConnections());
cm.getParams().setDefaultMaxConnectionsPerHost(1000);
cm.getParams().setMaxTotalConnections(1000);
// If defined, set the default timeouts
// Can be overridden by the MessageContext
if(this.clientProperties.getDefaultConnectionTimeout()>0) {
cm.getParams().setConnectionTimeout(this.clientProperties.getDefaultConnectionTimeout());
}
if(this.clientProperties.getDefaultSoTimeout()>0) {
cm.getParams().setSoTimeout(this.clientProperties.getDefaultSoTimeout());
}
connectionManager = cm;
}
}
}
protected static class ExecuteMethodThread extends Thread
{
protected HttpClient client;
protected HostConfiguration hostConfiguration;
protected HttpMethodBase executeMethod;
protected Throwable exception = null;
protected int rval = 0;
public ExecuteMethodThread(HttpClient client, HostConfiguration hostConfiguration, HttpMethodBase executeMethod)
{
super();
setDaemon(true);
this.client = client;
this.hostConfiguration = hostConfiguration;
this.executeMethod = executeMethod;
}
public void run()
{
try
{
// Call the execute method appropriately
rval = client.executeMethod(hostConfiguration,executeMethod,null);
}
catch (Throwable e)
{
this.exception = e;
}
}
public Throwable getException()
{
return exception;
}
public int getResponse()
{
return rval;
}
}
/**
* invoke creates a socket connection, sends the request SOAP message and then
* reads the response SOAP message back from the SOAP server
*
* @param msgContext the messsage context
*
* @throws AxisFault
*/
public void invoke(MessageContext msgContext) throws AxisFault {
HttpMethodBase method = null;
if (log.isDebugEnabled()) {
log.debug(Messages.getMessage("enter00",
"CommonsHTTPSender::invoke"));
}
try {
URL targetURL =
new URL(msgContext.getStrProp(MessageContext.TRANS_URL));
ConnectionConfig configInfo = (ConnectionConfig)msgContext.getProperty(MeridioWrapper.CONFIGURATION_PROPERTY);
HttpConnectionManager localConnectionManager = configInfo.getConnectionManager();
if (localConnectionManager == null)
localConnectionManager = connectionManager;
// no need to retain these, as the cookies/credentials are
// stored in the message context across multiple requests.
// the underlying connection manager, however, is retained
// so sockets get recycled when possible.
HttpClient httpClient = new HttpClient(localConnectionManager);
// the timeout value for allocation of connections from the pool
httpClient.getParams().setConnectionManagerTimeout(this.clientProperties.getConnectionPoolTimeout());
// Set our protocol factory, in case there's a redirect
ProtocolFactory myFactory = configInfo.getProtocolFactory();
if (myFactory != null)
httpClient.getParams().setParameter(org.apache.commons.httpclient.params.HttpClientParams.PROTOCOL_FACTORY,myFactory);
// Allow circular redirections
httpClient.getParams().setParameter(org.apache.commons.httpclient.params.HttpClientParams.ALLOW_CIRCULAR_REDIRECTS,new Boolean(true));
HostConfiguration hostConfiguration =
getHostConfiguration(httpClient, msgContext, targetURL, configInfo);
boolean posting = true;
// If we're SOAP 1.2, allow the web method to be set from the
// MessageContext.
if (msgContext.getSOAPConstants() == SOAPConstants.SOAP12_CONSTANTS) {
String webMethod = msgContext.getStrProp(SOAP12Constants.PROP_WEBMETHOD);
if (webMethod != null) {
posting = webMethod.equals(HTTPConstants.HEADER_POST);
}
}
// Since the host configuration contains the host/port/protocol, we don't want to
// have it overwritten. So, we need the relative url.
String relativeTargetURL = targetURL.toString();
int slashindex = relativeTargetURL.indexOf("/");
if (slashindex != 0 && slashindex != -1)
{
slashindex = relativeTargetURL.indexOf("/",slashindex + 2);
if (slashindex != -1)
relativeTargetURL = relativeTargetURL.substring(slashindex);
}
if (posting) {
method = new PostMethod(relativeTargetURL);
} else {
method = new GetMethod(relativeTargetURL);
method.setFollowRedirects(true);
}
// The variable 'releaseMethod' is null if we no longer have to release the connection into the pool
// on exit from this section. Otherwise it remains set to the method, so that all exceptions cause
// the release to occur.
HttpMethodBase releaseMethod = method;
try
{
// This stuff all needs to be inside the try above
if (posting)
{
Message reqMessage = msgContext.getRequestMessage();
// set false as default, addContetInfo can overwrite
method.getParams().setBooleanParameter(HttpMethodParams.USE_EXPECT_CONTINUE,
false);
// Do stuff similar to curl, for stat-oil
method.addRequestHeader("Pragma","no-cache");
method.addRequestHeader("Accept","*/*");
addContextInfo(method, httpClient, msgContext, targetURL);
Credentials c = httpClient.getState().getCredentials(AuthScope.ANY);
//if (c instanceof NTCredentials)
// System.out.println("Credential host "+((NTCredentials)c).getHost());
//else
// System.out.println("Not NT credentials!");
MessageRequestEntity requestEntity = null;
if (msgContext.isPropertyTrue(HTTPConstants.MC_GZIP_REQUEST)) {
requestEntity = new GzipMessageRequestEntity(method, reqMessage, httpChunkStream);
} else {
requestEntity = new MessageRequestEntity(method, reqMessage, httpChunkStream);
}
((PostMethod)method).setRequestEntity(requestEntity);
}
else
{
addContextInfo(method, httpClient, msgContext, targetURL);
}
String httpVersion = msgContext.getStrProp(MessageContext.HTTP_TRANSPORT_VERSION);
if (httpVersion != null) {
if (httpVersion.equals(HTTPConstants.HEADER_PROTOCOL_V10)) {
method.getParams().setVersion(HttpVersion.HTTP_1_0);
}
// assume 1.1
}
// don't forget the cookies!
// Cookies need to be set on HttpState, since HttpMethodBase
// overwrites the cookies from HttpState
if (msgContext.getMaintainSession()) {
HttpState state = httpClient.getState();
method.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
String host = hostConfiguration.getHost();
String path = targetURL.getPath();
boolean secure = hostConfiguration.getProtocol().isSecure();
fillHeaders(msgContext, state, HTTPConstants.HEADER_COOKIE, host, path, secure);
fillHeaders(msgContext, state, HTTPConstants.HEADER_COOKIE2, host, path, secure);
httpClient.setState(state);
}
int returnCode;
ExecuteMethodThread t = new ExecuteMethodThread(httpClient,hostConfiguration,method);
try
{
t.start();
t.join();
Throwable thr = t.getException();
if (thr != null)
{
if (thr instanceof Exception)
throw (Exception)thr;
else
throw (Error)thr;
}
returnCode = t.getResponse();
}
catch (InterruptedException e)
{
t.interrupt();
// We need the caller to abandon any connections left around, so rethrow in a way that forces them to process the event properly.
releaseMethod = null;
throw e;
}
String contentType =
getHeader(method, HTTPConstants.HEADER_CONTENT_TYPE);
String contentLocation =
getHeader(method, HTTPConstants.HEADER_CONTENT_LOCATION);
String contentLength =
getHeader(method, HTTPConstants.HEADER_CONTENT_LENGTH);
if ((returnCode > 199) && (returnCode < 300)) {
// SOAP return is OK - so fall through
} else if (msgContext.getSOAPConstants() ==
SOAPConstants.SOAP12_CONSTANTS) {
// For now, if we're SOAP 1.2, fall through, since the range of
// valid result codes is much greater
} else if ((contentType != null) && !contentType.equals("text/html")
&& ((returnCode > 499) && (returnCode < 600))) {
// SOAP Fault should be in here - so fall through
} else {
String statusMessage = method.getStatusText();
AxisFault fault = new AxisFault("HTTP",
"(" + returnCode + ")"
+ statusMessage, null,
null);
fault.setFaultDetailString(
Messages.getMessage("return01",
"" + returnCode,
method.getResponseBodyAsString()));
fault.addFaultDetail(Constants.QNAME_FAULTDETAIL_HTTPERRORCODE,
Integer.toString(returnCode));
throw fault;
}
// wrap the response body stream so that close() also releases
// the connection back to the pool.
InputStream releaseConnectionOnCloseStream =
createConnectionReleasingInputStream(method);
// If something goes wrong after this point and before this is safely
// saved in the msg, we'll have a dangling stream, so we need a
// try/catch to guarantee that it doesn't hang around.
InputStream streamToClose = releaseConnectionOnCloseStream;
try
{
Header contentEncoding =
method.getResponseHeader(HTTPConstants.HEADER_CONTENT_ENCODING);
if (contentEncoding != null) {
if (contentEncoding.getValue().
equalsIgnoreCase(HTTPConstants.COMPRESSION_GZIP)) {
releaseConnectionOnCloseStream =
new GZIPInputStream(releaseConnectionOnCloseStream);
streamToClose = releaseConnectionOnCloseStream;
} else {
AxisFault fault = new AxisFault("HTTP",
"unsupported content-encoding of '"
+ contentEncoding.getValue()
+ "' found", null, null);
throw fault;
}
}
Message outMsg = new Message(releaseConnectionOnCloseStream,
false, contentType, contentLocation);
// Transfer HTTP headers of HTTP message to MIME headers of SOAP message
Header[] responseHeaders = method.getResponseHeaders();
MimeHeaders responseMimeHeaders = outMsg.getMimeHeaders();
for (int i = 0; i < responseHeaders.length; i++) {
Header responseHeader = responseHeaders[i];
responseMimeHeaders.addHeader(responseHeader.getName(),
responseHeader.getValue());
}
outMsg.setMessageType(Message.RESPONSE);
// It's definitely not safe to not release the connection back to the pool until after the
// successful execution of the following line (which, presumably, registers the
// message in the msgcontext, where it will be closed if something goes wrong)
msgContext.setResponseMessage(outMsg);
releaseMethod = null;
streamToClose = null;
if (log.isDebugEnabled()) {
if (null == contentLength) {
log.debug("\n"
+ Messages.getMessage("no00", "Content-Length"));
}
log.debug("\n" + Messages.getMessage("xmlRecd00"));
log.debug("-----------------------------------------------");
log.debug(outMsg.getSOAPPartAsString());
}
// if we are maintaining session state,
// handle cookies (if any)
if (msgContext.getMaintainSession()) {
Header[] headers = method.getResponseHeaders();
for (int i = 0; i < headers.length; i++) {
if (headers[i].getName().equalsIgnoreCase(HTTPConstants.HEADER_SET_COOKIE)) {
handleCookie(HTTPConstants.HEADER_COOKIE, headers[i].getValue(), msgContext);
} else if (headers[i].getName().equalsIgnoreCase(HTTPConstants.HEADER_SET_COOKIE2)) {
handleCookie(HTTPConstants.HEADER_COOKIE2, headers[i].getValue(), msgContext);
}
}
}
// always release the connection back to the pool if
// it was one way invocation
if (msgContext.isPropertyTrue("axis.one.way")) {
method.releaseConnection();
releaseMethod = null;
}
}
finally
{
if (streamToClose != null)
{
streamToClose.close();
releaseMethod = null;
}
}
}
finally
{
if (releaseMethod != null)
releaseMethod.releaseConnection();
}
} catch (Exception e) {
//e.printStackTrace();
log.debug(e);
throw AxisFault.makeFault(e);
}
if (log.isDebugEnabled()) {
log.debug(Messages.getMessage("exit00",
"CommonsHTTPSender::invoke"));
}
}
/**
* little helper function for cookies. fills up the message context with
* a string or an array of strings (if there are more than one Set-Cookie)
*
* @param cookieName
* @param cookie
* @param msgContext
*/
public void handleCookie(String cookieName, String cookie,
MessageContext msgContext) {
cookie = cleanupCookie(cookie);
int keyIndex = cookie.indexOf("=");
String key = (keyIndex != -1) ? cookie.substring(0, keyIndex) : cookie;
ArrayList cookies = new ArrayList();
Object oldCookies = msgContext.getProperty(cookieName);
boolean alreadyExist = false;
if(oldCookies != null) {
if(oldCookies instanceof String[]) {
String[] oldCookiesArray = (String[])oldCookies;
for(int i = 0; i < oldCookiesArray.length; i++) {
String anOldCookie = oldCookiesArray[i];
if (key != null && anOldCookie.indexOf(key) == 0) {
// same cookie key
anOldCookie = cookie; // update to new one
alreadyExist = true;
}
cookies.add(anOldCookie);
}
} else {
String oldCookie = (String)oldCookies;
if (key != null && oldCookie.indexOf(key) == 0) {
// same cookie key
oldCookie = cookie; // update to new one
alreadyExist = true;
}
cookies.add(oldCookie);
}
}
if (!alreadyExist) {
cookies.add(cookie);
}
if(cookies.size()==1) {
msgContext.setProperty(cookieName, cookies.get(0));
} else if (cookies.size() > 1) {
msgContext.setProperty(cookieName, cookies.toArray(new String[cookies.size()]));
}
}
/**
* Add cookies from message context
*
* @param msgContext
* @param state
* @param header
* @param host
* @param path
* @param secure
*/
private void fillHeaders(MessageContext msgContext, HttpState state, String header, String host, String path, boolean secure) {
Object ck1 = msgContext.getProperty(header);
if (ck1 != null) {
if (ck1 instanceof String[]) {
String [] cookies = (String[]) ck1;
for (int i = 0; i < cookies.length; i++) {
addCookie(state, cookies[i], host, path, secure);
}
} else {
addCookie(state, (String) ck1, host, path, secure);
}
}
}
/**
* add cookie to state
* @param state
* @param cookie
*/
private void addCookie(HttpState state, String cookie,String host, String path, boolean secure) {
int index = cookie.indexOf('=');
state.addCookie(new Cookie(host, cookie.substring(0, index),
cookie.substring(index + 1), path,
null, secure));
}
/**
* cleanup the cookie value.
*
* @param cookie initial cookie value
*
* @return a cleaned up cookie value.
*/
private String cleanupCookie(String cookie) {
cookie = cookie.trim();
// chop after first ; a la Apache SOAP (see HTTPUtils.java there)
int index = cookie.indexOf(';');
if (index != -1) {
cookie = cookie.substring(0, index);
}
return cookie;
}
protected HostConfiguration getHostConfiguration(HttpClient client,
MessageContext context,
URL targetURL,
ConnectionConfig configInfo)
{
// OK, this was very poor design on the part of Axis. The TransportClientProperties object
// only has the proxy credentials and proxy host as members. This code apparently EXPECTED the
// actual host credentials to be stuffed into the proxy credentials! There was no direct way to pass non-proxy
// credentials in either.
//
// Instead, I wound up revamping this code pretty much completely, using my own configuration class to get properties
// and protocols.
HostConfiguration config = new HostConfiguration();
String theProtocolString = targetURL.getProtocol();
int port = targetURL.getPort();
if (port == -1)
{
if(theProtocolString.equalsIgnoreCase("https")) {
port = 443; // default port for https being 443
} else {
// it must be http
port = 80; // default port for http being 80
}
}
Protocol theProtocol = configInfo.getProtocol(theProtocolString);
// Set the host parameters
config.setHost(targetURL.getHost(), port, theProtocol);
// Set the primary (host) credentials
if (configInfo.getUserName() != null && configInfo.getUserName().length() > 0)
{
Credentials cred = null;
if (configInfo.getDomain() != null && configInfo.getDomain().length() > 0)
{
cred = new NTCredentials(configInfo.getUserName(),
configInfo.getPassword(),
targetURL.getHost(),
configInfo.getDomain());
//System.out.println(targetURL.getHost());
}
else
cred = new UsernamePasswordCredentials(configInfo.getUserName(),
configInfo.getPassword());
client.getState().setCredentials(AuthScope.ANY,cred);
}
// Now, if there's a proxy, set that up too.
if (configInfo.getProxyHost() != null && configInfo.getProxyHost().length() > 0)
{
Credentials cred = null;
if (configInfo.getDomain() != null && configInfo.getDomain().length() > 0)
cred = new NTCredentials(configInfo.getUserName(),
configInfo.getPassword(),
configInfo.getProxyHost(),
configInfo.getDomain());
else
cred = new UsernamePasswordCredentials(configInfo.getUserName(),
configInfo.getPassword());
client.getState().setProxyCredentials(AuthScope.ANY,cred);
int proxyPort = 80;
if (theProtocolString.equalsIgnoreCase("https"))
proxyPort = 443;
if (configInfo.getProxyPort() != null)
proxyPort = configInfo.getProxyPort().intValue();
config.setProxy(configInfo.getProxyHost(),proxyPort);
}
return config;
}
/**
* Extracts info from message context.
*
* @param method Post method
* @param httpClient The client used for posting
* @param msgContext the message context
* @param tmpURL the url to post to.
*
* @throws Exception
*/
private void addContextInfo(HttpMethodBase method,
HttpClient httpClient,
MessageContext msgContext,
URL tmpURL)
throws Exception {
// optionally set a timeout for the request
if (msgContext.getTimeout() != 0) {
/* ISSUE: these are not the same, but MessageContext has only one
definition of timeout */
// SO_TIMEOUT -- timeout for blocking reads
httpClient.getHttpConnectionManager().getParams().setSoTimeout(msgContext.getTimeout());
// timeout for initial connection
httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(msgContext.getTimeout());
}
// Get SOAPAction, default to ""
String action = msgContext.useSOAPAction()
? msgContext.getSOAPActionURI()
: "";
if (action == null) {
action = "";
}
Message msg = msgContext.getRequestMessage();
if (msg != null){
method.setRequestHeader(new Header(HTTPConstants.HEADER_CONTENT_TYPE,
msg.getContentType(msgContext.getSOAPConstants())));
}
method.setRequestHeader(new Header(HTTPConstants.HEADER_SOAP_ACTION,
"\"" + action + "\""));
method.setRequestHeader(new Header(HTTPConstants.HEADER_USER_AGENT, Messages.getMessage("axisUserAgent")));
String userID = msgContext.getUsername();
String passwd = msgContext.getPassword();
// if UserID is not part of the context, but is in the URL, use
// the one in the URL.
if ((userID == null) && (tmpURL.getUserInfo() != null)) {
String info = tmpURL.getUserInfo();
int sep = info.indexOf(':');
if ((sep >= 0) && (sep + 1 < info.length())) {
userID = info.substring(0, sep);
passwd = info.substring(sep + 1);
} else {
userID = info;
}
}
if (userID != null) {
Credentials proxyCred =
new UsernamePasswordCredentials(userID,
passwd);
// if the username is in the form "user\domain"
// then use NTCredentials instead.
int domainIndex = userID.indexOf("\\");
if (domainIndex > 0) {
String domain = userID.substring(0, domainIndex);
if (userID.length() > domainIndex + 1) {
String user = userID.substring(domainIndex + 1);
proxyCred = new NTCredentials(user,
passwd,
NetworkUtils.getLocalHostname(), domain);
}
}
httpClient.getState().setCredentials(AuthScope.ANY, proxyCred);
}
// add compression headers if needed
if (msgContext.isPropertyTrue(HTTPConstants.MC_ACCEPT_GZIP)) {
method.addRequestHeader(HTTPConstants.HEADER_ACCEPT_ENCODING,
HTTPConstants.COMPRESSION_GZIP);
}
if (msgContext.isPropertyTrue(HTTPConstants.MC_GZIP_REQUEST)) {
method.addRequestHeader(HTTPConstants.HEADER_CONTENT_ENCODING,
HTTPConstants.COMPRESSION_GZIP);
}
// Transfer MIME headers of SOAPMessage to HTTP headers.
MimeHeaders mimeHeaders = msg.getMimeHeaders();
if (mimeHeaders != null) {
for (Iterator i = mimeHeaders.getAllHeaders(); i.hasNext(); ) {
MimeHeader mimeHeader = (MimeHeader) i.next();
//HEADER_CONTENT_TYPE and HEADER_SOAP_ACTION are already set.
//Let's not duplicate them.
String headerName = mimeHeader.getName();
if (headerName.equals(HTTPConstants.HEADER_CONTENT_TYPE)
|| headerName.equals(HTTPConstants.HEADER_SOAP_ACTION)) {
continue;
}
method.addRequestHeader(mimeHeader.getName(),
mimeHeader.getValue());
}
}
// process user defined headers for information.
Hashtable userHeaderTable =
(Hashtable) msgContext.getProperty(HTTPConstants.REQUEST_HEADERS);
if (userHeaderTable != null) {
for (Iterator e = userHeaderTable.entrySet().iterator();
e.hasNext();) {
Map.Entry me = (Map.Entry) e.next();
Object keyObj = me.getKey();
if (null == keyObj) {
continue;
}
String key = keyObj.toString().trim();
String value = me.getValue().toString().trim();
if (key.equalsIgnoreCase(HTTPConstants.HEADER_EXPECT) &&
value.equalsIgnoreCase(HTTPConstants.HEADER_EXPECT_100_Continue)) {
method.getParams().setBooleanParameter(HttpMethodParams.USE_EXPECT_CONTINUE,
true);
} else if (key.equalsIgnoreCase(HTTPConstants.HEADER_TRANSFER_ENCODING_CHUNKED)) {
String val = me.getValue().toString();
if (null != val) {
httpChunkStream = JavaUtils.isTrue(val);
}
} else {
method.addRequestHeader(key, value);
}
}
}
}
/**
* Check if the specified host is in the list of non proxy hosts.
*
* @param host host name
* @param nonProxyHosts string containing the list of non proxy hosts
*
* @return true/false
*/
protected boolean isHostInNonProxyList(String host, String nonProxyHosts) {
if ((nonProxyHosts == null) || (host == null)) {
return false;
}
/*
* The http.nonProxyHosts system property is a list enclosed in
* double quotes with items separated by a vertical bar.
*/
StringTokenizer tokenizer = new StringTokenizer(nonProxyHosts, "|\"");
while (tokenizer.hasMoreTokens()) {
String pattern = tokenizer.nextToken();
if (log.isDebugEnabled()) {
log.debug(Messages.getMessage("match00",
new String[]{"HTTPSender",
host,
pattern}));
}
if (match(pattern, host, false)) {
return true;
}
}
return false;
}
/**
* Matches a string against a pattern. The pattern contains two special
* characters:
* '*' which means zero or more characters,
*
* @param pattern the (non-null) pattern to match against
* @param str the (non-null) string that must be matched against the
* pattern
* @param isCaseSensitive
*
* @return <code>true</code> when the string matches against the pattern,
* <code>false</code> otherwise.
*/
protected static boolean match(String pattern, String str,
boolean isCaseSensitive) {
char[] patArr = pattern.toCharArray();
char[] strArr = str.toCharArray();
int patIdxStart = 0;
int patIdxEnd = patArr.length - 1;
int strIdxStart = 0;
int strIdxEnd = strArr.length - 1;
char ch;
boolean containsStar = false;
for (int i = 0; i < patArr.length; i++) {
if (patArr[i] == '*') {
containsStar = true;
break;
}
}
if (!containsStar) {
// No '*'s, so we make a shortcut
if (patIdxEnd != strIdxEnd) {
return false; // Pattern and string do not have the same size
}
for (int i = 0; i <= patIdxEnd; i++) {
ch = patArr[i];
if (isCaseSensitive && (ch != strArr[i])) {
return false; // Character mismatch
}
if (!isCaseSensitive
&& (Character.toUpperCase(ch)
!= Character.toUpperCase(strArr[i]))) {
return false; // Character mismatch
}
}
return true; // String matches against pattern
}
if (patIdxEnd == 0) {
return true; // Pattern contains only '*', which matches anything
}
// Process characters before first star
while ((ch = patArr[patIdxStart]) != '*'
&& (strIdxStart <= strIdxEnd)) {
if (isCaseSensitive && (ch != strArr[strIdxStart])) {
return false; // Character mismatch
}
if (!isCaseSensitive
&& (Character.toUpperCase(ch)
!= Character.toUpperCase(strArr[strIdxStart]))) {
return false; // Character mismatch
}
patIdxStart++;
strIdxStart++;
}
if (strIdxStart > strIdxEnd) {
// All characters in the string are used. Check if only '*'s are
// left in the pattern. If so, we succeeded. Otherwise failure.
for (int i = patIdxStart; i <= patIdxEnd; i++) {
if (patArr[i] != '*') {
return false;
}
}
return true;
}
// Process characters after last star
while ((ch = patArr[patIdxEnd]) != '*' && (strIdxStart <= strIdxEnd)) {
if (isCaseSensitive && (ch != strArr[strIdxEnd])) {
return false; // Character mismatch
}
if (!isCaseSensitive
&& (Character.toUpperCase(ch)
!= Character.toUpperCase(strArr[strIdxEnd]))) {
return false; // Character mismatch
}
patIdxEnd--;
strIdxEnd--;
}
if (strIdxStart > strIdxEnd) {
// All characters in the string are used. Check if only '*'s are
// left in the pattern. If so, we succeeded. Otherwise failure.
for (int i = patIdxStart; i <= patIdxEnd; i++) {
if (patArr[i] != '*') {
return false;
}
}
return true;
}
// process pattern between stars. padIdxStart and patIdxEnd point
// always to a '*'.
while ((patIdxStart != patIdxEnd) && (strIdxStart <= strIdxEnd)) {
int patIdxTmp = -1;
for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
if (patArr[i] == '*') {
patIdxTmp = i;
break;
}
}
if (patIdxTmp == patIdxStart + 1) {
// Two stars next to each other, skip the first one.
patIdxStart++;
continue;
}
// Find the pattern between padIdxStart & padIdxTmp in str between
// strIdxStart & strIdxEnd
int patLength = (patIdxTmp - patIdxStart - 1);
int strLength = (strIdxEnd - strIdxStart + 1);
int foundIdx = -1;
strLoop:
for (int i = 0; i <= strLength - patLength; i++) {
for (int j = 0; j < patLength; j++) {
ch = patArr[patIdxStart + j + 1];
if (isCaseSensitive
&& (ch != strArr[strIdxStart + i + j])) {
continue strLoop;
}
if (!isCaseSensitive && (Character
.toUpperCase(ch) != Character
.toUpperCase(strArr[strIdxStart + i + j]))) {
continue strLoop;
}
}
foundIdx = strIdxStart + i;
break;
}
if (foundIdx == -1) {
return false;
}
patIdxStart = patIdxTmp;
strIdxStart = foundIdx + patLength;
}
// All characters in the string are used. Check if only '*'s are left
// in the pattern. If so, we succeeded. Otherwise failure.
for (int i = patIdxStart; i <= patIdxEnd; i++) {
if (patArr[i] != '*') {
return false;
}
}
return true;
}
private static String getHeader(HttpMethodBase method, String headerName) {
Header header = method.getResponseHeader(headerName);
return (header == null) ? null : header.getValue().trim();
}
private InputStream createConnectionReleasingInputStream(final HttpMethodBase method) throws IOException {
return new FilterInputStream(method.getResponseBodyAsStream()) {
public void close() throws IOException {
try {
super.close();
} finally {
method.releaseConnection();
}
}
};
}
private static class MessageRequestEntity implements RequestEntity {
private HttpMethodBase method;
private Message message;
boolean httpChunkStream = true; //Use HTTP chunking or not.
public MessageRequestEntity(HttpMethodBase method, Message message) {
this.message = message;
this.method = method;
}
public MessageRequestEntity(HttpMethodBase method, Message message, boolean httpChunkStream) {
this.message = message;
this.method = method;
this.httpChunkStream = httpChunkStream;
}
public boolean isRepeatable() {
return true;
}
public void writeRequest(OutputStream out) throws IOException {
try {
this.message.writeTo(out);
} catch (SOAPException e) {
throw new IOException(e.getMessage());
}
}
protected boolean isContentLengthNeeded() {
return this.method.getParams().getVersion() == HttpVersion.HTTP_1_0 || !httpChunkStream;
}
public long getContentLength() {
if (isContentLengthNeeded()) {
try {
return message.getContentLength();
} catch (Exception e) {
}
}
return -1; /* -1 for chunked */
}
public String getContentType() {
return null; // a separate header is added
}
}
private static class GzipMessageRequestEntity extends MessageRequestEntity {
public GzipMessageRequestEntity(HttpMethodBase method, Message message) {
super(method, message);
}
public GzipMessageRequestEntity(HttpMethodBase method, Message message, boolean httpChunkStream) {
super(method, message, httpChunkStream);
}
public void writeRequest(OutputStream out) throws IOException {
if (cachedStream != null) {
cachedStream.writeTo(out);
} else {
GZIPOutputStream gzStream = new GZIPOutputStream(out);
super.writeRequest(gzStream);
gzStream.finish();
}
}
public long getContentLength() {
if(isContentLengthNeeded()) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
writeRequest(baos);
cachedStream = baos;
return baos.size();
} catch (java.net.SocketTimeoutException e) {
// fall through to doing chunked.
} catch (org.apache.commons.httpclient.ConnectTimeoutException e) {
// fall through to doing chunked
} catch (java.io.InterruptedIOException e) {
Thread.currentThread().interrupt();
// fall through to doing chunked.
} catch (IOException e) {
// fall through to doing chunked.
}
}
return -1; // do chunked
}
private ByteArrayOutputStream cachedStream;
}
}