/* | |
* Copyright 2004,2005 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. | |
*/ | |
package org.apache.axis2.transport.http; | |
import org.apache.axiom.om.OMAttribute; | |
import org.apache.axiom.om.OMElement; | |
import org.apache.axiom.om.OMOutputFormat; | |
import org.apache.axis2.AxisFault; | |
import org.apache.axis2.Constants; | |
import org.apache.axis2.context.MessageContext; | |
import org.apache.axis2.context.OperationContext; | |
import org.apache.axis2.description.Parameter; | |
import org.apache.axis2.description.TransportOutDescription; | |
import org.apache.axis2.i18n.Messages; | |
import org.apache.axis2.util.JavaUtils; | |
import org.apache.axis2.util.Utils; | |
import org.apache.commons.httpclient.*; | |
import org.apache.commons.httpclient.auth.AuthScope; | |
import org.apache.commons.httpclient.auth.AuthPolicy; | |
import org.apache.commons.logging.Log; | |
import org.apache.commons.logging.LogFactory; | |
import javax.xml.namespace.QName; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.net.MalformedURLException; | |
import java.net.URL; | |
import java.util.ArrayList; | |
import java.util.Iterator; | |
import java.util.List; | |
import java.util.zip.GZIPInputStream; | |
public abstract class AbstractHTTPSender { | |
protected static final String ANONYMOUS = "anonymous"; | |
protected static final String PROXY_HOST_NAME = "proxy_host"; | |
protected static final String PROXY_PORT = "proxy_port"; | |
protected boolean chunked = false; | |
protected String httpVersion = HTTPConstants.HEADER_PROTOCOL_11; | |
private static final Log log = LogFactory.getLog(AbstractHTTPSender.class); | |
int soTimeout = HTTPConstants.DEFAULT_SO_TIMEOUT; | |
/** | |
* proxydiscription | |
*/ | |
protected TransportOutDescription proxyOutSetting = null; | |
protected OMOutputFormat format = new OMOutputFormat(); | |
int connectionTimeout = HTTPConstants.DEFAULT_CONNECTION_TIMEOUT; | |
/** | |
* isAllowedRetry will be using to check where the | |
* retry should be allowed or not. | |
*/ | |
protected boolean isAllowedRetry = false; | |
public void setChunked(boolean chunked) { | |
this.chunked = chunked; | |
} | |
public void setHttpVersion(String version) throws AxisFault { | |
if (version != null) { | |
if (HTTPConstants.HEADER_PROTOCOL_11.equals(version)) { | |
this.httpVersion = HTTPConstants.HEADER_PROTOCOL_11; | |
} else if (HTTPConstants.HEADER_PROTOCOL_10.equals(version)) { | |
this.httpVersion = HTTPConstants.HEADER_PROTOCOL_10; | |
// chunked is not possible with HTTP/1.0 | |
this.chunked = false; | |
} else { | |
throw new AxisFault( | |
"Parameter " + HTTPConstants.PROTOCOL_VERSION | |
+ " Can have values only HTTP/1.0 or HTTP/1.1"); | |
} | |
} | |
} | |
/** | |
* Helper method to Proxy and NTLM authentication | |
* | |
* @param client | |
* @param proxySetting | |
* @param config | |
*/ | |
protected void configProxyAuthentication(HttpClient client, | |
TransportOutDescription proxySetting, | |
HostConfiguration config, | |
MessageContext msgCtx) | |
throws AxisFault { | |
Parameter proxyParam = proxySetting.getParameter(HTTPConstants.PROXY); | |
String usrName; | |
String domain; | |
String passwd; | |
Credentials proxyCred = null; | |
String proxyHostName = null; | |
int proxyPort = -1; | |
if (proxyParam != null) { | |
String value = (String) proxyParam.getValue(); | |
String split[] = value.split(":"); | |
// values being hard coded due best practise | |
usrName = split[0]; | |
domain = split[1]; | |
passwd = split[2]; | |
OMElement proxyParamElement = proxyParam.getParameterElement(); | |
Iterator ite = proxyParamElement.getAllAttributes(); | |
while (ite.hasNext()) { | |
OMAttribute att = (OMAttribute) ite.next(); | |
if (att.getLocalName().equalsIgnoreCase(PROXY_HOST_NAME)) { | |
proxyHostName = att.getAttributeValue(); | |
} | |
if (att.getLocalName().equalsIgnoreCase(PROXY_PORT)) { | |
proxyPort = Integer.parseInt(att.getAttributeValue()); | |
} | |
} | |
if (domain.length() == 0 || domain.equals(ANONYMOUS)) { | |
if (usrName.equals(ANONYMOUS) && passwd.equals(ANONYMOUS)) { | |
proxyCred = new UsernamePasswordCredentials("", ""); | |
} else { | |
proxyCred = new UsernamePasswordCredentials(usrName, | |
passwd); // proxy | |
} | |
} else { | |
proxyCred = new NTCredentials(usrName, passwd, proxyHostName, | |
domain); // NTLM authentication with additionals prams | |
} | |
} | |
HttpTransportProperties.ProxyProperties proxyProperties = | |
(HttpTransportProperties.ProxyProperties) msgCtx | |
.getProperty(HTTPConstants.PROXY); | |
if (proxyProperties != null) { | |
if (proxyProperties.getProxyPort() != -1) { | |
proxyPort = proxyProperties.getProxyPort(); | |
} | |
proxyHostName = proxyProperties.getProxyHostName(); | |
if (proxyHostName == null | |
|| proxyHostName.length() == 0) { | |
throw new AxisFault("Proxy Name is not valid"); | |
} | |
if (proxyProperties.getUserName().equals(ANONYMOUS) | |
|| proxyProperties.getPassWord().equals(ANONYMOUS)) { | |
proxyCred = new UsernamePasswordCredentials("", ""); | |
} | |
if (!proxyProperties.getUserName().equals(ANONYMOUS) && | |
!proxyProperties.getPassWord().equals(ANONYMOUS)) { | |
proxyCred = new UsernamePasswordCredentials( | |
proxyProperties.getUserName().trim(), | |
proxyProperties | |
.getPassWord().trim()); // Basic Authentication | |
} | |
if (!proxyProperties.getDomain().equals(ANONYMOUS)) { | |
if (!proxyProperties.getUserName().equals(ANONYMOUS) && | |
!proxyProperties.getPassWord().equals(ANONYMOUS) && | |
!proxyProperties.getDomain().equals(ANONYMOUS)) { | |
proxyCred = new NTCredentials( | |
proxyProperties.getUserName().trim(), | |
proxyProperties.getPassWord().trim(), proxyHostName, | |
proxyProperties | |
.getDomain().trim()); // NTLM Authentication | |
} | |
} | |
} | |
client.getState().setProxyCredentials(AuthScope.ANY, proxyCred); | |
config.setProxy(proxyHostName, proxyPort); | |
} | |
/** | |
* Collect the HTTP header information and set them in the message context | |
* | |
* @param method | |
* @param msgContext | |
*/ | |
protected void obtainHTTPHeaderInformation(HttpMethodBase method, | |
MessageContext msgContext) { | |
Header header = | |
method.getResponseHeader(HTTPConstants.HEADER_CONTENT_TYPE); | |
if (header != null) { | |
HeaderElement[] headers = header.getElements(); | |
for (int i = 0; i < headers.length; i++) { | |
NameValuePair charsetEnc = | |
headers[i].getParameterByName( | |
HTTPConstants.CHAR_SET_ENCODING); | |
OperationContext opContext = msgContext.getOperationContext(); | |
String name = headers[i].getName(); | |
if (name.equalsIgnoreCase( | |
HTTPConstants.HEADER_ACCEPT_MULTIPART_RELATED)) { | |
if (opContext != null) { | |
opContext.setProperty( | |
HTTPConstants.MTOM_RECEIVED_CONTENT_TYPE, | |
header.getValue()); | |
} | |
} else if (charsetEnc != null) { | |
if (opContext != null) { | |
opContext.setProperty(Constants.Configuration.CHARACTER_SET_ENCODING, | |
charsetEnc.getValue()); // change to the value, which is text/xml or application/xml+soap | |
} | |
} | |
} | |
} | |
String sessionCookie = null; | |
// Process old style headers first | |
Header[] cookieHeaders = method.getResponseHeaders(HTTPConstants.HEADER_SET_COOKIE); | |
for (int i = 0; i < cookieHeaders.length; i++) { | |
HeaderElement[] elements = cookieHeaders[i].getElements(); | |
for (int e = 0; e < elements.length; e++) { | |
HeaderElement element = elements[e]; | |
if (Constants.SESSION_COOKIE.equalsIgnoreCase(element.getName())) { | |
sessionCookie = element.getValue(); | |
} | |
} | |
} | |
// Overwrite old style cookies with new style ones if present | |
cookieHeaders = method.getResponseHeaders(HTTPConstants.HEADER_SET_COOKIE2); | |
for (int i = 0; i < cookieHeaders.length; i++) { | |
HeaderElement[] elements = cookieHeaders[i].getElements(); | |
for (int e = 0; e < elements.length; e++) { | |
HeaderElement element = elements[e]; | |
if (Constants.SESSION_COOKIE.equalsIgnoreCase(element.getName())) { | |
sessionCookie = element.getValue(); | |
} | |
} | |
} | |
if (sessionCookie != null) { | |
msgContext.getServiceContext().setProperty(HTTPConstants.COOKIE_STRING, sessionCookie); | |
} | |
} | |
protected void processResponse(HttpMethodBase httpMethod, | |
MessageContext msgContext) | |
throws IOException { | |
obtainHTTPHeaderInformation(httpMethod, msgContext); | |
InputStream in = httpMethod.getResponseBodyAsStream(); | |
Header contentEncoding = | |
httpMethod.getResponseHeader(HTTPConstants.HEADER_CONTENT_ENCODING); | |
if (contentEncoding != null) { | |
if (contentEncoding.getValue(). | |
equalsIgnoreCase(HTTPConstants.COMPRESSION_GZIP)) { | |
in = | |
new GZIPInputStream(in); | |
} else { | |
throw new AxisFault("HTTP :" | |
+ "unsupported content-encoding of '" | |
+ contentEncoding.getValue() | |
+ "' found"); | |
} | |
} | |
if (in == null) { | |
throw new AxisFault( | |
Messages.getMessage("canNotBeNull", "InputStream")); | |
} | |
if (msgContext.getOperationContext() != null) { | |
msgContext.getOperationContext() | |
.setProperty(MessageContext.TRANSPORT_IN, in); | |
} | |
} | |
public abstract void send(MessageContext msgContext, OMElement dataout, | |
URL url, | |
String soapActionString) | |
throws MalformedURLException, AxisFault, IOException; | |
/** | |
* getting host configuration to support standard http/s, proxy and NTLM support | |
*/ | |
protected HostConfiguration getHostConfiguration(HttpClient client, | |
MessageContext msgCtx, | |
URL targetURL) | |
throws AxisFault { | |
boolean isHostProxy = isProxyListed(msgCtx); // list the proxy | |
boolean isAuthenticationEnabled = isAuthenticationEnabled(msgCtx); | |
int port = targetURL.getPort(); | |
if (port == -1) { | |
port = 80; | |
} | |
// to see the host is a proxy and in the proxy list - available in axis2.xml | |
HostConfiguration config = new HostConfiguration(); | |
if (isAuthenticationEnabled) { | |
// premtive authentication Basic or NTLM | |
this.setAuthenticationInfo(client, msgCtx, config, targetURL); | |
} | |
// proxy configuration | |
if (!isHostProxy) { | |
config.setHost(targetURL.getHost(), port, targetURL.getProtocol()); | |
} else { | |
this.configProxyAuthentication(client, proxyOutSetting, config, | |
msgCtx); | |
} | |
return config; | |
} | |
protected boolean isAuthenticationEnabled(MessageContext msgCtx) { | |
return (msgCtx.getProperty(HTTPConstants.AUTHENTICATE) != null); | |
} | |
/* | |
This will handle server Authentication, It could be either NTLM, Digest or Basic Authentication. | |
Apart from that user can change the priory or add a custom authentication scheme. | |
*/ | |
protected void setAuthenticationInfo(HttpClient agent, | |
MessageContext msgCtx, | |
HostConfiguration config, | |
URL targetURL) throws AxisFault { | |
config.setHost(targetURL.getHost(), targetURL.getPort(), | |
targetURL.getProtocol()); | |
HttpTransportProperties.Authenticator authenticator; | |
Object obj = msgCtx.getProperty(HTTPConstants.AUTHENTICATE); | |
if (obj != null) { | |
if (obj instanceof HttpTransportProperties.Authenticator) { | |
authenticator = (HttpTransportProperties.Authenticator) obj; | |
String username = authenticator.getUsername(); | |
String password = authenticator.getPassword(); | |
String host = authenticator.getHost(); | |
String domain = authenticator.getDomain(); | |
int port = authenticator.getPort(); | |
String realm = authenticator.getRealm(); | |
/* If retrying is available set it first */ | |
isAllowedRetry = authenticator.isAllowedRetry(); | |
Credentials creds; | |
agent.getParams().setAuthenticationPreemptive(authenticator.getPreemptiveAuthentication()); | |
if (host != null) { | |
if (domain != null) { | |
/*Credentials for NTLM Authentication*/ | |
creds = new NTCredentials(username, password, host, domain); | |
} else { | |
/*Credentials for Digest and Basic Authentication*/ | |
creds = new UsernamePasswordCredentials(username, password); | |
} | |
agent.getState().setCredentials(new AuthScope(host, port, realm), creds); | |
} else { | |
/*Credentials only for Digest and Basic Authentication*/ | |
creds = new UsernamePasswordCredentials(username, password); | |
agent.getState().setCredentials(new AuthScope(AuthScope.ANY), creds); | |
} | |
/* Customizing the priority Order */ | |
List schemes = authenticator.getAuthSchemes(); | |
if (schemes != null && schemes.size() > 0) { | |
List authPrefs = new ArrayList(3); | |
for (int i = 0; i < schemes.size(); i++) { | |
if (schemes.get(i) instanceof AuthPolicy) { | |
authPrefs.add(schemes.get(i)); | |
continue; | |
} | |
String scheme = (String) schemes.get(i); | |
if (HttpTransportProperties.Authenticator.BASIC.equals(scheme)) { | |
authPrefs.add(AuthPolicy.BASIC); | |
} else if (HttpTransportProperties.Authenticator.NTLM.equals(scheme)) { | |
authPrefs.add(AuthPolicy.NTLM); | |
} else if (HttpTransportProperties.Authenticator.DIGEST.equals(scheme)) { | |
authPrefs.add(AuthPolicy.DIGEST); | |
} | |
} | |
agent.getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, | |
authPrefs); | |
} | |
} else { | |
throw new AxisFault("HttpTransportProperties.Authenticator class cast exception"); | |
} | |
} | |
} | |
/** | |
* This is used to get the dynamically set time out values from the | |
* message context. If the values are not available or invalid then | |
* teh default values or the values set by teh configuration will be used | |
* | |
* @param msgContext | |
*/ | |
protected void getTimeoutValues(MessageContext msgContext) { | |
try { | |
// If the SO_TIMEOUT of CONNECTION_TIMEOUT is set by dynamically the | |
// override the static config | |
Integer tempSoTimeoutProperty = | |
(Integer) msgContext.getProperty(HTTPConstants.SO_TIMEOUT); | |
Integer tempConnTimeoutProperty = | |
(Integer) msgContext | |
.getProperty(HTTPConstants.CONNECTION_TIMEOUT); | |
if (tempSoTimeoutProperty != null) { | |
soTimeout = tempSoTimeoutProperty.intValue(); | |
} | |
if (tempConnTimeoutProperty != null) { | |
connectionTimeout = tempConnTimeoutProperty.intValue(); | |
} | |
} catch (NumberFormatException nfe) { | |
// If there's a problem log it and use the default values | |
log.error("Invalid timeout value format: not a number", nfe); | |
} | |
} | |
private boolean isProxyListed(MessageContext msgCtx) throws AxisFault { | |
boolean returnValue = false; | |
Parameter par = null; | |
proxyOutSetting = msgCtx.getConfigurationContext() | |
.getAxisConfiguration().getTransportOut( | |
new QName(Constants.TRANSPORT_HTTP)); | |
if (proxyOutSetting != null) { | |
par = proxyOutSetting.getParameter(HTTPConstants.PROXY); | |
} | |
OMElement hostElement = null; | |
if (par != null) { | |
hostElement = par.getParameterElement(); | |
} | |
if (hostElement != null) { | |
Iterator ite = hostElement.getAllAttributes(); | |
while (ite.hasNext()) { | |
OMAttribute attribute = (OMAttribute) ite.next(); | |
if (attribute.getLocalName().equalsIgnoreCase(PROXY_HOST_NAME)) { | |
returnValue = true; | |
} | |
} | |
} | |
HttpTransportProperties.ProxyProperties proxyProperties; | |
if ((proxyProperties = | |
(HttpTransportProperties.ProxyProperties) msgCtx.getProperty( | |
HTTPConstants.PROXY)) != null) { | |
if (proxyProperties.getProxyHostName() != null) { | |
returnValue = true; | |
} | |
} | |
return returnValue; | |
} | |
public void setFormat(OMOutputFormat format) { | |
this.format = format; | |
} | |
protected HttpClient getHttpClient(MessageContext msgContext) { | |
HttpClient httpClient; | |
Object reuse = msgContext.getOptions().getProperty(HTTPConstants.REUSE_HTTP_CLIENT); | |
if (reuse != null && JavaUtils.isTrueExplicitly(reuse)) { | |
httpClient = (HttpClient) msgContext.getConfigurationContext() | |
.getProperty(HTTPConstants.CACHED_HTTP_CLIENT); | |
if (httpClient == null) { | |
MultiThreadedHttpConnectionManager connectionManager = | |
new MultiThreadedHttpConnectionManager(); | |
httpClient = new HttpClient(connectionManager); | |
msgContext.getConfigurationContext() | |
.setProperty(HTTPConstants.CACHED_HTTP_CLIENT, httpClient); | |
} | |
} else { | |
httpClient = new HttpClient(); | |
} | |
// Get the timeout values set in the runtime | |
getTimeoutValues(msgContext); | |
// SO_TIMEOUT -- timeout for blocking reads | |
httpClient.getHttpConnectionManager().getParams().setSoTimeout(soTimeout); | |
// timeout for initial connection | |
httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(connectionTimeout); | |
return httpClient; | |
} | |
protected void executeMethod(HttpClient httpClient, MessageContext msgContext, URL url, | |
HttpMethod method) throws IOException { | |
HostConfiguration config = this.getHostConfiguration(httpClient, msgContext, url); | |
msgContext.setProperty(HTTPConstants.HTTP_METHOD, method); | |
// set the custom headers, if available | |
addCustomHeaders(method, msgContext); | |
// add compression headers if needed | |
if (Utils.isExplicitlyTrue(msgContext, HTTPConstants.MC_ACCEPT_GZIP)) { | |
method.addRequestHeader(HTTPConstants.HEADER_ACCEPT_ENCODING, | |
HTTPConstants.COMPRESSION_GZIP); | |
} | |
if (Utils.isExplicitlyTrue(msgContext, HTTPConstants.MC_GZIP_REQUEST)) { | |
method.addRequestHeader(HTTPConstants.HEADER_CONTENT_ENCODING, | |
HTTPConstants.COMPRESSION_GZIP); | |
} | |
httpClient.executeMethod(config, method); | |
} | |
public void addCustomHeaders(HttpMethod method, MessageContext msgContext) { | |
boolean isCustomUserAgentSet = false; | |
// set the custom headers, if available | |
Object httpHeadersObj = msgContext.getProperty(HTTPConstants.HTTP_HEADERS); | |
if (httpHeadersObj != null && httpHeadersObj instanceof ArrayList) { | |
ArrayList httpHeaders = (ArrayList) httpHeadersObj; | |
Header header; | |
for (int i = 0; i < httpHeaders.size(); i++) { | |
header = (Header) httpHeaders.get(i); | |
if (HTTPConstants.HEADER_USER_AGENT.equals(header.getName())) { | |
isCustomUserAgentSet = true; | |
} | |
method.addRequestHeader(header); | |
} | |
} | |
if (!isCustomUserAgentSet) { | |
String userAgentString = getUserAgent(msgContext); | |
method.setRequestHeader(HTTPConstants.HEADER_USER_AGENT, userAgentString); | |
} | |
} | |
private String getUserAgent(MessageContext messageContext) { | |
String userAgentString = "Axis2"; | |
boolean locked = false; | |
if (messageContext.getParameter(HTTPConstants.USER_AGENT) != null) { | |
OMElement userAgentElement = | |
messageContext.getParameter(HTTPConstants.USER_AGENT).getParameterElement(); | |
userAgentString = userAgentElement.getText().trim(); | |
OMAttribute lockedAttribute = userAgentElement.getAttribute(new QName("locked")); | |
if (lockedAttribute != null) { | |
if (lockedAttribute.getAttributeValue().equalsIgnoreCase("true")) { | |
locked = true; | |
} | |
} | |
} | |
// Runtime overing part | |
if (!locked) { | |
if (messageContext.getProperty(HTTPConstants.USER_AGENT) != null) { | |
userAgentString = (String) messageContext.getProperty(HTTPConstants.USER_AGENT); | |
} | |
} | |
return userAgentString; | |
} | |
} |