Author: milinda Date: Mon Dec 27 12:31:53 2010 New Revision: 1053054 URL: http://svn.apache.org/viewvc?rev=1053054&view=rev Log: Fix for AXIS2-3839 and AXIS2-4050. This fix uses the same configuration mechanism used in old code. But there are possible improvements to configuration mechanism and those will be added to the Axis2 later.
Added: axis/axis2/java/core/trunk/modules/transport/http/src/org/apache/axis2/transport/http/util/HTTPProxyConfigurationUtil.java Removed: axis/axis2/java/core/trunk/modules/transport/http/src/org/apache/axis2/transport/http/ProxyConfiguration.java Modified: axis/axis2/java/core/trunk/modules/integration/test/org/apache/axis2/transport/http/NonProxyHostTest.java axis/axis2/java/core/trunk/modules/transport/http/src/org/apache/axis2/transport/http/AbstractHTTPSender.java Modified: axis/axis2/java/core/trunk/modules/integration/test/org/apache/axis2/transport/http/NonProxyHostTest.java URL: http://svn.apache.org/viewvc/axis/axis2/java/core/trunk/modules/integration/test/org/apache/axis2/transport/http/NonProxyHostTest.java?rev=1053054&r1=1053053&r2=1053054&view=diff ============================================================================== --- axis/axis2/java/core/trunk/modules/integration/test/org/apache/axis2/transport/http/NonProxyHostTest.java (original) +++ axis/axis2/java/core/trunk/modules/integration/test/org/apache/axis2/transport/http/NonProxyHostTest.java Mon Dec 27 12:31:53 2010 @@ -20,13 +20,14 @@ package org.apache.axis2.transport.http; import junit.framework.TestCase; +import org.apache.axis2.transport.http.util.HTTPProxyConfigurationUtil; public class NonProxyHostTest extends TestCase { public void testForAxis2_3453() { String nonProxyHosts = "sealbook.ncl.ac.uk|*.sealbook.ncl.ac.uk|eskdale.ncl.ac.uk|*.eskdale.ncl.ac.uk|giga25.ncl.ac.uk|*.giga25.ncl.ac.uk"; - assertTrue(ProxyConfiguration.isHostInNonProxyList("sealbook.ncl.ac.uk", nonProxyHosts)); - assertFalse(ProxyConfiguration.isHostInNonProxyList("xsealbook.ncl.ac.uk", nonProxyHosts)); - assertTrue(ProxyConfiguration.isHostInNonProxyList("local","local|*.local|169.254/16|*.169.254/16")); - assertFalse(ProxyConfiguration.isHostInNonProxyList("localhost","local|*.local|169.254/16|*.169.254/16")); + assertTrue(HTTPProxyConfigurationUtil.isHostInNonProxyList("sealbook.ncl.ac.uk", nonProxyHosts)); + assertFalse(HTTPProxyConfigurationUtil.isHostInNonProxyList("xsealbook.ncl.ac.uk", nonProxyHosts)); + assertTrue(HTTPProxyConfigurationUtil.isHostInNonProxyList("local","local|*.local|169.254/16|*.169.254/16")); + assertFalse(HTTPProxyConfigurationUtil.isHostInNonProxyList("localhost","local|*.local|169.254/16|*.169.254/16")); } } Modified: axis/axis2/java/core/trunk/modules/transport/http/src/org/apache/axis2/transport/http/AbstractHTTPSender.java URL: http://svn.apache.org/viewvc/axis/axis2/java/core/trunk/modules/transport/http/src/org/apache/axis2/transport/http/AbstractHTTPSender.java?rev=1053054&r1=1053053&r2=1053054&view=diff ============================================================================== --- axis/axis2/java/core/trunk/modules/transport/http/src/org/apache/axis2/transport/http/AbstractHTTPSender.java (original) +++ axis/axis2/java/core/trunk/modules/transport/http/src/org/apache/axis2/transport/http/AbstractHTTPSender.java Mon Dec 27 12:31:53 2010 @@ -31,6 +31,7 @@ import org.apache.axis2.description.Tran import org.apache.axis2.i18n.Messages; import org.apache.axis2.transport.MessageFormatter; import org.apache.axis2.transport.TransportUtils; +import org.apache.axis2.transport.http.util.HTTPProxyConfigurationUtil; import org.apache.axis2.util.JavaUtils; import org.apache.axis2.wsdl.WSDLConstants; import org.apache.commons.httpclient.Credentials; @@ -286,10 +287,11 @@ public abstract class AbstractHTTPSender } // proxy configuration - if (ProxyConfiguration.isProxyEnabled(msgCtx,targetURL)) { - log.debug("ProxyConfiguration"); - ProxyConfiguration proxyConfiguration = new ProxyConfiguration(); - proxyConfiguration.configure(msgCtx,client,config); + if (HTTPProxyConfigurationUtil.isProxyEnabled(msgCtx, targetURL)) { + if(log.isDebugEnabled()){ + log.debug("Configuring HTTP proxy."); + } + HTTPProxyConfigurationUtil.configure(msgCtx, client, config); } return config; Added: axis/axis2/java/core/trunk/modules/transport/http/src/org/apache/axis2/transport/http/util/HTTPProxyConfigurationUtil.java URL: http://svn.apache.org/viewvc/axis/axis2/java/core/trunk/modules/transport/http/src/org/apache/axis2/transport/http/util/HTTPProxyConfigurationUtil.java?rev=1053054&view=auto ============================================================================== --- axis/axis2/java/core/trunk/modules/transport/http/src/org/apache/axis2/transport/http/util/HTTPProxyConfigurationUtil.java (added) +++ axis/axis2/java/core/trunk/modules/transport/http/src/org/apache/axis2/transport/http/util/HTTPProxyConfigurationUtil.java Mon Dec 27 12:31:53 2010 @@ -0,0 +1,472 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.util; + +import org.apache.axiom.om.OMElement; +import org.apache.axis2.AxisFault; +import org.apache.axis2.context.MessageContext; +import org.apache.axis2.description.Parameter; +import org.apache.axis2.transport.http.HTTPConstants; +import org.apache.axis2.transport.http.HttpTransportProperties; +import org.apache.commons.httpclient.*; +import org.apache.commons.httpclient.auth.AuthScope; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.xml.namespace.QName; +import java.net.URL; +import java.util.StringTokenizer; + +/** + * Contains utility functions used when configuring HTTP Proxy for HTTP Sender. + */ +public class HTTPProxyConfigurationUtil { + private static Log log = LogFactory.getLog(HTTPProxyConfigurationUtil.class); + + protected static final String HTTP_PROXY_HOST = "http.proxyHost"; + protected static final String HTTP_PROXY_PORT = "http.proxyPort"; + protected static final String HTTP_NON_PROXY_HOSTS = "http.nonProxyHosts"; + + protected static final String ATTR_PROXY = "Proxy"; + protected static final String PROXY_HOST_ELEMENT = "ProxyHost"; + protected static final String PROXY_PORT_ELEMENT = "ProxyPort"; + protected static final String PROXY_USER_ELEMENT = "ProxyUser"; + protected static final String PROXY_PASSWORD_ELEMENT = "ProxyPassword"; + + + protected static final String PROXY_CONFIGURATION_NOT_FOUND = + "HTTP Proxy is enabled, but proxy configuration element is missing in axis2.xml"; + protected static final String PROXY_HOST_ELEMENT_NOT_FOUND = + "HTTP Proxy is enabled, but proxy host element is missing in axis2.xml"; + protected static final String PROXY_PORT_ELEMENT_NOT_FOUND = + "HTTP Proxy is enabled, but proxy port element is missing in axis2.xml"; + protected static final String PROXY_HOST_ELEMENT_WITH_EMPTY_VALUE = + "HTTP Proxy is enabled, but proxy host value is empty."; + protected static final String PROXY_PORT_ELEMENT_WITH_EMPTY_VALUE = + "HTTP Proxy is enabled, but proxy port value is empty."; + + /** + * Configure HTTP Proxy settings of commons-httpclient HostConfiguration. Proxy settings can be get from + * axis2.xml, Java proxy settings or can be override through property in message context. + * <p/> + * HTTP Proxy setting element format: + * <parameter name="Proxy"> + * <Configuration> + * <ProxyHost>example.org</ProxyHost> + * <ProxyPort>3128</ProxyPort> + * <ProxyUser>EXAMPLE/John</ProxyUser> + * <ProxyPassword>password</ProxyPassword> + * <Configuration> + * <parameter> + * + * @param messageContext in message context for + * @param httpClient commons-httpclient instance + * @param config commons-httpclient HostConfiguration + * @throws AxisFault if Proxy settings are invalid + */ + public static void configure(MessageContext messageContext, + HttpClient httpClient, + HostConfiguration config) throws AxisFault { + + Credentials proxyCredentials = null; + String proxyHost = null; + String nonProxyHosts = null; + Integer proxyPort = -1; + String proxyUser = null; + String proxyPassword = null; + + //Getting configuration values from Axis2.xml + Parameter proxySettingsFromAxisConfig = messageContext.getConfigurationContext().getAxisConfiguration() + .getParameter(ATTR_PROXY); + if (proxySettingsFromAxisConfig != null) { + OMElement proxyConfiguration = getProxyConfigurationElement(proxySettingsFromAxisConfig); + proxyHost = getProxyHost(proxyConfiguration); + proxyPort = getProxyPort(proxyConfiguration); + proxyUser = getProxyUser(proxyConfiguration); + proxyPassword = getProxyPassword(proxyConfiguration); + if(proxyUser != null){ + if(proxyPassword == null){ + proxyPassword = ""; + } + int proxyUserDomainIndex = proxyUser.indexOf("\\"); + if( proxyUserDomainIndex > 0){ + String domain = proxyUser.substring(0, proxyUserDomainIndex); + if(proxyUser.length() > proxyUserDomainIndex + 1) { + String user = proxyUser.substring(proxyUserDomainIndex + 1); + proxyCredentials = new NTCredentials(user, proxyPassword, proxyHost, domain); + } + } + proxyCredentials = new UsernamePasswordCredentials(proxyUser, proxyPassword); + } + + } + + // If there is runtime proxy settings, these settings will override settings from axis2.xml + HttpTransportProperties.ProxyProperties proxyProperties = + (HttpTransportProperties.ProxyProperties) messageContext.getProperty(HTTPConstants.PROXY); + if(proxyProperties != null) { + String proxyHostProp = proxyProperties.getProxyHostName(); + if(proxyHostProp == null || proxyHostProp.length() <= 0) { + throw new AxisFault("HTTP Proxy host is not available. Host is a MUST parameter"); + } else { + proxyHost = proxyHostProp; + } + proxyPort = proxyProperties.getProxyPort(); + + // Overriding credentials + String userName = proxyProperties.getUserName(); + String password = proxyProperties.getPassWord(); + String domain = proxyProperties.getDomain(); + + if(userName != null && password != null && domain != null){ + proxyCredentials = new NTCredentials(userName, password, proxyHost, domain); + } else if(userName != null && domain == null){ + proxyCredentials = new UsernamePasswordCredentials(userName, password); + } + + } + + // Overriding proxy settings if proxy is available from JVM settings + String host = System.getProperty(HTTP_PROXY_HOST); + if(host != null) { + proxyHost = host; + } + + String port = System.getProperty(HTTP_PROXY_PORT); + if(port != null) { + proxyPort = Integer.parseInt(port); + } + + if(proxyCredentials != null) { + httpClient.getParams().setAuthenticationPreemptive(true); + HttpState cachedHttpState = (HttpState)messageContext.getProperty(HTTPConstants.CACHED_HTTP_STATE); + if(cachedHttpState != null){ + httpClient.setState(cachedHttpState); + } + httpClient.getState().setProxyCredentials(AuthScope.ANY, proxyCredentials); + } + config.setProxy(proxyHost, proxyPort); + } + + private static OMElement getProxyConfigurationElement(Parameter proxySettingsFromAxisConfig) throws AxisFault { + OMElement proxyConfigurationElement = proxySettingsFromAxisConfig.getParameterElement().getFirstElement(); + if (proxyConfigurationElement == null) { + log.error(PROXY_CONFIGURATION_NOT_FOUND); + throw new AxisFault(PROXY_CONFIGURATION_NOT_FOUND); + } + return proxyConfigurationElement; + } + + private static String getProxyHost(OMElement proxyConfiguration) throws AxisFault { + OMElement proxyHostElement = proxyConfiguration.getFirstChildWithName(new QName(PROXY_HOST_ELEMENT)); + if (proxyHostElement == null) { + log.error(PROXY_HOST_ELEMENT_NOT_FOUND); + throw new AxisFault(PROXY_HOST_ELEMENT_NOT_FOUND); + } + String proxyHost = proxyHostElement.getText(); + if (proxyHost == null) { + log.error(PROXY_HOST_ELEMENT_WITH_EMPTY_VALUE); + throw new AxisFault(PROXY_HOST_ELEMENT_WITH_EMPTY_VALUE); + } + return proxyHost; + } + + private static Integer getProxyPort(OMElement proxyConfiguration) throws AxisFault { + OMElement proxyPortElement = proxyConfiguration.getFirstChildWithName(new QName(PROXY_PORT_ELEMENT)); + if (proxyPortElement == null) { + log.error(PROXY_PORT_ELEMENT_NOT_FOUND); + throw new AxisFault(PROXY_PORT_ELEMENT_NOT_FOUND); + } + String proxyPort = proxyPortElement.getText(); + if (proxyPort == null) { + log.error(PROXY_PORT_ELEMENT_WITH_EMPTY_VALUE); + throw new AxisFault(PROXY_PORT_ELEMENT_WITH_EMPTY_VALUE); + } + return Integer.parseInt(proxyPort); + } + + private static String getProxyUser(OMElement proxyConfiguration) { + OMElement proxyUserElement = proxyConfiguration.getFirstChildWithName(new QName(PROXY_USER_ELEMENT)); + if (proxyUserElement == null) { + return null; + } + String proxyUser = proxyUserElement.getText(); + if (proxyUser == null) { + log.warn("Empty user name element in HTTP Proxy settings."); + return null; + } + + return proxyUser; + } + + private static String getProxyPassword(OMElement proxyConfiguration) { + OMElement proxyPasswordElement = proxyConfiguration.getFirstChildWithName(new QName(PROXY_PASSWORD_ELEMENT)); + if (proxyPasswordElement == null) { + return null; + } + String proxyUser = proxyPasswordElement.getText(); + if (proxyUser == null) { + log.warn("Empty user name element in HTTP Proxy settings."); + return null; + } + + return proxyUser; + } + + /** + * Check whether http proxy is configured or active. + * This is not a deep check. + * + * @param messageContext in message context + * @param targetURL URL of the edpoint which we are sending the request + * @return true if proxy is enabled, false otherwise + */ + public static boolean isProxyEnabled(MessageContext messageContext, URL targetURL) { + boolean proxyEnabled = false; + + Parameter param = messageContext.getConfigurationContext().getAxisConfiguration() + .getParameter(ATTR_PROXY); + + //If configuration is over ridden + Object obj = messageContext.getProperty(HTTPConstants.PROXY); + + //From Java Networking Properties + String sp = System.getProperty(HTTP_PROXY_HOST); + + if (param != null || obj != null || sp != null) { + proxyEnabled = true; + } + + boolean isNonProxyHost = validateNonProxyHosts(targetURL.getHost()); + + return proxyEnabled && !isNonProxyHost; + } + + /** + * Validates for names that shouldn't be listered as proxies. + * The http.nonProxyHosts can be set to specify the hosts which should be + * connected to directly (not through the proxy server). + * The value of the http.nonProxyHosts property can be a list of hosts, + * each separated by a |; it can also take a regular expression for matches; + * for example: *.sfbay.sun.com would match any fully qualified hostname in the sfbay domain. + * <p/> + * For more information refer to : http://java.sun.com/features/2002/11/hilevel_network.html + * <p/> + * false : validation fail : User can use the proxy + * true : validation pass ; User can't use the proxy + * + * @return boolean + */ + private static boolean validateNonProxyHosts(String host) { + //From system property http.nonProxyHosts + String nonProxyHosts = System.getProperty(HTTP_NON_PROXY_HOSTS); + return isHostInNonProxyList(host, nonProxyHosts); + } + + /** + * 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 + */ + public static 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 (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. + */ + private 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; + } + +}