Package nexj.core.rpc.http

Source Code of nexj.core.rpc.http.HTTPUtil

// Copyright 2010 NexJ Systems Inc. This software is licensed under the terms of the Eclipse Public License 1.0
package nexj.core.rpc.http;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Set;
import java.util.zip.CRC32;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import nexj.core.meta.Metadata;
import nexj.core.rpc.RPCSizeException;
import nexj.core.util.Binary;
import nexj.core.util.HTTP;
import nexj.core.util.IOUtil;
import nexj.core.util.LimitIOException;
import nexj.core.util.LimitInputStream;
import nexj.core.util.Lookup;
import nexj.core.util.LookupHashMap;
import nexj.core.util.MIMEHeader;
import nexj.core.util.MultipartDataException;
import nexj.core.util.MultipartInputStream;
import nexj.core.util.ObjUtil;
import nexj.core.util.StringUtil;
import nexj.core.util.SysUtil;

/**
* HTTP server utility class.
*/
public class HTTPUtil
{
   // constants

   /**
    * The name of the HttpServletRequest attribute that contains the X.509 certificate
    * chain produced by the client to authenticate itself.
    */
   public final static String CLIENT_CERTIFICATE_ATTRIBUTE_NAME = "javax.servlet.request.X509Certificate";

   // operations

   /**
    * @param sAnonymousAddress Anonymous Address.
    * @param sContextPath Request context path.
    * @param sScheme Request address scheme.
    * @param nPort Request port.
    * @param sServerName Request server name.
    * @param bSecure True if secure request.
    * @return The anonymous server root.
    */
   public static String computeAnonymousRoot(String sAnonymousAddress, String sContextPath,
      String sScheme, int nPort, String sServerName, boolean bSecure)
   {
      String sAnonymousRoot = null;
     
      if (sAnonymousAddress != null)
      {
         sAnonymousRoot = sAnonymousAddress + sContextPath;
      }
      else
      {
         if (bSecure)
         {
            sScheme = "http";
            nPort = computePort(nPort, false);
         }

         try
         {
            sAnonymousRoot = new URI(sScheme, null, sServerName, nPort, sContextPath, null, null).toString();
         }
         catch (URISyntaxException e)
         {
            sAnonymousRoot = "";
         }
      }

      return sAnonymousRoot;
   }


   /**
    * Computes the server root.
    *
    * @param sAddress     Address, can be null.
    * @param sContextPath Request context path.
    * @param sScheme      Request address scheme.
    * @param nPort        Request port.
    * @param sServerName  Request server name.
    * @param bSecure      True if secure request.
    *
    * @return A String URI of the form "scheme://host.domain:port/context/path".
    */
   public static String computeRoot(String sAddress, String sContextPath, String sScheme,
      int nPort, String sServerName, boolean bSecure)
   {
      String sRoot = null;

      if (sAddress != null)
      {
         sRoot = sAddress + sContextPath;
      }
      else
      {
         if (bSecure)
         {
            sScheme = "https";
            nPort = computePort(nPort, true);
         }

         try
         {
            sRoot = new URI(sScheme, null, sServerName, nPort, sContextPath, null, null).toString();
         }
         catch (URISyntaxException e)
         {
            sRoot = "";
         }
      }

      return sRoot;
   }

   /**
    * Computes an HTTPS port based on an HTTP port.
    * @param nHTTPPort The HTTP port.
    * @return The HTTPS port.
    */
   public static int computePort(int nPort, boolean bSecure)
   {
      return (nPort / 1000) * 1000 + ((bSecure) ? 443 : 80);
   }

   /**
    * Gets the host URI from a URI. E.g. converts
    * "http://example.com/test" to "http://example.com".
    *
    * @param sURI The URI. May be null.
    * @return The host URI; null if there was no host specified.
    */
   public static String getHostURI(String sURI)
   {
      if (sURI == null || sURI.length() == 0 || sURI.charAt(0) == '/')
      {
         return null;
      }

      try
      {
         URI fullURI = new URI(sURI);
         URI hostURI = new URI(fullURI.getScheme(), null, fullURI.getHost(), fullURI.getPort(), null, null, null);

         return StringUtil.trimToNull(hostURI.toString());
      }
      catch (URISyntaxException ex)
      {
         return null;
      }
   }

