dims 2003/03/06 05:22:57
Modified: java/src/org/apache/axis/transport/http
CommonsHTTPSender.java
Log:
Kind-Of Fix for Bug 16522 - Allow Axis HTTP 1.1 clients to use Keep-Alive
from [EMAIL PROTECTED]
Notes:
This updates just the CommonsHTTPSender...Bug still exists in HTTPSender.
Revision Changes Path
1.11 +101 -136
xml-axis/java/src/org/apache/axis/transport/http/CommonsHTTPSender.java
Index: CommonsHTTPSender.java
===================================================================
RCS file:
/home/cvs/xml-axis/java/src/org/apache/axis/transport/http/CommonsHTTPSender.java,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -r1.10 -r1.11
--- CommonsHTTPSender.java 1 Mar 2003 22:33:16 -0000 1.10
+++ CommonsHTTPSender.java 6 Mar 2003 13:22:56 -0000 1.11
@@ -58,7 +58,6 @@
import org.apache.axis.Message;
import org.apache.axis.MessageContext;
import org.apache.axis.components.logger.LogFactory;
-import org.apache.axis.components.net.BooleanHolder;
import org.apache.axis.components.net.TransportClientProperties;
import org.apache.axis.components.net.TransportClientPropertiesFactory;
import org.apache.axis.encoding.Base64;
@@ -66,13 +65,16 @@
import org.apache.axis.utils.Messages;
import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.Header;
-import org.apache.commons.httpclient.HttpConnection;
-import org.apache.commons.httpclient.HttpState;
+import org.apache.commons.httpclient.HostConfiguration;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.HttpConnectionManager;
+import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.logging.Log;
import java.io.ByteArrayOutputStream;
+import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.util.Hashtable;
import java.util.StringTokenizer;
@@ -86,7 +88,15 @@
/** Field log */
protected static Log log =
- LogFactory.getLog(CommonsHTTPSender.class.getName());
+ LogFactory.getLog(CommonsHTTPSender.class.getName());
+
+ private HttpConnectionManager connectionManager;
+
+ public CommonsHTTPSender() {
+ // should pull settings for pool size, timeouts from
+ // declarative configuration
+ connectionManager = new MultiThreadedHttpConnectionManager();
+ }
/**
* invoke creates a socket connection, sends the request SOAP message and then
@@ -97,86 +107,89 @@
* @throws AxisFault
*/
public void invoke(MessageContext msgContext) throws AxisFault {
-
+ PostMethod method = null;
+
if (log.isDebugEnabled()) {
log.debug(Messages.getMessage("enter00",
- "CommonsHTTPSender::invoke"));
+ "CommonsHTTPSender::invoke"));
}
try {
- BooleanHolder useFullURL = new BooleanHolder(false);
- StringBuffer otherHeaders = new StringBuffer();
URL targetURL =
- new URL(msgContext.getStrProp(MessageContext.TRANS_URL));
- String host = targetURL.getHost();
- int port = targetURL.getPort();
- HttpConnection conn = null;
- HttpState state = new HttpState();
-
- // create socket based on the url protocol type
- if (targetURL.getProtocol().equalsIgnoreCase("https")) {
- conn = getSecureConnection(state, host, port);
- } else {
- conn = getConnection(state, host, port);
- }
- PostMethod method = new PostMethod(targetURL.getFile());
+ new URL(msgContext.getStrProp(MessageContext.TRANS_URL));
+
+ // 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(connectionManager);
- addContextInfo(method, conn, state, msgContext, targetURL);
+ HostConfiguration hostConfiguration = getHostConfiguration(httpClient,
targetURL);
+
+ method = new PostMethod(targetURL.toString());
+
+ addContextInfo(method, httpClient, msgContext, targetURL);
Message reqMessage = msgContext.getRequestMessage();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
reqMessage.writeTo(baos);
- method.setRequestBody(new String(baos.toByteArray(), "UTF-8"));
- method.execute(state, conn);
- int returnCode = method.getStatusCode();
+ method.setRequestBody(bytesAsString(baos.toByteArray()));
+ method.setUseExpectHeader(false); // workaround for
+ // outbound chunking bug
+ // in httpclient
+
+ int returnCode = httpClient.executeMethod(method);
String contentType = null;
String contentLocation = null;
String contentLength = null;
if (method.getResponseHeader(HTTPConstants.HEADER_CONTENT_TYPE)
- != null) {
+ != null) {
contentType = method.getResponseHeader(
- HTTPConstants.HEADER_CONTENT_TYPE).getValue();
+ HTTPConstants.HEADER_CONTENT_TYPE).getValue();
}
if (method.getResponseHeader(HTTPConstants.HEADER_CONTENT_LOCATION)
- != null) {
+ != null) {
contentLocation = method.getResponseHeader(
- HTTPConstants.HEADER_CONTENT_LOCATION).getValue();
+ HTTPConstants.HEADER_CONTENT_LOCATION).getValue();
}
if (method.getResponseHeader(HTTPConstants.HEADER_CONTENT_LENGTH)
- != null) {
+ != null) {
contentLength = method.getResponseHeader(
- HTTPConstants.HEADER_CONTENT_LENGTH).getValue();
+ HTTPConstants.HEADER_CONTENT_LENGTH).getValue();
}
contentType = (null == contentType)
- ? null
- : contentType.trim();
+ ? null
+ : contentType.trim();
if ((returnCode > 199) && (returnCode < 300)) {
// SOAP return is OK - so fall through
} else if ((contentType != null) && !contentType.equals("text/html")
- && ((returnCode > 499) && (returnCode < 600))) {
+ && ((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);
+ "(" + returnCode + ")"
+ + statusMessage, null,
+ null);
fault.setFaultDetailString(Messages.getMessage("return01",
- "" + returnCode, method.getResponseBodyAsString()));
+ "" + returnCode,
method.getResponseBodyAsString()));
throw fault;
}
Message outMsg = new Message(method.getResponseBodyAsStream(),
- false, contentType, contentLocation);
-
+ false, contentType, contentLocation);
+ // no need to invoke method.releaseConnection here, as that will
+ // happen automatically when the response body is read.
+ // issue: what if the stream is never closed? Are we certain
+ // that InputStream.close() always gets called?
outMsg.setMessageType(Message.RESPONSE);
msgContext.setResponseMessage(outMsg);
if (log.isDebugEnabled()) {
if (null == contentLength) {
log.debug("\n"
- + Messages.getMessage("no00", "Content-Length"));
+ + Messages.getMessage("no00", "Content-Length"));
}
log.debug("\n" + Messages.getMessage("xmlRecd00"));
log.debug("-----------------------------------------------");
@@ -198,12 +211,22 @@
log.debug(e);
throw AxisFault.makeFault(e);
}
+
if (log.isDebugEnabled()) {
log.debug(Messages.getMessage("exit00",
- "CommonsHTTPSender::invoke"));
+ "CommonsHTTPSender::invoke"));
}
}
+ private static String bytesAsString(byte[] b) {
+ try {
+ return new String(b, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ // always supported
+ throw new IllegalStateException(e.getMessage());
+ } // end of try-catch
+ }
+
/**
* cleanup the cookie value.
*
@@ -222,124 +245,66 @@
return cookie;
}
- /**
- * creates a secure HttpConnection.
- *
- * @param state HttpState
- * @param host host name/ip
- * @param port port
- *
- * @return a secure connection
- *
- * @throws Exception
- */
- private HttpConnection getSecureConnection(
- HttpState state, String host, int port) throws Exception {
- TransportClientProperties tcp =
TransportClientPropertiesFactory.create("https");
-
- if (port == -1) {
- port = 443;
- }
-
+ private HostConfiguration getHostConfiguration(HttpClient client, URL
targetURL) {
+ boolean isSecure = targetURL.getProtocol().equalsIgnoreCase("http");
+ TransportClientProperties tcp =
TransportClientPropertiesFactory.create(targetURL.getProtocol()); // http or https
+ int port = targetURL.getPort();
boolean hostInNonProxyList =
- isHostInNonProxyList(host, tcp.getNonProxyHosts());
-
- if (tcp.getProxyHost().length() == 0 || hostInNonProxyList) {
- return new HttpConnection(host, port, true);
- } else {
-
- // Default proxy port is 80, even for https
- int tunnelPort = ((tcp.getProxyPort().length() != 0)
- ? Integer.parseInt(tcp.getProxyPort())
- : 80);
-
- if (tunnelPort < 0)
- tunnelPort = 80;
-
- if (tcp.getProxyUser().length() != 0) {
- Credentials proxyCred =
- new UsernamePasswordCredentials(tcp.getProxyUser(),
- tcp.getProxyPassword());
-
- state.setProxyCredentials(null, proxyCred);
- }
-
- return new HttpConnection(tcp.getProxyHost(), tunnelPort, host, port,
true);
- }
- }
-
- /**
- * creates a non-secure connection.
- *
- * @param state HttpState
- * @param host host name/ip
- * @param port port
- *
- * @return a non-secure connection.
- *
- * @throws Exception
- */
- private HttpConnection getConnection(HttpState state, String host, int port)
- throws Exception {
- TransportClientProperties tcp =
TransportClientPropertiesFactory.create("http");
-
- boolean hostInNonProxyList =
- isHostInNonProxyList(host, tcp.getNonProxyHosts());
+ isHostInNonProxyList(targetURL.getHost(), tcp.getNonProxyHosts());
+ HostConfiguration config = new HostConfiguration();
+
if (port == -1) {
- port = 80;
+ port = 80; // even for https
}
if (tcp.getProxyHost().length() == 0 ||
tcp.getProxyPort().length() == 0 ||
hostInNonProxyList) {
- return new HttpConnection(host, port);
+ config.setHost(targetURL.getHost(), port, targetURL.getProtocol());
} else {
if (tcp.getProxyUser().length() != 0) {
Credentials proxyCred =
- new UsernamePasswordCredentials(tcp.getProxyUser(),
- tcp.getProxyPassword());
-
- state.setProxyCredentials(null, proxyCred);
+ new UsernamePasswordCredentials(tcp.getProxyUser(),
+ tcp.getProxyPassword());
+ client.getState().setProxyCredentials(null, proxyCred);
}
- return new HttpConnection(tcp.getProxyHost(),
- new Integer(tcp.getProxyPort()).intValue(),
host,
- port);
+ int proxyPort = new Integer(tcp.getProxyPort()).intValue();
+ config.setProxy(tcp.getProxyHost(), proxyPort);
}
+ return config;
}
/**
* Extracts info from message context.
*
* @param method Post method
- * @param conn a valid connection
- * @param state HttpState
+ * @param httpClient The client used for posting
* @param msgContext the message context
* @param tmpURL the url to post to.
*
* @throws Exception
*/
private void addContextInfo(
- PostMethod method, HttpConnection conn, HttpState state, MessageContext
msgContext, URL tmpURL)
- throws Exception {
+ PostMethod method, HttpClient httpClient, MessageContext msgContext, URL
tmpURL)
+ throws Exception {
// optionally set a timeout for the request
if (msgContext.getTimeout() != 0) {
- conn.setSoTimeout(msgContext.getTimeout());
+ httpClient.setTimeout(msgContext.getTimeout());
}
// Get SOAPAction, default to ""
String action = msgContext.useSOAPAction()
- ? msgContext.getSOAPActionURI()
- : "";
+ ? msgContext.getSOAPActionURI()
+ : "";
if (action == null) {
action = "";
}
Message msg = msgContext.getRequestMessage();
method.setRequestHeader(new Header(HTTPConstants.HEADER_CONTENT_TYPE,
- msg.getContentType(msgContext.getSOAPConstants())));
+
msg.getContentType(msgContext.getSOAPConstants())));
method.setRequestHeader(new Header(HTTPConstants.HEADER_SOAP_ACTION, "\"" +
action + "\""));
- method.setUseDisk(false);
String userID = msgContext.getUsername();
String passwd = msgContext.getPassword();
@@ -358,7 +323,7 @@
}
if (userID != null) {
Credentials cred = new UsernamePasswordCredentials(userID, passwd);
- state.setCredentials(null, cred);
+ httpClient.getState().setCredentials(null, cred);
// The following 3 lines should NOT be required. But Our
SimpleAxisServer fails
// during all-tests if this is missing.
@@ -370,9 +335,9 @@
// don't forget the cookies!
if (msgContext.getMaintainSession()) {
String cookie =
- (String) msgContext.getProperty(HTTPConstants.HEADER_COOKIE);
+ (String) msgContext.getProperty(HTTPConstants.HEADER_COOKIE);
String cookie2 =
- (String) msgContext.getProperty(HTTPConstants.HEADER_COOKIE2);
+ (String) msgContext.getProperty(HTTPConstants.HEADER_COOKIE2);
if (cookie != null) {
method.addRequestHeader(HTTPConstants.HEADER_COOKIE, cookie);
@@ -384,7 +349,7 @@
// process user defined headers for information.
Hashtable userHeaderTable =
- (Hashtable) msgContext.getProperty(HTTPConstants.REQUEST_HEADERS);
+ (Hashtable) msgContext.getProperty(HTTPConstants.REQUEST_HEADERS);
if (userHeaderTable != null) {
for (java.util.Iterator e = userHeaderTable.entrySet().iterator();
@@ -428,9 +393,9 @@
if (log.isDebugEnabled()) {
log.debug(Messages.getMessage("match00",
- new String[]{"HTTPSender",
- host,
- pattern}));
+ new String[]{"HTTPSender",
+ host,
+ pattern}));
}
if (match(pattern, host, false)) {
return true;
@@ -482,7 +447,7 @@
return false; // Character mismatch
}
if (!isCaseSensitive
- && (Character.toUpperCase(ch)
+ && (Character.toUpperCase(ch)
!= Character.toUpperCase(strArr[i]))) {
return false; // Character mismatch
}
@@ -495,12 +460,12 @@
// Process characters before first star
while ((ch = patArr[patIdxStart]) != '*'
- && (strIdxStart <= strIdxEnd)) {
+ && (strIdxStart <= strIdxEnd)) {
if (isCaseSensitive && (ch != strArr[strIdxStart])) {
return false; // Character mismatch
}
if (!isCaseSensitive
- && (Character.toUpperCase(ch)
+ && (Character.toUpperCase(ch)
!= Character.toUpperCase(strArr[strIdxStart]))) {
return false; // Character mismatch
}
@@ -525,7 +490,7 @@
return false; // Character mismatch
}
if (!isCaseSensitive
- && (Character.toUpperCase(ch)
+ && (Character.toUpperCase(ch)
!= Character.toUpperCase(strArr[strIdxEnd]))) {
return false; // Character mismatch
}
@@ -573,12 +538,12 @@
for (int j = 0; j < patLength; j++) {
ch = patArr[patIdxStart + j + 1];
if (isCaseSensitive
- && (ch != strArr[strIdxStart + i + j])) {
+ && (ch != strArr[strIdxStart + i + j])) {
continue strLoop;
}
if (!isCaseSensitive && (Character
- .toUpperCase(ch) != Character
- .toUpperCase(strArr[strIdxStart + i + j]))) {
+ .toUpperCase(ch) != Character
+ .toUpperCase(strArr[strIdxStart + i +
j]))) {
continue strLoop;
}
}