/* * Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. 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.wso2.carbon.mediator.cache.digest; import org.apache.axiom.om.OMNode; import org.apache.axiom.om.OMElement; import org.apache.axiom.om.OMText; import org.apache.axiom.om.OMProcessingInstruction; import org.apache.axiom.om.OMAttribute; import org.apache.axis2.context.MessageContext; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.wso2.carbon.mediator.cache.CachingException; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Collection; import java.util.Iterator; import java.util.Map; /** * This is the extended implementation of * <a href="http://www.ietf.org/rfc/rfc2803.txt">DOMHASH algorithm</a> over a HTTP request * and Payload (XML Node) for retrieving a unique key for the request * * @see org.wso2.caching.digest.DigestGenerator */ public class REQUESTHASHGenerator extends DOMHASHGenerator { /** String representing the MD5 digest algorithm */ public static final String MD5_DIGEST_ALGORITHM = "MD5"; private static final Log log = LogFactory.getLog(REQUESTHASHGenerator.class); /** * This is the implementation of the getDigest method and will implement the Extended DOMHASH * algorithm based HTTP request identifications. This will consider To address of the request, * HTTP headers and XML Payload in generating the digets. So, in effect * this will uniquely identify the HTTP request with the same To address, Headers and Payload. * * @param msgContext - MessageContext on which the XML node identifier will be generated * @return Object representing the DOMHASH value of the normalized XML node * @throws CachingException if there is an error in generating the digest key * * @see org.wso2.caching.digest.DigestGenerator * #getDigest(org.apache.axis2.context.MessageContext) */ public String getDigest(MessageContext msgContext) throws CachingException { OMNode body = msgContext.getEnvelope().getBody(); String toAddress = null; if (msgContext.getTo() != null) { toAddress = msgContext.getTo().getAddress(); } Map<String, String> headers = (Map) msgContext.getProperty(org.apache.axis2.context.MessageContext.TRANSPORT_HEADERS); if (body != null) { byte[] digest = null; if (toAddress != null) { digest = getDigest(body, toAddress, headers, MD5_DIGEST_ALGORITHM); } else { digest = getDigest(body, MD5_DIGEST_ALGORITHM); } return digest != null ? getStringRepresentation(digest) : null; } else { return null; } } /** * This is an overloaded method for the digest generation for OMNode and request * * @param node - OMNode to be subjected to the key generation * @param toAddress - Request To address to be subjected to the key generation * @param headers - Header parameters to be subjected to the key generation * @param digestAlgorithm - digest algorithm as a String * @return byte[] representing the calculated digest over the provided node * @throws CachingException if there is an error in generating the digest */ public byte[] getDigest(OMNode node, String toAddress, Map<String, String> headers, String digestAlgorithm) throws CachingException { if (node.getType() == OMNode.ELEMENT_NODE) { return getDigest((OMElement) node, toAddress, headers, digestAlgorithm); } else if (node.getType() == OMNode.TEXT_NODE) { return getDigest((OMText) node, digestAlgorithm); } else if (node.getType() == OMNode.PI_NODE) { return getDigest((OMProcessingInstruction) node, digestAlgorithm); } else { return new byte[0]; } } /** * This is an overloaded method for the digest generation for OMElement and request * * @param element - OMElement to be subjected to the key generation * @param toAddress - Request To address to be subjected to the key generation * @param headers - Header parameters to be subjected to the key generation * @param digestAlgorithm - digest algorithm as a String * @return byte[] representing the calculated digest over the provided element * @throws CachingException if there is an io error or the specified algorithm is incorrect */ public byte[] getDigest(OMElement element, String toAddress, Map<String, String> headers, String digestAlgorithm) throws CachingException { byte[] digest = new byte[0]; try { MessageDigest md = MessageDigest.getInstance(digestAlgorithm); ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos); dos.writeInt(1); dos.write(getExpandedName(element).getBytes("UnicodeBigUnmarked")); dos.write((byte) 0); dos.write((byte) 0); dos.write(toAddress.getBytes("UnicodeBigUnmarked")); /*String acceptHeader = headers.get("accept"); acceptHeader = (acceptHeader == null) ? headers.get("Accept") : acceptHeader; if (acceptHeader != null) { dos.write(acceptHeader.getBytes("UnicodeBigUnmarked")); } String contentTypeHeader = headers.get("content-type"); contentTypeHeader = (contentTypeHeader == null) ? headers.get("Content-Type") : contentTypeHeader; if (contentTypeHeader != null) { dos.write(contentTypeHeader.getBytes("UnicodeBigUnmarked")); }*/ Iterator itr = headers.keySet().iterator(); while (itr.hasNext()) { String key = (String) itr.next(); String value = headers.get(key); dos.write(getDigest(key, value, digestAlgorithm)); } Collection attrs = getAttributesWithoutNS(element); dos.writeInt(attrs.size()); itr = attrs.iterator(); while (itr.hasNext()) dos.write(getDigest((OMAttribute) itr.next(), digestAlgorithm)); OMNode node = element.getFirstOMChild(); // adjoining Texts are merged, // there is no 0-length Text, and // comment nodes are removed. int length = 0; itr = element.getChildElements(); while (itr.hasNext()) { length++; itr.next(); } dos.writeInt(length); while (node != null) { dos.write(getDigest(node, toAddress, headers, digestAlgorithm)); node = node.getNextOMSibling(); } dos.close(); md.update(baos.toByteArray()); digest = md.digest(); } catch (NoSuchAlgorithmException e) { handleException("Can not locate the algorithm " + "provided for the digest generation : " + digestAlgorithm, e); } catch (IOException e) { handleException("Error in calculating the " + "digest value for the OMElement : " + element, e); } return digest; } /** * This is an overloaded method for the digest generation for HTTP header propery * * @param key - Key of the header property subjected to the key generation * @param value - Value of the header property subjected to the key generation * @param digestAlgorithm - digest algorithm as a String * @return byte[] representing the calculated digest over the provided attribute * @throws CachingException if the specified algorithm is incorrect or the encoding * is not supported by the processor */ public byte[] getDigest(String key,String value, String digestAlgorithm) throws CachingException { byte[] digest = new byte[0]; if (!key.equalsIgnoreCase("Date") && !key.equalsIgnoreCase("User-Agent")) { try { MessageDigest md = MessageDigest.getInstance(digestAlgorithm); md.update((byte) 0); md.update((byte) 0); md.update((byte) 0); md.update((byte) 2); md.update(key.getBytes("UnicodeBigUnmarked")); if (value != null) { md.update((byte) 0); md.update((byte) 0); md.update(value.getBytes("UnicodeBigUnmarked")); } digest = md.digest(); } catch (NoSuchAlgorithmException e) { handleException("Can not locate the algorithm " + "provided for the digest generation : " + digestAlgorithm, e); } catch (UnsupportedEncodingException e) { handleException("Error in generating the digest " + "using the provided encoding : UnicodeBigUnmarked", e); } } return digest; } private void handleException(String message, Throwable cause) throws CachingException { log.debug(message, cause); throw new CachingException(message, cause); } }