   /**
    * Gets the name of the resource requested by a given URI. This is determined
    * by the last segment found in the URI before any query parameters.
    *
    * @param sURI
    * @return Name of the resource requested.
    */
   public static String getResourceName(String sURI)
   {
      if (sURI == null)
      {
         return null;
      }
     
      String sResourceName = sURI;

      int nQueryParamIdx = sURI.lastIndexOf('?');

      if (nQueryParamIdx > -1)
      {
         sResourceName = sResourceName.substring(0, nQueryParamIdx);
      }

      int nLastSlashIdx = sResourceName.lastIndexOf('/');

      if (nLastSlashIdx > -1)
      {
         sResourceName = sResourceName.substring(nLastSlashIdx + 1);
      }

      return sResourceName;
   }

   /**
    * Computes the relative path (without trailing slash) needed to obtain the
    * context root given a servlet path.
    *
    * @param sServletPath
    * @return A path prefix that can be used to obtain the context root. The
    *         path will be in the form of '.' or '../..'
    */
   public static String computeRelativeRoot(String sServletPath)
   {
      int nLastIdx = (sServletPath == null) ? -1 : sServletPath.lastIndexOf('/');

      // Case where path is immediately off the context root
      if (nLastIdx <= 0)
      {
         return ".";
      }

      // Case where path is several levels deep
      StringBuilder sbPath = new StringBuilder("..");
      nLastIdx = sServletPath.lastIndexOf('/', nLastIdx - 1);

      while (nLastIdx > -1)
      {
         if (nLastIdx > 0)
         {
            sbPath.append("/..");
         }
         else
         {
            break;
         }

         nLastIdx = sServletPath.lastIndexOf('/', nLastIdx - 1);
      }

      return sbPath.toString();
   }

   /**
    * Sets the response caching headers.
    * @param response The HTTP response.
    * @param bCached True to cache.
    */
   public static void setCached(HttpServletResponse response, boolean bCached)
   {
      response.setHeader(HTTP.HEADER_CACHE_CONTROL, (bCached) ? "public" : "no-cache");

      if (response.containsHeader(HTTP.HEADER_PRAGMA))
      {
         response.setHeader(HTTP.HEADER_PRAGMA, (bCached) ? "public" : "no-cache");
      }
   }

   /**
    * Verifies that a given trusted certificate authorizes a client (that presented
    * the given client certificate chain).
    *
    * @param trustedCert The trusted certificate to look for.
    * @param clientChain The certificate chain presented by the client.
    * @return True if trustedCert is in clientChain; false otherwise.
    */
   public static boolean isCertificateMatched(Certificate trustedCert, X509Certificate[] clientChain)
   {
      for (int nCertificate = 0; nCertificate < clientChain.length; nCertificate++)
      {
         X509Certificate clientCert = clientChain[nCertificate];

         if (clientCert.equals(trustedCert))
         {
            return true;
         }
      }

      return false;
   }

   /**
    * Inspects an HTTP request to see if client certificate authentication is
    * being used.
    *
    * @param request The request to inspect.
    * @return True if the request was made with client certificate authentication;
    * false otherwise.
    */
   public static boolean isUsingClientCertificateAuthentication(HttpServletRequest request)
   {
      /*
       * Tomcat version 5.5.20 bug 40524: Tomcat returns "CLIENT-CERT" instead
       * of CLIENT_CERT_AUTH (which is "CLIENT_CERT").
       * http://issues.apache.org/bugzilla/show_bug.cgi?id=40524
       */
      return HttpServletRequest.CLIENT_CERT_AUTH.equals(request.getAuthType()) ||
         "CLIENT-CERT".equals(request.getAuthType());
   }

   /**
    * Determines if the HTTP request is being made on the anonymous HTTP context.
    *
    * @param request The request to inspect.
    * @param metadata The metadata.
    * @return True if the HTTP request is being made on the anonymous HTTP context.
    */
   public static boolean isAnonymousRequest(HttpServletRequest request, Metadata metadata)
   {
      String sPath = request.getContextPath();

      return (sPath != null && sPath.equals(metadata.getHTTPAnonymousContextRoot()));
   }

