0001: /*
0002: * Copyright 2001-2004 The Apache Software Foundation.
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016:
0017: /*
0018: * Modified by Nabh Information Systems, Inc. for Stringbeans Web Services
0019: * Framework.
0020: *
0021: * Modifications (c) 2005 Nabh Information Systems, Inc.
0022: */
0023: package com.nabhinc.ws.soap;
0024:
0025: import org.apache.axis.AxisFault;
0026: import org.apache.axis.AxisProperties;
0027: import org.apache.axis.Message;
0028: import org.apache.axis.MessageContext;
0029: import org.apache.axis.Constants;
0030: import org.apache.axis.components.logger.LogFactory;
0031: import org.apache.axis.components.net.BooleanHolder;
0032: import org.apache.axis.components.net.SocketFactory;
0033: import org.apache.axis.components.net.SocketFactoryFactory;
0034: import org.apache.axis.components.net.DefaultSocketFactory;
0035: import org.apache.axis.handlers.BasicHandler;
0036: import org.apache.axis.soap.SOAP12Constants;
0037: import org.apache.axis.soap.SOAPConstants;
0038: import org.apache.axis.transport.http.ChunkedInputStream;
0039: import org.apache.axis.transport.http.ChunkedOutputStream;
0040: import org.apache.axis.transport.http.HTTPConstants;
0041: import org.apache.axis.transport.http.SocketHolder;
0042: import org.apache.axis.transport.http.SocketInputStream;
0043: import org.apache.axis.utils.Messages;
0044: import org.apache.axis.utils.TeeOutputStream;
0045: import org.apache.commons.logging.Log;
0046:
0047: import com.nabhinc.util.Base64;
0048:
0049: import javax.xml.soap.MimeHeader;
0050: import javax.xml.soap.MimeHeaders;
0051: import javax.xml.soap.SOAPException;
0052: import java.io.BufferedInputStream;
0053: import java.io.BufferedOutputStream;
0054: import java.io.ByteArrayOutputStream;
0055: import java.io.IOException;
0056: import java.io.InputStream;
0057: import java.io.OutputStream;
0058: import java.net.HttpURLConnection;
0059: import java.net.Socket;
0060: import java.net.URL;
0061: import java.rmi.RemoteException;
0062: import java.util.ArrayList;
0063: import java.util.Enumeration;
0064: import java.util.Hashtable;
0065: import java.util.Iterator;
0066:
0067: /**
0068: * This is meant to be used on a SOAP Client to call a SOAP server.
0069: *
0070: * @author Doug Davis (dug@us.ibm.com)
0071: * @author Davanum Srinivas (dims@yahoo.com)
0072: */
0073: public class HTTPSender extends BasicHandler {
0074:
0075: /**
0076: * Comment for <code>serialVersionUID</code>
0077: */
0078: private static final long serialVersionUID = 3256437010649397305L;
0079:
0080: protected static Log log = LogFactory.getLog(HTTPSender.class
0081: .getName());
0082:
0083: private static final String ACCEPT_HEADERS = HTTPConstants.HEADER_ACCEPT
0084: + //Limit to the types that are meaningful to us.
0085: ": "
0086: + HTTPConstants.HEADER_ACCEPT_APPL_SOAP
0087: + ", "
0088: + HTTPConstants.HEADER_ACCEPT_APPLICATION_DIME
0089: + ", "
0090: + HTTPConstants.HEADER_ACCEPT_MULTIPART_RELATED
0091: + ", "
0092: + HTTPConstants.HEADER_ACCEPT_TEXT_ALL
0093: + "\r\n"
0094: + HTTPConstants.HEADER_USER_AGENT + //Tell who we are.
0095: ": " + Messages.getMessage("axisUserAgent") + "\r\n";
0096:
0097: private static final String CACHE_HEADERS = HTTPConstants.HEADER_CACHE_CONTROL
0098: + //Stop caching proxies from caching SOAP reqeuest.
0099: ": "
0100: + HTTPConstants.HEADER_CACHE_CONTROL_NOCACHE
0101: + "\r\n"
0102: + HTTPConstants.HEADER_PRAGMA
0103: + ": "
0104: + HTTPConstants.HEADER_CACHE_CONTROL_NOCACHE + "\r\n";
0105:
0106: private static final String CHUNKED_HEADER = HTTPConstants.HEADER_TRANSFER_ENCODING
0107: + ": "
0108: + HTTPConstants.HEADER_TRANSFER_ENCODING_CHUNKED
0109: + "\r\n";
0110:
0111: private static final String HEADER_CONTENT_TYPE_LC = HTTPConstants.HEADER_CONTENT_TYPE
0112: .toLowerCase();
0113:
0114: private static final String HEADER_LOCATION_LC = HTTPConstants.HEADER_LOCATION
0115: .toLowerCase();
0116:
0117: private static final String HEADER_CONTENT_LOCATION_LC = HTTPConstants.HEADER_CONTENT_LOCATION
0118: .toLowerCase();
0119:
0120: private static final String HEADER_CONTENT_LENGTH_LC = HTTPConstants.HEADER_CONTENT_LENGTH
0121: .toLowerCase();
0122:
0123: private static final String HEADER_TRANSFER_ENCODING_LC = HTTPConstants.HEADER_TRANSFER_ENCODING
0124: .toLowerCase();
0125:
0126: private static final int AUTH_TYPE_BASIC = 0;
0127: private static final int AUTH_TYPE_DIGEST = 1;
0128: private static final int AUTH_TYPE_FORM = 2;
0129:
0130: private static final String AUTH_TYPE_PROPERTY = "stringbeans.auth_type";
0131: @SuppressWarnings("unused")
0132: private static final String AUTH_TYPE_BASIC_VALUE = "basic";
0133: private static final String AUTH_TYPE_DIGEST_VALUE = "digest";
0134: private static final String AUTH_TYPE_FORM_VALUE = "form";
0135:
0136: private int hsAuthType = AUTH_TYPE_BASIC;
0137: /**
0138: * the url; used for error reporting
0139: */
0140: URL targetURL;
0141:
0142: public HTTPSender() {
0143: String authMethod = AxisProperties
0144: .getProperty(AUTH_TYPE_PROPERTY);
0145: if (authMethod != null) {
0146: authMethod = authMethod.toLowerCase();
0147: if (authMethod.equals(AUTH_TYPE_FORM_VALUE)) {
0148: hsAuthType = AUTH_TYPE_FORM;
0149: } else if (authMethod.equals(AUTH_TYPE_DIGEST_VALUE)) {
0150: hsAuthType = AUTH_TYPE_DIGEST;
0151: }
0152: }
0153:
0154: }
0155:
0156: /**
0157: * invoke creates a socket connection, sends the request SOAP message and then
0158: * reads the response SOAP message back from the SOAP server
0159: *
0160: * @param msgContext the messsage context
0161: *
0162: * @throws AxisFault
0163: */
0164: public void invoke(MessageContext msgContext) throws AxisFault {
0165: invokeHelper(msgContext, true);
0166: }
0167:
0168: private void invokeHelper(MessageContext msgContext,
0169: boolean tryLogin) throws AxisFault {
0170:
0171: if (log.isDebugEnabled()) {
0172: log.debug(Messages.getMessage("enter00",
0173: "HTTPSender::invoke"));
0174: }
0175: SocketHolder socketHolder = new SocketHolder(null);
0176: try {
0177: BooleanHolder useFullURL = new BooleanHolder(false);
0178: StringBuffer otherHeaders = new StringBuffer();
0179: String urlStr = msgContext
0180: .getStrProp(MessageContext.TRANS_URL);
0181: targetURL = new URL(urlStr);
0182: String host = targetURL.getHost();
0183: int port = targetURL.getPort();
0184:
0185: // Send the SOAP request to the server
0186: InputStream inp = writeToSocket(socketHolder, msgContext,
0187: targetURL, otherHeaders, host, port, msgContext
0188: .getTimeout(), useFullURL);
0189:
0190: // Read the response back from the server
0191: Hashtable headers = new Hashtable();
0192: inp = readHeadersFromSocket(socketHolder, msgContext, inp,
0193: headers);
0194: String redirect = (String) headers.get("location");
0195: if (redirect != null && hsAuthType == AUTH_TYPE_FORM) {
0196: if (!tryLogin) {
0197: throw new AxisFault("Login failed.");
0198: }
0199: // Assume that the server is redirecting us to the login page.
0200: // so close the input stream and log in.
0201: inp.close();
0202: login(redirect, urlStr, msgContext, headers, targetURL);
0203: inp.close();
0204: //socketHolder.getSocket().close();
0205: // remove former result
0206: msgContext
0207: .removeProperty(HTTPConstants.MC_HTTP_STATUS_CODE);
0208: invokeHelper(msgContext, false);
0209: return;
0210: }
0211:
0212: readFromSocket(socketHolder, msgContext, inp, headers);
0213: } catch (Exception e) {
0214: log.debug(e);
0215: try {
0216: if (socketHolder.getSocket() != null) {
0217: socketHolder.getSocket().close();
0218: }
0219: } catch (IOException ie) {
0220: // we shouldn't get here.
0221: }
0222: throw AxisFault.makeFault(e);
0223: }
0224: if (log.isDebugEnabled()) {
0225: log.debug(Messages.getMessage("exit00",
0226: "HTTPDispatchHandler::invoke"));
0227: }
0228: }
0229:
0230: /**
0231: * Creates a socket connection to the SOAP server
0232: *
0233: * @param protocol "http" for standard, "https" for ssl.
0234: * @param host host name
0235: * @param port port to connect to
0236: * @param otherHeaders buffer for storing additional headers that need to be sent
0237: * @param useFullURL flag to indicate if the complete URL has to be sent
0238: *
0239: * @throws IOException
0240: */
0241: protected void getSocket(SocketHolder sockHolder,
0242: MessageContext msgContext, String protocol, String host,
0243: int port, int timeout, StringBuffer otherHeaders,
0244: BooleanHolder useFullURL) throws Exception {
0245: Hashtable options = getOptions();
0246: if (timeout > 0) {
0247: if (options == null) {
0248: options = new Hashtable();
0249: }
0250: options.put(DefaultSocketFactory.CONNECT_TIMEOUT, Integer
0251: .toString(timeout));
0252: }
0253: SocketFactory factory = SocketFactoryFactory.getFactory(
0254: protocol, options);
0255: if (factory == null) {
0256: throw new IOException(Messages.getMessage(
0257: "noSocketFactory", protocol));
0258: }
0259: Socket sock = factory.create(host, port, otherHeaders,
0260: useFullURL);
0261: if (timeout > 0) {
0262: sock.setSoTimeout(timeout);
0263: }
0264: sockHolder.setSocket(sock);
0265: }
0266:
0267: /**
0268: * Send the soap request message to the server
0269: *
0270: * @param msgContext message context
0271: * @param tmpURL url to connect to
0272: * @param otherHeaders other headers if any
0273: * @param host host name
0274: * @param port port
0275: * @param useFullURL flag to indicate if the whole url needs to be sent
0276: *
0277: * @throws IOException
0278: */
0279: private InputStream writeToSocket(SocketHolder sockHolder,
0280: MessageContext msgContext, URL tmpURL,
0281: StringBuffer otherHeaders, String host, int port,
0282: int timeout, BooleanHolder useFullURL) throws Exception {
0283:
0284: // Get SOAPAction, default to ""
0285: String action = msgContext.useSOAPAction() ? msgContext
0286: .getSOAPActionURI() : "";
0287:
0288: if (action == null) {
0289: action = "";
0290: }
0291:
0292: if (hsAuthType == AUTH_TYPE_BASIC) {
0293: String userID = msgContext.getUsername();
0294: String passwd = msgContext.getPassword();
0295:
0296: // if UserID is not part of the context, but is in the URL, use
0297: // the one in the URL.
0298:
0299: if ((userID == null) && (tmpURL.getUserInfo() != null)) {
0300: String info = tmpURL.getUserInfo();
0301: int sep = info.indexOf(':');
0302:
0303: if ((sep >= 0) && (sep + 1 < info.length())) {
0304: userID = info.substring(0, sep);
0305: passwd = info.substring(sep + 1);
0306: } else {
0307: userID = info;
0308: }
0309: }
0310: if (userID != null) {
0311: StringBuffer tmpBuf = new StringBuffer();
0312:
0313: tmpBuf.append(userID).append(":").append(
0314: (passwd == null) ? "" : passwd);
0315: otherHeaders.append(HTTPConstants.HEADER_AUTHORIZATION)
0316: .append(": Basic ").append(
0317: Base64.encode(tmpBuf.toString()
0318: .getBytes())).append("\r\n");
0319: }
0320: }
0321:
0322: // don't forget the cookies!
0323: // mmm... cookies
0324: if (msgContext.getMaintainSession()
0325: || hsAuthType != AUTH_TYPE_BASIC) {
0326: fillHeaders(msgContext, HTTPConstants.HEADER_COOKIE,
0327: otherHeaders);
0328: fillHeaders(msgContext, HTTPConstants.HEADER_COOKIE2,
0329: otherHeaders);
0330: }
0331:
0332: StringBuffer header2 = new StringBuffer();
0333:
0334: String webMethod = null;
0335: boolean posting = true;
0336:
0337: Message reqMessage = msgContext.getRequestMessage();
0338:
0339: boolean http10 = true; //True if this is to use HTTP 1.0 / false HTTP 1.1
0340: boolean httpChunkStream = false; //Use HTTP chunking or not.
0341: boolean httpContinueExpected = false; //Under HTTP 1.1 if false you *MAY* need to wait for a 100 rc,
0342: // if true the server MUST reply with 100 continue.
0343: String httpConnection = null;
0344:
0345: String httpver = msgContext
0346: .getStrProp(MessageContext.HTTP_TRANSPORT_VERSION);
0347: if (null == httpver) {
0348: httpver = HTTPConstants.HEADER_PROTOCOL_V10;
0349: }
0350: httpver = httpver.trim();
0351: if (httpver.equals(HTTPConstants.HEADER_PROTOCOL_V11)) {
0352: http10 = false;
0353: }
0354:
0355: //process user defined headers for information.
0356: Hashtable userHeaderTable = (Hashtable) msgContext
0357: .getProperty(HTTPConstants.REQUEST_HEADERS);
0358:
0359: if (userHeaderTable != null) {
0360: if (null == otherHeaders) {
0361: otherHeaders = new StringBuffer(1024);
0362: }
0363:
0364: for (java.util.Iterator e = userHeaderTable.entrySet()
0365: .iterator(); e.hasNext();) {
0366:
0367: java.util.Map.Entry me = (java.util.Map.Entry) e.next();
0368: Object keyObj = me.getKey();
0369: if (null == keyObj)
0370: continue;
0371: String key = keyObj.toString().trim();
0372:
0373: if (key
0374: .equalsIgnoreCase(HTTPConstants.HEADER_TRANSFER_ENCODING)) {
0375: if (!http10) {
0376: String val = me.getValue().toString();
0377: if (null != val
0378: && val
0379: .trim()
0380: .equalsIgnoreCase(
0381: HTTPConstants.HEADER_TRANSFER_ENCODING_CHUNKED))
0382: httpChunkStream = true;
0383: }
0384: } else if (key
0385: .equalsIgnoreCase(HTTPConstants.HEADER_CONNECTION)) {
0386: if (!http10) {
0387: String val = me.getValue().toString();
0388: if (val.trim().equalsIgnoreCase(
0389: HTTPConstants.HEADER_CONNECTION_CLOSE))
0390: httpConnection = HTTPConstants.HEADER_CONNECTION_CLOSE;
0391: }
0392: //HTTP 1.0 will always close.
0393: //HTTP 1.1 will use persistent. //no need to specify
0394: } else {
0395: if (!http10
0396: && key
0397: .equalsIgnoreCase(HTTPConstants.HEADER_EXPECT)) {
0398: String val = me.getValue().toString();
0399: if (null != val
0400: && val
0401: .trim()
0402: .equalsIgnoreCase(
0403: HTTPConstants.HEADER_EXPECT_100_Continue))
0404: httpContinueExpected = true;
0405: }
0406:
0407: otherHeaders.append(key).append(": ").append(
0408: me.getValue()).append("\r\n");
0409: }
0410: }
0411: }
0412:
0413: if (!http10) {
0414: //Force close for now.
0415: //TODO HTTP/1.1
0416: httpConnection = HTTPConstants.HEADER_CONNECTION_CLOSE;
0417: }
0418:
0419: header2.append(" ");
0420: header2.append(
0421: http10 ? HTTPConstants.HEADER_PROTOCOL_10
0422: : HTTPConstants.HEADER_PROTOCOL_11).append(
0423: "\r\n");
0424: MimeHeaders mimeHeaders = reqMessage.getMimeHeaders();
0425:
0426: if (posting) {
0427: String contentType;
0428: final String[] header = mimeHeaders
0429: .getHeader(HTTPConstants.HEADER_CONTENT_TYPE);
0430: if (header != null && header.length > 0) {
0431: contentType = mimeHeaders
0432: .getHeader(HTTPConstants.HEADER_CONTENT_TYPE)[0];
0433: } else {
0434: contentType = reqMessage.getContentType(msgContext
0435: .getSOAPConstants());
0436: }
0437:
0438: //fix for AXIS-2027
0439: if (contentType == null || contentType.equals("")) {
0440: throw new Exception(Messages
0441: .getMessage("missingContentType"));
0442: }
0443: header2.append(HTTPConstants.HEADER_CONTENT_TYPE).append(
0444: ": ").append(contentType).append("\r\n");
0445: }
0446:
0447: header2.append(ACCEPT_HEADERS)
0448: .append(HTTPConstants.HEADER_HOST) //used for virtual connections
0449: .append(": ").append(host).append(
0450: (port == -1) ? ("") : (":" + port)).append(
0451: "\r\n").append(CACHE_HEADERS).append(
0452: HTTPConstants.HEADER_SOAP_ACTION) //The SOAP action.
0453: .append(": \"").append(action).append("\"\r\n");
0454:
0455: if (posting) {
0456: if (!httpChunkStream) {
0457: //Content length MUST be sent on HTTP 1.0 requests.
0458: header2.append(HTTPConstants.HEADER_CONTENT_LENGTH)
0459: .append(": ").append(
0460: reqMessage.getContentLength()).append(
0461: "\r\n");
0462: } else {
0463: //Do http chunking.
0464: header2.append(CHUNKED_HEADER);
0465: }
0466: }
0467:
0468: // Transfer MIME headers of SOAPMessage to HTTP headers.
0469: if (mimeHeaders != null) {
0470: for (Iterator i = mimeHeaders.getAllHeaders(); i.hasNext();) {
0471: MimeHeader mimeHeader = (MimeHeader) i.next();
0472: String headerName = mimeHeader.getName();
0473: if (headerName
0474: .equals(HTTPConstants.HEADER_CONTENT_TYPE)
0475: || headerName
0476: .equals(HTTPConstants.HEADER_SOAP_ACTION)) {
0477: continue;
0478: }
0479: header2.append(mimeHeader.getName()).append(": ")
0480: .append(mimeHeader.getValue()).append("\r\n");
0481: }
0482: }
0483:
0484: if (null != httpConnection) {
0485: header2.append(HTTPConstants.HEADER_CONNECTION);
0486: header2.append(": ");
0487: header2.append(httpConnection);
0488: header2.append("\r\n");
0489: }
0490:
0491: getSocket(sockHolder, msgContext, targetURL.getProtocol(),
0492: host, port, timeout, otherHeaders, useFullURL);
0493:
0494: if (null != otherHeaders) {
0495: //Add other headers to the end.
0496: //for pre java1.4 support, we have to turn the string buffer argument into
0497: //a string before appending.
0498: header2.append(otherHeaders.toString());
0499: }
0500:
0501: header2.append("\r\n"); //The empty line to start the BODY.
0502:
0503: StringBuffer header = new StringBuffer();
0504:
0505: // If we're SOAP 1.2, allow the web method to be set from the
0506: // MessageContext.
0507: if (msgContext.getSOAPConstants() == SOAPConstants.SOAP12_CONSTANTS) {
0508: webMethod = msgContext
0509: .getStrProp(SOAP12Constants.PROP_WEBMETHOD);
0510: }
0511: if (webMethod == null) {
0512: webMethod = HTTPConstants.HEADER_POST;
0513: } else {
0514: posting = webMethod.equals(HTTPConstants.HEADER_POST);
0515: }
0516:
0517: header.append(webMethod).append(" ");
0518: if (useFullURL.value) {
0519: header.append(tmpURL.toExternalForm());
0520: } else {
0521: header.append((((tmpURL.getFile() == null) || tmpURL
0522: .getFile().equals("")) ? "/" : tmpURL.getFile()));
0523: }
0524: header.append(header2.toString());
0525:
0526: OutputStream out = sockHolder.getSocket().getOutputStream();
0527:
0528: if (!posting) {
0529: out.write(header.toString().getBytes(
0530: HTTPConstants.HEADER_DEFAULT_CHAR_ENCODING));
0531: out.flush();
0532: return null;
0533: }
0534:
0535: InputStream inp = null;
0536:
0537: if (httpChunkStream || httpContinueExpected) {
0538: out.write(header.toString().getBytes(
0539: HTTPConstants.HEADER_DEFAULT_CHAR_ENCODING));
0540: }
0541:
0542: if (httpContinueExpected) { //We need to get a reply from the server as to whether
0543: // it wants us send anything more.
0544: out.flush();
0545: Hashtable cheaders = new Hashtable();
0546: inp = readHeadersFromSocket(sockHolder, msgContext, null,
0547: cheaders);
0548: int returnCode = -1;
0549: Integer Irc = (Integer) msgContext
0550: .getProperty(HTTPConstants.MC_HTTP_STATUS_CODE);
0551: if (null != Irc) {
0552: returnCode = Irc.intValue();
0553: }
0554: if (100 == returnCode) { // got 100 we may continue.
0555: //Need todo a little msgContext house keeping....
0556: msgContext
0557: .removeProperty(HTTPConstants.MC_HTTP_STATUS_CODE);
0558: msgContext
0559: .removeProperty(HTTPConstants.MC_HTTP_STATUS_MESSAGE);
0560: } else { //If no 100 Continue then we must not send anything!
0561: String statusMessage = (String) msgContext
0562: .getProperty(HTTPConstants.MC_HTTP_STATUS_MESSAGE);
0563:
0564: AxisFault fault = new AxisFault("HTTP", "("
0565: + returnCode + ")" + statusMessage, null, null);
0566:
0567: fault.setFaultDetailString(Messages.getMessage(
0568: "return01", "" + returnCode, ""));
0569: throw fault;
0570: }
0571: }
0572: ByteArrayOutputStream baos = null;
0573: if (log.isDebugEnabled()) {
0574: log.debug(Messages.getMessage("xmlSent00"));
0575: log
0576: .debug("---------------------------------------------------");
0577: baos = new ByteArrayOutputStream();
0578: }
0579: if (httpChunkStream) {
0580: ChunkedOutputStream chunkedOutputStream = new ChunkedOutputStream(
0581: out);
0582: out = new BufferedOutputStream(chunkedOutputStream,
0583: Constants.HTTP_TXR_BUFFER_SIZE);
0584: try {
0585: if (baos != null) {
0586: out = new TeeOutputStream(out, baos);
0587: }
0588: reqMessage.writeTo(out);
0589: } catch (SOAPException e) {
0590: log.error(Messages.getMessage("exception00"), e);
0591: }
0592: out.flush();
0593: chunkedOutputStream.eos();
0594: } else {
0595: out = new BufferedOutputStream(out,
0596: Constants.HTTP_TXR_BUFFER_SIZE);
0597: try {
0598: if (!httpContinueExpected) {
0599: out
0600: .write(header
0601: .toString()
0602: .getBytes(
0603: HTTPConstants.HEADER_DEFAULT_CHAR_ENCODING));
0604: }
0605: if (baos != null) {
0606: out = new TeeOutputStream(out, baos);
0607: }
0608: reqMessage.writeTo(out);
0609: } catch (SOAPException e) {
0610: log.error(Messages.getMessage("exception00"), e);
0611: }
0612: // Flush ONLY once.
0613: out.flush();
0614: }
0615: if (log.isDebugEnabled()) {
0616: log.debug(header + new String(baos.toByteArray()));
0617: }
0618:
0619: return inp;
0620: }
0621:
0622: /**
0623: * Get cookies from message context and add it to the headers
0624: * @param msgContext
0625: * @param header
0626: * @param otherHeaders
0627: */
0628: private void fillHeaders(MessageContext msgContext, String header,
0629: StringBuffer otherHeaders) {
0630: Object ck1 = msgContext.getProperty(header);
0631: if (ck1 != null) {
0632: if (ck1 instanceof String[]) {
0633: String[] cookies = (String[]) ck1;
0634: for (int i = 0; i < cookies.length; i++) {
0635: addCookie(otherHeaders, header, cookies[i]);
0636: }
0637: } else {
0638: addCookie(otherHeaders, header, (String) ck1);
0639: }
0640: }
0641: }
0642:
0643: /**
0644: * add cookie to headers
0645: * @param otherHeaders
0646: * @param header
0647: * @param cookie
0648: */
0649: private void addCookie(StringBuffer otherHeaders, String header,
0650: String cookie) {
0651: otherHeaders.append(header).append(": ").append(cookie).append(
0652: "\r\n");
0653: }
0654:
0655: private InputStream readHeadersFromSocket(SocketHolder sockHolder,
0656: MessageContext msgContext, InputStream inp,
0657: Hashtable headers) throws IOException {
0658: byte b = 0;
0659: int len = 0;
0660: int colonIndex = -1;
0661: String name, value;
0662: int returnCode = 0;
0663: if (null == inp) {
0664: inp = new BufferedInputStream(sockHolder.getSocket()
0665: .getInputStream());
0666: }
0667:
0668: if (headers == null) {
0669: headers = new Hashtable();
0670: }
0671:
0672: // Should help performance. Temporary fix only till its all stream oriented.
0673: // Need to add logic for getting the version # and the return code
0674: // but that's for tomorrow!
0675:
0676: /* Logic to read HTTP response headers */
0677: boolean readTooMuch = false;
0678:
0679: for (ByteArrayOutputStream buf = new ByteArrayOutputStream(4097);;) {
0680: if (!readTooMuch) {
0681: b = (byte) inp.read();
0682: }
0683: if (b == -1) {
0684: break;
0685: }
0686: readTooMuch = false;
0687: if ((b != '\r') && (b != '\n')) {
0688: if ((b == ':') && (colonIndex == -1)) {
0689: colonIndex = len;
0690: }
0691: len++;
0692: buf.write(b);
0693: } else if (b == '\r') {
0694: continue;
0695: } else { // b== '\n'
0696: if (len == 0) {
0697: break;
0698: }
0699: b = (byte) inp.read();
0700: readTooMuch = true;
0701:
0702: // A space or tab at the begining of a line means the header continues.
0703: if ((b == ' ') || (b == '\t')) {
0704: continue;
0705: }
0706: buf.close();
0707: byte[] hdata = buf.toByteArray();
0708: buf.reset();
0709: if (colonIndex != -1) {
0710: name = new String(hdata, 0, colonIndex,
0711: HTTPConstants.HEADER_DEFAULT_CHAR_ENCODING);
0712: value = new String(hdata, colonIndex + 1, len - 1
0713: - colonIndex,
0714: HTTPConstants.HEADER_DEFAULT_CHAR_ENCODING);
0715: colonIndex = -1;
0716: } else {
0717:
0718: name = new String(hdata, 0, len,
0719: HTTPConstants.HEADER_DEFAULT_CHAR_ENCODING);
0720: value = "";
0721: }
0722: if (log.isDebugEnabled()) {
0723: log.debug(name + value);
0724: }
0725: if (msgContext
0726: .getProperty(HTTPConstants.MC_HTTP_STATUS_CODE) == null) {
0727:
0728: // Reader status code
0729: int start = name.indexOf(' ') + 1;
0730: String tmp = name.substring(start).trim();
0731: int end = tmp.indexOf(' ');
0732:
0733: if (end != -1) {
0734: tmp = tmp.substring(0, end);
0735: }
0736: returnCode = Integer.parseInt(tmp);
0737: msgContext.setProperty(
0738: HTTPConstants.MC_HTTP_STATUS_CODE,
0739: new Integer(returnCode));
0740: msgContext.setProperty(
0741: HTTPConstants.MC_HTTP_STATUS_MESSAGE, name
0742: .substring(start + end + 1));
0743: } else {
0744: // if we are maintaining session state,
0745: // handle cookies (if any)
0746: if (msgContext.getMaintainSession()) {
0747: final String nameLowerCase = name.toLowerCase();
0748: if (nameLowerCase
0749: .equalsIgnoreCase(HTTPConstants.HEADER_SET_COOKIE)) {
0750: handleCookie(HTTPConstants.HEADER_COOKIE,
0751: null, value, msgContext);
0752: } else if (nameLowerCase
0753: .equalsIgnoreCase(HTTPConstants.HEADER_SET_COOKIE2)) {
0754: handleCookie(HTTPConstants.HEADER_COOKIE2,
0755: null, value, msgContext);
0756: } else {
0757: headers.put(name.toLowerCase(), value);
0758: }
0759: } else {
0760: headers.put(name.toLowerCase(), value);
0761: }
0762: }
0763: len = 0;
0764: }
0765: }
0766:
0767: return inp;
0768: }
0769:
0770: /**
0771: * Reads the SOAP response back from the server
0772: *
0773: * @param msgContext message context
0774: *
0775: * @throws IOException
0776: */
0777: private InputStream readFromSocket(SocketHolder socketHolder,
0778: MessageContext msgContext, InputStream inp,
0779: Hashtable headers) throws IOException {
0780: Message outMsg = null;
0781: byte b;
0782:
0783: Integer rc = (Integer) msgContext
0784: .getProperty(HTTPConstants.MC_HTTP_STATUS_CODE);
0785: int returnCode = 0;
0786: if (rc != null) {
0787: returnCode = rc.intValue();
0788: } else {
0789: // No return code?? Should have one by now.
0790: }
0791:
0792: /* All HTTP headers have been read. */
0793: String contentType = (String) headers
0794: .get(HEADER_CONTENT_TYPE_LC);
0795:
0796: contentType = (null == contentType) ? null : contentType.trim();
0797:
0798: String location = (String) headers.get(HEADER_LOCATION_LC);
0799:
0800: location = (null == location) ? null : location.trim();
0801:
0802: if ((returnCode > 199) && (returnCode < 300)) {
0803: if (returnCode == 202) {
0804: return inp;
0805: }
0806: // SOAP return is OK - so fall through
0807: } else if (msgContext.getSOAPConstants() == SOAPConstants.SOAP12_CONSTANTS) {
0808: // For now, if we're SOAP 1.2, fall through, since the range of
0809: // valid result codes is much greater
0810: } else if ((contentType != null)
0811: && !contentType.startsWith("text/html")
0812: && ((returnCode > 499) && (returnCode < 600))) {
0813: // SOAP Fault should be in here - so fall through
0814: } else if ((location != null)
0815: && ((returnCode == 302) || (returnCode == 307))) {
0816: // Temporary Redirect (HTTP: 302/307)
0817: // close old connection
0818: inp.close();
0819: socketHolder.getSocket().close();
0820: // remove former result and set new target url
0821: msgContext
0822: .removeProperty(HTTPConstants.MC_HTTP_STATUS_CODE);
0823: msgContext.setProperty(MessageContext.TRANS_URL, location);
0824: // next try
0825: invoke(msgContext);
0826: return inp;
0827: } else if (returnCode == 100) {
0828: msgContext
0829: .removeProperty(HTTPConstants.MC_HTTP_STATUS_CODE);
0830: msgContext
0831: .removeProperty(HTTPConstants.MC_HTTP_STATUS_MESSAGE);
0832: readHeadersFromSocket(socketHolder, msgContext, inp,
0833: headers);
0834: return readFromSocket(socketHolder, msgContext, inp,
0835: headers);
0836: } else {
0837: // Unknown return code - so wrap up the content into a
0838: // SOAP Fault.
0839: ByteArrayOutputStream buf = new ByteArrayOutputStream(4097);
0840:
0841: while (-1 != (b = (byte) inp.read())) {
0842: buf.write(b);
0843: }
0844: String statusMessage = msgContext
0845: .getStrProp(HTTPConstants.MC_HTTP_STATUS_MESSAGE);
0846: AxisFault fault = new AxisFault("HTTP", "(" + returnCode
0847: + ")" + statusMessage, null, null);
0848:
0849: fault.setFaultDetailString(Messages.getMessage("return01",
0850: "" + returnCode, buf.toString()));
0851: fault.addFaultDetail(
0852: Constants.QNAME_FAULTDETAIL_HTTPERRORCODE, Integer
0853: .toString(returnCode));
0854: throw fault;
0855: }
0856:
0857: String contentLocation = (String) headers
0858: .get(HEADER_CONTENT_LOCATION_LC);
0859:
0860: contentLocation = (null == contentLocation) ? null
0861: : contentLocation.trim();
0862:
0863: String contentLength = (String) headers
0864: .get(HEADER_CONTENT_LENGTH_LC);
0865:
0866: contentLength = (null == contentLength) ? null : contentLength
0867: .trim();
0868:
0869: String transferEncoding = (String) headers
0870: .get(HEADER_TRANSFER_ENCODING_LC);
0871:
0872: if (null != transferEncoding) {
0873: transferEncoding = transferEncoding.trim().toLowerCase();
0874: if (transferEncoding
0875: .equals(HTTPConstants.HEADER_TRANSFER_ENCODING_CHUNKED)) {
0876: inp = new ChunkedInputStream(inp);
0877: }
0878: }
0879:
0880: outMsg = new Message(new SocketInputStream(inp, socketHolder
0881: .getSocket()), false, contentType, contentLocation);
0882: // Transfer HTTP headers of HTTP message to MIME headers of SOAP message
0883: MimeHeaders mimeHeaders = outMsg.getMimeHeaders();
0884: for (Enumeration e = headers.keys(); e.hasMoreElements();) {
0885: String key = (String) e.nextElement();
0886: mimeHeaders.addHeader(key, ((String) headers.get(key))
0887: .trim());
0888: }
0889: outMsg.setMessageType(Message.RESPONSE);
0890: msgContext.setResponseMessage(outMsg);
0891: if (log.isDebugEnabled()) {
0892: if (null == contentLength) {
0893: log
0894: .debug("\n"
0895: + Messages.getMessage("no00",
0896: "Content-Length"));
0897: }
0898: log.debug("\n" + Messages.getMessage("xmlRecd00"));
0899: log
0900: .debug("-----------------------------------------------");
0901: log.debug(outMsg.getSOAPEnvelope().toString());
0902: }
0903:
0904: return inp;
0905: }
0906:
0907: /**
0908: * little helper function for cookies. fills up the message context with
0909: * a string or an array of strings (if there are more than one Set-Cookie)
0910: *
0911: * @param cookieName
0912: * @param setCookieName
0913: * @param cookie
0914: * @param msgContext
0915: */
0916: public void handleCookie(String cookieName, String setCookieName,
0917: String cookie, MessageContext msgContext) {
0918:
0919: cookie = cleanupCookie(cookie);
0920: int keyIndex = cookie.indexOf("=");
0921: String key = (keyIndex != -1) ? cookie.substring(0, keyIndex)
0922: : cookie;
0923:
0924: ArrayList cookies = new ArrayList();
0925: Object oldCookies = msgContext.getProperty(cookieName);
0926: boolean alreadyExist = false;
0927: if (oldCookies != null) {
0928: if (oldCookies instanceof String[]) {
0929: String[] oldCookiesArray = (String[]) oldCookies;
0930: for (int i = 0; i < oldCookiesArray.length; i++) {
0931: String anOldCookie = oldCookiesArray[i];
0932: if (key != null && anOldCookie.indexOf(key) == 0) { // same cookie key
0933: anOldCookie = cookie; // update to new one
0934: alreadyExist = true;
0935: }
0936: cookies.add(anOldCookie);
0937: }
0938: } else {
0939: String oldCookie = (String) oldCookies;
0940: if (key != null && oldCookie.indexOf(key) == 0) { // same cookie key
0941: oldCookie = cookie; // update to new one
0942: alreadyExist = true;
0943: }
0944: cookies.add(oldCookie);
0945: }
0946: }
0947:
0948: if (!alreadyExist) {
0949: cookies.add(cookie);
0950: }
0951:
0952: if (cookies.size() == 1) {
0953: msgContext.setProperty(cookieName, cookies.get(0));
0954: } else if (cookies.size() > 1) {
0955: msgContext.setProperty(cookieName, cookies
0956: .toArray(new String[cookies.size()]));
0957: }
0958: }
0959:
0960: /**
0961: * cleanup the cookie value.
0962: *
0963: * @param cookie initial cookie value
0964: *
0965: * @return a cleaned up cookie value.
0966: */
0967: private String cleanupCookie(String cookie) {
0968: cookie = cookie.trim();
0969: // chop after first ; a la Apache SOAP (see HTTPUtils.java there)
0970: int index = cookie.indexOf(';');
0971: if (index != -1) {
0972: cookie = cookie.substring(0, index);
0973: }
0974: return cookie;
0975: }
0976:
0977: private void login(String loginURL, String origPage,
0978: MessageContext msgContext, Hashtable headers, URL tmpURL)
0979: throws RemoteException {
0980:
0981: /*
0982: String cookieStr = (String) headers.get("set-cookie");
0983: String cookieStr2 = (String) headers.get("set-cookie2");
0984: // Extract the right tokens from the cookie and access the
0985: // login form.
0986: String filteredCookieStr = extractRightCookies(cookieStr);
0987: msgContext.setProperty(HTTPConstants.HEADER_COOKIE, filteredCookieStr);
0988:
0989: String filteredCookieStr2 = extractRightCookies(cookieStr2);
0990: msgContext.setProperty(HTTPConstants.HEADER_COOKIE2, filteredCookieStr2);
0991: */
0992: // Post login form data.
0993: String userID = msgContext.getUsername();
0994: String passwd = msgContext.getPassword();
0995:
0996: // if UserID is not part of the context, but is in the URL, use
0997: // the one in the URL.
0998: if ((userID == null) && (tmpURL.getUserInfo() != null)) {
0999: String info = tmpURL.getUserInfo();
1000: int sep = info.indexOf(':');
1001:
1002: if ((sep >= 0) && (sep + 1 < info.length())) {
1003: userID = info.substring(0, sep);
1004: passwd = info.substring(sep + 1);
1005: } else {
1006: userID = info;
1007: passwd = "";
1008: }
1009: }
1010:
1011: String type = "application/x-www-form-urlencoded";
1012: String post = "j_username=" + userID + "&j_password=" + passwd
1013: + "&j_security_check=Submit+Query";
1014:
1015: HttpURLConnection urlConn = null;
1016: try {
1017: URL url = new URL(loginURL);
1018: urlConn = (HttpURLConnection) url.openConnection();
1019: HttpURLConnection.setFollowRedirects(false);
1020: Object ck1 = msgContext.getProperty("Cookie");
1021: if (ck1 != null) {
1022: if (ck1 instanceof String[]) {
1023: String[] cookies = (String[]) ck1;
1024: for (int i = 0; i < cookies.length; i++) {
1025: urlConn
1026: .setRequestProperty(
1027: HTTPConstants.HEADER_COOKIE,
1028: cookies[i]);
1029: }
1030: } else {
1031: urlConn.setRequestProperty(
1032: HTTPConstants.HEADER_COOKIE, (String) ck1);
1033: }
1034: }
1035:
1036: urlConn.setRequestMethod("POST");
1037: urlConn.setDoOutput(true);
1038: urlConn.setDoInput(true);
1039: OutputStream out = urlConn.getOutputStream();
1040: out.write(post.getBytes());
1041: out.flush();
1042: out.close();
1043: } catch (Exception ex) {
1044: throw new RemoteException("Failed to submit login form: "
1045: + ex.getClass().getName());
1046: }
1047:
1048: // String redirect = null;
1049: try {
1050: InputStream in = urlConn.getInputStream();
1051: // redirect = urlConn.getHeaderField("Location");
1052: in.close();
1053: } catch (Exception ex) {
1054: throw new RemoteException("Failed to read response: "
1055: + ex.getClass().getName());
1056: }
1057:
1058: /*
1059: * TODO: Introduce error page check
1060: */
1061: /*
1062: String errorPage = null;
1063: if (errorPage == null) {
1064: errorPage = loginURL.substring(0,
1065: loginURL.lastIndexOf('/')) + "/login/error.jsp";
1066: }
1067:
1068: if (errorPage.equals(redirect)) {
1069: throw new RemoteException("Login failed.");
1070: }
1071:
1072: */
1073:
1074: }
1075:
1076: /**
1077: * This method assumes that cStr argument is a string containing HTTP cookies.
1078: * It removes "path" cookie from the string and returns it.
1079: */
1080: private String extractRightCookies(String cStr) {
1081: if (cStr == null)
1082: return null;
1083: int index = cStr.indexOf(";");
1084: if (index > -1) {
1085: return cStr.substring(0, index);
1086: } else {
1087: return cStr;
1088: }
1089: }
1090: }
|