   /**
    * Parses a string containing parameters and their values into a parameter map.
    *
    * @param sQuery The string containing the parameters and their values.
    * @param ignoreSet The set of parameters to ignore.
    * @return A map containing the parameters and their values.
    */
   public static Lookup parseQuery(String sQuery, Set ignoreSet)
   {
      Lookup result = null;
     
      int nQueryLength = (sQuery == null) ? 0 : sQuery.length();

      for (int i = 0; i < nQueryLength;)
      {
         int j = sQuery.indexOf('&', i);

         if (j < 0)
         {
            j = nQueryLength;
         }

         int k = sQuery.indexOf('=', i);

         if (k < 0 || k > j)
         {
            k = j;
         }

         try
         {
            String sName = URLDecoder.decode(sQuery.substring(i, k), GenericHTTPServer.ENCODING);
           
            if (ignoreSet == null || !ignoreSet.contains(sName))
            {
               String sValue;

               if (k != j && k + 1 < nQueryLength)
               {
                  sValue = URLDecoder.decode(sQuery.substring(k + 1, j), GenericHTTPServer.ENCODING);
               }
               else
               {
                  sValue = "";
               }

               if (result == null)
               {
                  result = new LookupHashMap();
               }

               String[] sValueArray = (String[])result.get(sName);

               if (sValueArray != null)
               {
                  int n = sValueArray.length;
                  String[] sNewValueArray = new String[n + 1];

                  System.arraycopy(sValueArray, 0, sNewValueArray, 0, n);
                  sNewValueArray[n] = sValue;
                  result.put(sName, sNewValueArray);
               }
               else
               {           
                  result.put(sName, new String[]{sValue});
               }
            }
         }
         // If there is a decoding issue then we do not add the key value pair to the result map
         // and fall back on the value in the request parameter map.
         catch (UnsupportedEncodingException e)
         {
         }

         i = j + 1;
      }
     
      return result;
   }
  
   /**
    * Returns a cache cookie based on the metadata passed-in. This key is
    * designed to be almost unique for every build.
    *
    * @param metadata The metadata to calculate the cache cookie against.
    * @return A unique cache cookie string with length less than 8. Not null.
    */
   public static String getCacheCookie(Metadata metadata)
   {
      String sBuildKey = null;
      CRC32 crc = new CRC32();
     
      try
      {
         crc.update(metadata.getChecksum().getBytes(GenericHTTPServer.ENCODING));
         crc.update(metadata.getVersion().getBytes(GenericHTTPServer.ENCODING));
         updateCRC(crc, (int)SysUtil.BUILD_NUMBER);
         updateCRC(crc, (int)SysUtil.BUILD_TIME);
         updateCRC(crc, (int)(SysUtil.BUILD_TIME >>> 32));
        
         sBuildKey = Long.toString(crc.getValue() & 0xffffffffL, Character.MAX_RADIX);
      }
      catch (UnsupportedEncodingException e)
      {
         ObjUtil.rethrow(e);
      }
     
      return sBuildKey;
   }
  
   /**
    * Updates the given CRC32 checksum with all 32 bits of an integer.
    *
    * @param crc The target CRC32 checksum to update.
    * @param n A 32-bit integer to update the checksum.
    */
   private static void updateCRC(CRC32 crc, int n)
   {
      for (int i = 0; i < 4; i++)
      {
         crc.update(n);
         n >>>= 8;
      }
   }
  
   /**
    * Adds multi-part form parameters from an input stream to a given Lookup
    * instance.
    *
    * @param requestInputStream InputStream from which to read request.
    * @param sEncoding Character encoding to be used when reading request.
    * @param sSeparator String used to delimit parts of a multi-part request.
    * @param paramMap Lookup to which request parameters should be added.
    * @param sizeMap Lookup containing max size information for request parameters.
    * @param lDefMaxSize Default max size for a parameter value.
    * @throws MultipartDataException
    * @throws UnsupportedEncodingException
    * @throws IOException
    */
   public static void addMultiPartParameters(InputStream requestInputStream, String sEncoding, String sSeparator,
      Lookup paramMap, Lookup sizeMap, long lDefMaxSize) throws IOException
   {
      if (paramMap == null)
      {
         return;
      }
     
      byte[] buf = null;
      MultipartInputStream multipartStream = new MultipartInputStream(requestInputStream, sSeparator.getBytes(sEncoding),
         sEncoding);
     
      while (multipartStream.nextPart())
      {
         MIMEHeader header = multipartStream.getHeaders().find("Content-Disposition");

         if (header == null)
         {
            continue;
         }

         MIMEHeader.Value value = header.getFirstValue();

         if (value == null || !value.getName().equals("form-data") && !value.getName().equals("attachment"))
         {
            continue;
         }

         String sName = value.findArg("name");

         if (sName == null)
         {
            continue;
         }

         String sFileName = getFilenameForUpload(header);
        
         header = multipartStream.getHeaders().find("Content-Type");

         if (header != null && header.getFirstValue() != null && header.getFirstValue().getName().startsWith("multipart/"))
         {
            throw new MultipartDataException("Multiple files per input are not supported");
         }

         long lMaxSize = lDefMaxSize;

         if (sizeMap != null)
         {
            Number maxSize = (Number)sizeMap.get(sName);

            if (maxSize != null)
            {
               lMaxSize = maxSize.longValue();
            }
         }

         InputStream is = multipartStream;
        
        
         if (lMaxSize > 0)
         {
            final boolean bParam = (sFileName == null);

            is = new LimitInputStream(multipartStream, lMaxSize, true)
            {
               /**
                * @see nexj.core.util.LimitInputStream#throwLimitException()
                */
               protected int throwLimitException() throws LimitIOException
               {
                  throw new RPCSizeException((bParam) ? "err.rpc.parameterSize" : "err.rpc.attachmentSize", new Object[]
                  {
                     new Long(m_lMaxCount)
                  });
               }
            };
         }

         if (sFileName == null)
         {
            StringWriter writer = new StringWriter();

            IOUtil.copy(writer, new InputStreamReader(is, sEncoding));
           
            Object currentValArray = paramMap.get(sName);
            String[] valArray = null;
           
            if (!(currentValArray instanceof String[]))
            {
               if (currentValArray == null)
               {
                  valArray = new String[]
                  {
                     writer.toString()
                  };
               }
               else
               {
                  valArray = new String[]
                  {
                     String.valueOf(currentValArray),
                     writer.toString()
                  };
               }
            }
            else
            {
               valArray = new String[((String[])currentValArray).length + 1];
               System.arraycopy(currentValArray, 0, valArray, 0, ((Object[])currentValArray).length);
               valArray[valArray.length - 1] = writer.toString();
            }
           
            paramMap.put(sName, valArray);
         }
         else
         {
            ByteArrayOutputStream ostream = new ByteArrayOutputStream(8192);

            if (buf == null)
            {
               buf = new byte[8192];
            }

            IOUtil.copy(ostream, multipartStream, buf);
            paramMap.put(sName, new Binary(ostream.toByteArray()));
            paramMap.put(sName + ".filename", sFileName);
         }
      }
   }
  
   /**
    * Retrieve's the filename for an upload from the given MIMEHeader instance.
    *
    * @param header MIMEHeader from which to extract the filename for an upload.
    * @return String containing the filename or NULL if not found.
    */
   public static String getFilenameForUpload(MIMEHeader header)
   {
      if (header != null)
      {
         MIMEHeader.Value value = header.getFirstValue();

         if (value == null || !value.getName().equals("form-data") && !value.getName().equals("attachment"))
         {
            return null;
         }

         String sFileName = value.findArg("filename");

         return (sFileName == null) ? null : sFileName.substring(Math.max(sFileName.lastIndexOf('/'), sFileName
            .lastIndexOf('\\')) + 1);
      }

      return null;
   }
}
TOP

Related Classes of nexj.core.rpc.http.HTTPUtil

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.