changeset 825:7fb7c1915788

remove jetty.util.B64Code
author Franklin Schmidt <fschmidt@gmail.com>
date Wed, 14 Sep 2016 16:38:33 -0600
parents 90bf40ba1ec2
children 6ebf86e4d2ca
files src/org/eclipse/jetty/util/B64Code.java src/org/eclipse/jetty/util/MultiPartInputStream.java src/org/eclipse/jetty/util/resource/Resource.java
diffstat 3 files changed, 1293 insertions(+), 1794 deletions(-) [+]
line wrap: on
line diff
diff -r 90bf40ba1ec2 -r 7fb7c1915788 src/org/eclipse/jetty/util/B64Code.java
--- a/src/org/eclipse/jetty/util/B64Code.java	Wed Sep 14 15:49:23 2016 -0600
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,450 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.util;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-
-
-/* ------------------------------------------------------------ */
-/** Fast B64 Encoder/Decoder as described in RFC 1421.
- * <p>Does not insert or interpret whitespace as described in RFC
- * 1521. If you require this you must pre/post process your data.
- * <p> Note that in a web context the usual case is to not want
- * linebreaks or other white space in the encoded output.
- * 
- */
-public class B64Code
-{
-    // ------------------------------------------------------------------
-    static final char __pad='=';
-    static final char[] __rfc1421alphabet=
-            {
-                'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
-                'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
-                'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
-                'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
-            };
-
-    static final byte[] __rfc1421nibbles;
-
-    static
-    {
-        __rfc1421nibbles=new byte[256];
-        for (int i=0;i<256;i++)
-            __rfc1421nibbles[i]=-1;
-        for (byte b=0;b<64;b++)
-            __rfc1421nibbles[(byte)__rfc1421alphabet[b]]=b;
-        __rfc1421nibbles[(byte)__pad]=0;
-    }
-
-    // ------------------------------------------------------------------
-    /**
-     * Base 64 encode as described in RFC 1421.
-     * <p>Does not insert whitespace as described in RFC 1521.
-     * @param s String to encode.
-     * @return String containing the encoded form of the input.
-     */
-    static public String encode(String s)
-    {
-        try
-        {
-            return encode(s,null);
-        }
-        catch (UnsupportedEncodingException e)
-        {
-            throw new IllegalArgumentException(e.toString());
-        }
-    }
-
-    // ------------------------------------------------------------------
-    /**
-     * Base 64 encode as described in RFC 1421.
-     * <p>Does not insert whitespace as described in RFC 1521.
-     * @param s String to encode.
-     * @param charEncoding String representing the name of
-     *        the character encoding of the provided input String.
-     * @return String containing the encoded form of the input.
-     */
-    static public String encode(String s,String charEncoding)
-            throws UnsupportedEncodingException
-    {
-        byte[] bytes;
-        if (charEncoding==null)
-            bytes=s.getBytes(StringUtil.__ISO_8859_1);
-        else
-            bytes=s.getBytes(charEncoding);
-
-        return new String(encode(bytes));
-    }
-    
-    // ------------------------------------------------------------------
-    /**
-     * Fast Base 64 encode as described in RFC 1421.
-     * <p>Does not insert whitespace as described in RFC 1521.
-     * <p> Avoids creating extra copies of the input/output.
-     * @param b byte array to encode.
-     * @return char array containing the encoded form of the input.
-     */
-    static public char[] encode(byte[] b)
-    {
-        if (b==null)
-            return null;
-
-        int bLen=b.length;
-        int cLen=((bLen+2)/3)*4;
-        char c[]=new char[cLen];
-        int ci=0;
-        int bi=0;
-        byte b0, b1, b2;
-        int stop=(bLen/3)*3;
-        while (bi<stop)
-        {
-            b0=b[bi++];
-            b1=b[bi++];
-            b2=b[bi++];
-            c[ci++]=__rfc1421alphabet[(b0>>>2)&0x3f];
-            c[ci++]=__rfc1421alphabet[(b0<<4)&0x3f|(b1>>>4)&0x0f];
-            c[ci++]=__rfc1421alphabet[(b1<<2)&0x3f|(b2>>>6)&0x03];
-            c[ci++]=__rfc1421alphabet[b2&077];
-        }
-
-        if (bLen!=bi)
-        {
-            switch (bLen%3)
-            {
-                case 2:
-                    b0=b[bi++];
-                    b1=b[bi++];
-                    c[ci++]=__rfc1421alphabet[(b0>>>2)&0x3f];
-                    c[ci++]=__rfc1421alphabet[(b0<<4)&0x3f|(b1>>>4)&0x0f];
-                    c[ci++]=__rfc1421alphabet[(b1<<2)&0x3f];
-                    c[ci++]=__pad;
-                    break;
-
-                case 1:
-                    b0=b[bi++];
-                    c[ci++]=__rfc1421alphabet[(b0>>>2)&0x3f];
-                    c[ci++]=__rfc1421alphabet[(b0<<4)&0x3f];
-                    c[ci++]=__pad;
-                    c[ci++]=__pad;
-                    break;
-
-                default:
-                    break;
-            }
-        }
-
-        return c;
-    }
-    
-    // ------------------------------------------------------------------
-    /**
-     * Fast Base 64 encode as described in RFC 1421 and RFC2045
-     * <p>Does not insert whitespace as described in RFC 1521, unless rfc2045 is passed as true.
-     * <p> Avoids creating extra copies of the input/output.
-     * @param b byte array to encode.
-     * @param rfc2045 If true, break lines at 76 characters with CRLF
-     * @return char array containing the encoded form of the input.
-     */
-    static public char[] encode(byte[] b, boolean rfc2045)
-    {
-        if (b==null)
-            return null;
-        if (!rfc2045)
-            return encode(b);
-
-        int bLen=b.length;
-        int cLen=((bLen+2)/3)*4;
-        cLen+=2+2*(cLen/76);
-        char c[]=new char[cLen];
-        int ci=0;
-        int bi=0;
-        byte b0, b1, b2;
-        int stop=(bLen/3)*3;
-        int l=0;
-        while (bi<stop)
-        {
-            b0=b[bi++];
-            b1=b[bi++];
-            b2=b[bi++];
-            c[ci++]=__rfc1421alphabet[(b0>>>2)&0x3f];
-            c[ci++]=__rfc1421alphabet[(b0<<4)&0x3f|(b1>>>4)&0x0f];
-            c[ci++]=__rfc1421alphabet[(b1<<2)&0x3f|(b2>>>6)&0x03];
-            c[ci++]=__rfc1421alphabet[b2&077];
-            l+=4;
-            if (l%76==0)
-            {
-                c[ci++]=13;
-                c[ci++]=10;
-            }
-        }
-
-        if (bLen!=bi)
-        {
-            switch (bLen%3)
-            {
-                case 2:
-                    b0=b[bi++];
-                    b1=b[bi++];
-                    c[ci++]=__rfc1421alphabet[(b0>>>2)&0x3f];
-                    c[ci++]=__rfc1421alphabet[(b0<<4)&0x3f|(b1>>>4)&0x0f];
-                    c[ci++]=__rfc1421alphabet[(b1<<2)&0x3f];
-                    c[ci++]=__pad;
-                    break;
-
-                case 1:
-                    b0=b[bi++];
-                    c[ci++]=__rfc1421alphabet[(b0>>>2)&0x3f];
-                    c[ci++]=__rfc1421alphabet[(b0<<4)&0x3f];
-                    c[ci++]=__pad;
-                    c[ci++]=__pad;
-                    break;
-
-                default:
-                    break;
-            }
-        }
-
-        c[ci++]=13;
-        c[ci++]=10;
-        return c;
-    }
-
-    // ------------------------------------------------------------------
-    /**
-     * Base 64 decode as described in RFC 2045.
-     * <p>Unlike {@link #decode(char[])}, extra whitespace is ignored.
-     * @param encoded String to decode.
-     * @param charEncoding String representing the character encoding
-     *        used to map the decoded bytes into a String.
-     * @return String decoded byte array.
-     * @throws UnsupportedEncodingException if the encoding is not supported
-     * @throws IllegalArgumentException if the input is not a valid
-     *         B64 encoding.
-     */
-    static public String decode(String encoded,String charEncoding)
-            throws UnsupportedEncodingException
-    {
-        byte[] decoded=decode(encoded);
-        if (charEncoding==null)
-            return new String(decoded);
-        return new String(decoded,charEncoding);
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * Fast Base 64 decode as described in RFC 1421.
-     * 
-     * <p>Unlike other decode methods, this does not attempt to 
-     * cope with extra whitespace as described in RFC 1521/2045.
-     * <p> Avoids creating extra copies of the input/output.
-     * <p> Note this code has been flattened for performance.
-     * @param b char array to decode.
-     * @return byte array containing the decoded form of the input.
-     * @throws IllegalArgumentException if the input is not a valid
-     *         B64 encoding.
-     */
-    static public byte[] decode(char[] b)
-    {
-        if (b==null)
-            return null;
-
-        int bLen=b.length;
-        if (bLen%4!=0)
-            throw new IllegalArgumentException("Input block size is not 4");
-
-        int li=bLen-1;
-        while (li>=0 && b[li]==(byte)__pad)
-            li--;
-
-        if (li<0)
-            return new byte[0];
-
-        // Create result array of exact required size.
-        int rLen=((li+1)*3)/4;
-        byte r[]=new byte[rLen];
-        int ri=0;
-        int bi=0;
-        int stop=(rLen/3)*3;
-        byte b0,b1,b2,b3;
-        try
-        {
-            while (ri<stop)
-            {
-                b0=__rfc1421nibbles[b[bi++]];
-                b1=__rfc1421nibbles[b[bi++]];
-                b2=__rfc1421nibbles[b[bi++]];
-                b3=__rfc1421nibbles[b[bi++]];
-                if (b0<0 || b1<0 || b2<0 || b3<0)
-                    throw new IllegalArgumentException("Not B64 encoded");
-
-                r[ri++]=(byte)(b0<<2|b1>>>4);
-                r[ri++]=(byte)(b1<<4|b2>>>2);
-                r[ri++]=(byte)(b2<<6|b3);
-            }
-
-            if (rLen!=ri)
-            {
-                switch (rLen%3)
-                {
-                    case 2:
-                        b0=__rfc1421nibbles[b[bi++]];
-                        b1=__rfc1421nibbles[b[bi++]];
-                        b2=__rfc1421nibbles[b[bi++]];
-                        if (b0<0 || b1<0 || b2<0)
-                            throw new IllegalArgumentException("Not B64 encoded");
-                        r[ri++]=(byte)(b0<<2|b1>>>4);
-                        r[ri++]=(byte)(b1<<4|b2>>>2);
-                        break;
-
-                    case 1:
-                        b0=__rfc1421nibbles[b[bi++]];
-                        b1=__rfc1421nibbles[b[bi++]];
-                        if (b0<0 || b1<0)
-                            throw new IllegalArgumentException("Not B64 encoded");
-                        r[ri++]=(byte)(b0<<2|b1>>>4);
-                        break;
-
-                    default:
-                        break;
-                }
-            }
-        }
-        catch (IndexOutOfBoundsException e)
-        {
-            throw new IllegalArgumentException("char "+bi
-                    +" was not B64 encoded");
-        }
-
-        return r;
-    }
-    
-    /* ------------------------------------------------------------ */
-    /**
-     * Base 64 decode as described in RFC 2045.
-     * <p>Unlike {@link #decode(char[])}, extra whitespace is ignored.
-     * @param encoded String to decode.
-     * @return byte array containing the decoded form of the input.
-     * @throws IllegalArgumentException if the input is not a valid
-     *         B64 encoding.
-     */
-    static public byte[] decode(String encoded)
-    {
-        if (encoded==null)
-            return null;
-
-        ByteArrayOutputStream bout = new ByteArrayOutputStream(4*encoded.length()/3);        
-        decode(encoded, bout);
-        return bout.toByteArray();
-    }
-    
-    /* ------------------------------------------------------------ */
-    /**
-     * Base 64 decode as described in RFC 2045.
-     * <p>Unlike {@link #decode(char[])}, extra whitespace is ignored.
-     * @param encoded String to decode.
-     * @param output stream for decoded bytes
-     * @return byte array containing the decoded form of the input.
-     * @throws IllegalArgumentException if the input is not a valid
-     *         B64 encoding.
-     */
-    static public void decode (String encoded, ByteArrayOutputStream bout)
-    {
-        if (encoded==null)
-            return;
-        
-        if (bout == null)
-            throw new IllegalArgumentException("No outputstream for decoded bytes");
-        
-        int ci=0;
-        byte nibbles[] = new byte[4];
-        int s=0;
-  
-        while (ci<encoded.length())
-        {
-            char c=encoded.charAt(ci++);
-
-            if (c==__pad)
-                break;
-            
-            if (Character.isWhitespace(c))
-                continue;
-
-            byte nibble=__rfc1421nibbles[c];
-            if (nibble<0)
-                throw new IllegalArgumentException("Not B64 encoded");
-
-            nibbles[s++]=__rfc1421nibbles[c];
-
-            switch(s)
-            {
-                case 1:
-                    break;
-                case 2:
-                    bout.write(nibbles[0]<<2|nibbles[1]>>>4);
-                    break;
-                case 3:
-                    bout.write(nibbles[1]<<4|nibbles[2]>>>2);
-                    break;
-                case 4:
-                    bout.write(nibbles[2]<<6|nibbles[3]);
-                    s=0;
-                    break;
-            }
-
-        }
-
-        return;
-    }
-    
-    
-    /* ------------------------------------------------------------ */
-    public static void encode(int value,Appendable buf) throws IOException
-    {
-        buf.append(__rfc1421alphabet[0x3f&((0xFC000000&value)>>26)]);
-        buf.append(__rfc1421alphabet[0x3f&((0x03F00000&value)>>20)]);
-        buf.append(__rfc1421alphabet[0x3f&((0x000FC000&value)>>14)]);
-        buf.append(__rfc1421alphabet[0x3f&((0x00003F00&value)>>8)]);
-        buf.append(__rfc1421alphabet[0x3f&((0x000000FC&value)>>2)]);
-        buf.append(__rfc1421alphabet[0x3f&((0x00000003&value)<<4)]);
-        buf.append('=');
-    }
-    
-    /* ------------------------------------------------------------ */
-    public static void encode(long lvalue,Appendable buf) throws IOException
-    {
-        int value=(int)(0xFFFFFFFC&(lvalue>>32));
-        buf.append(__rfc1421alphabet[0x3f&((0xFC000000&value)>>26)]);
-        buf.append(__rfc1421alphabet[0x3f&((0x03F00000&value)>>20)]);
-        buf.append(__rfc1421alphabet[0x3f&((0x000FC000&value)>>14)]);
-        buf.append(__rfc1421alphabet[0x3f&((0x00003F00&value)>>8)]);
-        buf.append(__rfc1421alphabet[0x3f&((0x000000FC&value)>>2)]);
-        
-        buf.append(__rfc1421alphabet[0x3f&((0x00000003&value)<<4) + (0xf&(int)(lvalue>>28))]);
-        
-        value=0x0FFFFFFF&(int)lvalue;
-        buf.append(__rfc1421alphabet[0x3f&((0x0FC00000&value)>>22)]);
-        buf.append(__rfc1421alphabet[0x3f&((0x003F0000&value)>>16)]);
-        buf.append(__rfc1421alphabet[0x3f&((0x0000FC00&value)>>10)]);
-        buf.append(__rfc1421alphabet[0x3f&((0x000003F0&value)>>4)]);
-        buf.append(__rfc1421alphabet[0x3f&((0x0000000F&value)<<2)]);
-    }
-}
diff -r 90bf40ba1ec2 -r 7fb7c1915788 src/org/eclipse/jetty/util/MultiPartInputStream.java
--- a/src/org/eclipse/jetty/util/MultiPartInputStream.java	Wed Sep 14 15:49:23 2016 -0600
+++ b/src/org/eclipse/jetty/util/MultiPartInputStream.java	Wed Sep 14 16:38:33 2016 -0600
@@ -41,6 +41,7 @@
 import java.util.Locale;
 import java.util.Map;
 import java.util.StringTokenizer;
+import java.util.Base64;
 
 import javax.servlet.MultipartConfigElement;
 import javax.servlet.ServletException;
@@ -58,794 +59,749 @@
  */
 public class MultiPartInputStream
 {
-    private static final Logger LOG = LoggerFactory.getLogger(MultiPartInputStream.class);
+	private static final Logger LOG = LoggerFactory.getLogger(MultiPartInputStream.class);
 
-    public static final MultipartConfigElement  __DEFAULT_MULTIPART_CONFIG = new MultipartConfigElement(System.getProperty("java.io.tmpdir"));
-    protected InputStream _in;
-    protected MultipartConfigElement _config;
-    protected String _contentType;
-    protected MultiMap<String> _parts;
-    protected File _tmpDir;
-    protected File _contextTmpDir;
-    protected boolean _deleteOnExit;
-    
-    
-    
-    public class MultiPart implements Part
-    {
-        protected String _name;
-        protected String _filename;
-        protected File _file;
-        protected OutputStream _out;
-        protected ByteArrayOutputStream2 _bout;
-        protected String _contentType;
-        protected MultiMap<String> _headers;
-        protected long _size = 0;
-        protected boolean _temporary = true;
+	public static final MultipartConfigElement  __DEFAULT_MULTIPART_CONFIG = new MultipartConfigElement(System.getProperty("java.io.tmpdir"));
+	protected InputStream _in;
+	protected MultipartConfigElement _config;
+	protected String _contentType;
+	protected MultiMap<String> _parts;
+	protected File _tmpDir;
+	protected File _contextTmpDir;
+	protected boolean _deleteOnExit;
+	
+	
+	
+	public class MultiPart implements Part
+	{
+		protected String _name;
+		protected String _filename;
+		protected File _file;
+		protected OutputStream _out;
+		protected ByteArrayOutputStream2 _bout;
+		protected String _contentType;
+		protected MultiMap<String> _headers;
+		protected long _size = 0;
+		protected boolean _temporary = true;
 
-        public MultiPart (String name, String filename) 
-        throws IOException
-        {
-            _name = name;
-            _filename = filename;
-        }
+		public MultiPart (String name, String filename) 
+		throws IOException
+		{
+			_name = name;
+			_filename = filename;
+		}
 
-        protected void setContentType (String contentType)
-        {
-            _contentType = contentType;
-        }
-        
-        
-        protected void open() 
-        throws IOException
-        {
-            //We will either be writing to a file, if it has a filename on the content-disposition
-            //and otherwise a byte-array-input-stream, OR if we exceed the getFileSizeThreshold, we
-            //will need to change to write to a file.           
-            if (_filename != null && _filename.trim().length() > 0)
-            {
-                createFile();            
-            }
-            else
-            {
-                //Write to a buffer in memory until we discover we've exceed the 
-                //MultipartConfig fileSizeThreshold
-                _out = _bout= new ByteArrayOutputStream2();
-            }
-        }
-        
-        protected void close() 
-        throws IOException
-        {
-            _out.close();
-        }
-        
-      
-        protected void write (int b)
-        throws IOException
-        {      
-            if (MultiPartInputStream.this._config.getMaxFileSize() > 0 && _size + 1 > MultiPartInputStream.this._config.getMaxFileSize())
-                throw new IllegalStateException ("Multipart Mime part "+_name+" exceeds max filesize");
-            
-            if (MultiPartInputStream.this._config.getFileSizeThreshold() > 0 && _size + 1 > MultiPartInputStream.this._config.getFileSizeThreshold() && _file==null)
-                createFile();
-            _out.write(b);   
-            _size ++;
-        }
-        
-        protected void write (byte[] bytes, int offset, int length) 
-        throws IOException
-        { 
-            if (MultiPartInputStream.this._config.getMaxFileSize() > 0 && _size + length > MultiPartInputStream.this._config.getMaxFileSize())
-                throw new IllegalStateException ("Multipart Mime part "+_name+" exceeds max filesize");
-            
-            if (MultiPartInputStream.this._config.getFileSizeThreshold() > 0 && _size + length > MultiPartInputStream.this._config.getFileSizeThreshold() && _file==null)
-                createFile();
-            
-            _out.write(bytes, offset, length);
-            _size += length;
-        }
-        
-        protected void createFile ()
-        throws IOException
-        {
-            _file = File.createTempFile("MultiPart", "", MultiPartInputStream.this._tmpDir);
-            if (_deleteOnExit)
-                _file.deleteOnExit();
-            FileOutputStream fos = new FileOutputStream(_file);
-            BufferedOutputStream bos = new BufferedOutputStream(fos);
-            
-            if (_size > 0 && _out != null)
-            {
-                //already written some bytes, so need to copy them into the file
-                _out.flush();
-                _bout.writeTo(bos);
-                _out.close();
-                _bout = null;
-            }
-            _out = bos;
-        }
-        
+		protected void setContentType (String contentType)
+		{
+			_contentType = contentType;
+		}
+		
+		
+		protected void open() 
+		throws IOException
+		{
+			//We will either be writing to a file, if it has a filename on the content-disposition
+			//and otherwise a byte-array-input-stream, OR if we exceed the getFileSizeThreshold, we
+			//will need to change to write to a file.           
+			if (_filename != null && _filename.trim().length() > 0)
+			{
+				createFile();            
+			}
+			else
+			{
+				//Write to a buffer in memory until we discover we've exceed the 
+				//MultipartConfig fileSizeThreshold
+				_out = _bout= new ByteArrayOutputStream2();
+			}
+		}
+		
+		protected void close() 
+		throws IOException
+		{
+			_out.close();
+		}
+		
+	  
+		protected void write (int b)
+		throws IOException
+		{      
+			if (MultiPartInputStream.this._config.getMaxFileSize() > 0 && _size + 1 > MultiPartInputStream.this._config.getMaxFileSize())
+				throw new IllegalStateException ("Multipart Mime part "+_name+" exceeds max filesize");
+			
+			if (MultiPartInputStream.this._config.getFileSizeThreshold() > 0 && _size + 1 > MultiPartInputStream.this._config.getFileSizeThreshold() && _file==null)
+				createFile();
+			_out.write(b);   
+			_size ++;
+		}
+		
+		protected void write (byte[] bytes, int offset, int length) 
+		throws IOException
+		{ 
+			if (MultiPartInputStream.this._config.getMaxFileSize() > 0 && _size + length > MultiPartInputStream.this._config.getMaxFileSize())
+				throw new IllegalStateException ("Multipart Mime part "+_name+" exceeds max filesize");
+			
+			if (MultiPartInputStream.this._config.getFileSizeThreshold() > 0 && _size + length > MultiPartInputStream.this._config.getFileSizeThreshold() && _file==null)
+				createFile();
+			
+			_out.write(bytes, offset, length);
+			_size += length;
+		}
+		
+		protected void createFile ()
+		throws IOException
+		{
+			_file = File.createTempFile("MultiPart", "", MultiPartInputStream.this._tmpDir);
+			if (_deleteOnExit)
+				_file.deleteOnExit();
+			FileOutputStream fos = new FileOutputStream(_file);
+			BufferedOutputStream bos = new BufferedOutputStream(fos);
+			
+			if (_size > 0 && _out != null)
+			{
+				//already written some bytes, so need to copy them into the file
+				_out.flush();
+				_bout.writeTo(bos);
+				_out.close();
+				_bout = null;
+			}
+			_out = bos;
+		}
+		
 
-        
-        protected void setHeaders(MultiMap<String> headers)
-        {
-            _headers = headers;
-        }
-        
-        /** 
-         * @see javax.servlet.http.Part#getContentType()
-         */
-        public String getContentType()
-        {
-            return _contentType;
-        }
+		
+		protected void setHeaders(MultiMap<String> headers)
+		{
+			_headers = headers;
+		}
+		
+		/** 
+		 * @see javax.servlet.http.Part#getContentType()
+		 */
+		public String getContentType()
+		{
+			return _contentType;
+		}
 
-        /** 
-         * @see javax.servlet.http.Part#getHeader(java.lang.String)
-         */
-        public String getHeader(String name)
-        {
-            if (name == null)
-                return null;
-            return (String)_headers.getValue(name.toLowerCase(Locale.ENGLISH), 0);
-        }
+		/** 
+		 * @see javax.servlet.http.Part#getHeader(java.lang.String)
+		 */
+		public String getHeader(String name)
+		{
+			if (name == null)
+				return null;
+			return (String)_headers.getValue(name.toLowerCase(Locale.ENGLISH), 0);
+		}
 
-        /** 
-         * @see javax.servlet.http.Part#getHeaderNames()
-         */
-        public Collection<String> getHeaderNames()
-        {
-            return _headers.keySet();
-        }
+		/** 
+		 * @see javax.servlet.http.Part#getHeaderNames()
+		 */
+		public Collection<String> getHeaderNames()
+		{
+			return _headers.keySet();
+		}
 
-        /** 
-         * @see javax.servlet.http.Part#getHeaders(java.lang.String)
-         */
-        public Collection<String> getHeaders(String name)
-        {
-           return _headers.getValues(name);
-        }
+		/** 
+		 * @see javax.servlet.http.Part#getHeaders(java.lang.String)
+		 */
+		public Collection<String> getHeaders(String name)
+		{
+		   return _headers.getValues(name);
+		}
 
-        /** 
-         * @see javax.servlet.http.Part#getInputStream()
-         */
-        public InputStream getInputStream() throws IOException
-        {
-           if (_file != null)
-           {
-               //written to a file, whether temporary or not
-               return new BufferedInputStream (new FileInputStream(_file));
-           }
-           else
-           {
-               //part content is in memory
-               return new ByteArrayInputStream(_bout.getBuf(),0,_bout.size());
-           }
-        }
+		/** 
+		 * @see javax.servlet.http.Part#getInputStream()
+		 */
+		public InputStream getInputStream() throws IOException
+		{
+		   if (_file != null)
+		   {
+			   //written to a file, whether temporary or not
+			   return new BufferedInputStream (new FileInputStream(_file));
+		   }
+		   else
+		   {
+			   //part content is in memory
+			   return new ByteArrayInputStream(_bout.getBuf(),0,_bout.size());
+		   }
+		}
 
-        public byte[] getBytes()
-        {
-            if (_bout!=null)
-                return _bout.toByteArray();
-            return null;
-        }
-        
-        /** 
-         * @see javax.servlet.http.Part#getName()
-         */
-        public String getName()
-        {
-           return _name;
-        }
+		public byte[] getBytes()
+		{
+			if (_bout!=null)
+				return _bout.toByteArray();
+			return null;
+		}
+		
+		/** 
+		 * @see javax.servlet.http.Part#getName()
+		 */
+		public String getName()
+		{
+		   return _name;
+		}
 
-        /** 
-         * @see javax.servlet.http.Part#getSize()
-         */
-        public long getSize()
-        {
-            return _size;         
-        }
+		/** 
+		 * @see javax.servlet.http.Part#getSize()
+		 */
+		public long getSize()
+		{
+			return _size;         
+		}
 
-        /** 
-         * @see javax.servlet.http.Part#write(java.lang.String)
-         */
-        public void write(String fileName) throws IOException
-        {
-            if (_file == null)
-            {
-                _temporary = false;
-                
-                //part data is only in the ByteArrayOutputStream and never been written to disk
-                _file = new File (_tmpDir, fileName);
+		/** 
+		 * @see javax.servlet.http.Part#write(java.lang.String)
+		 */
+		public void write(String fileName) throws IOException
+		{
+			if (_file == null)
+			{
+				_temporary = false;
+				
+				//part data is only in the ByteArrayOutputStream and never been written to disk
+				_file = new File (_tmpDir, fileName);
 
-                BufferedOutputStream bos = null;
-                try
-                {
-                    bos = new BufferedOutputStream(new FileOutputStream(_file));
-                    _bout.writeTo(bos);
-                    bos.flush();
-                }
-                finally
-                {
-                    if (bos != null)
-                        bos.close();
-                    _bout = null;
-                }
-            }
-            else
-            {
-                //the part data is already written to a temporary file, just rename it
-                _temporary = false;
-                
-                File f = new File(_tmpDir, fileName);
-                if (_file.renameTo(f))
-                    _file = f;
-            }
-        }
-        
-        /** 
-         * Remove the file, whether or not Part.write() was called on it
-         * (ie no longer temporary)
-         * @see javax.servlet.http.Part#delete()
-         */
-        public void delete() throws IOException
-        {
-            if (_file != null && _file.exists())
-                _file.delete();     
-        }
-        
-        /**
-         * Only remove tmp files.
-         * 
-         * @throws IOException
-         */
-        public void cleanUp() throws IOException
-        {
-            if (_temporary && _file != null && _file.exists())
-                _file.delete();
-        }
-        
-        
-        /**
-         * Get the file, if any, the data has been written to.
-         * @return
-         */
-        public File getFile ()
-        {
-            return _file;
-        }  
-        
-        
-        /**
-         * Get the filename from the content-disposition.
-         * @return null or the filename
-         */
-        public String getContentDispositionFilename ()
-        {
-            return _filename;
-        }
-    }
-    
-    
-    
-    
-    /**
-     * @param in Request input stream 
-     * @param contentType Content-Type header
-     * @param config MultipartConfigElement 
-     * @param contextTmpDir javax.servlet.context.tempdir
-     */
-    public MultiPartInputStream (InputStream in, String contentType, MultipartConfigElement config, File contextTmpDir)
-    {
-        _in = new ReadLineInputStream(in);
-       _contentType = contentType;
-       _config = config;
-       _contextTmpDir = contextTmpDir;
-       if (_contextTmpDir == null)
-           _contextTmpDir = new File (System.getProperty("java.io.tmpdir"));
-       
-       if (_config == null)
-           _config = new MultipartConfigElement(_contextTmpDir.getAbsolutePath());
-    }
+				BufferedOutputStream bos = null;
+				try
+				{
+					bos = new BufferedOutputStream(new FileOutputStream(_file));
+					_bout.writeTo(bos);
+					bos.flush();
+				}
+				finally
+				{
+					if (bos != null)
+						bos.close();
+					_bout = null;
+				}
+			}
+			else
+			{
+				//the part data is already written to a temporary file, just rename it
+				_temporary = false;
+				
+				File f = new File(_tmpDir, fileName);
+				if (_file.renameTo(f))
+					_file = f;
+			}
+		}
+		
+		/** 
+		 * Remove the file, whether or not Part.write() was called on it
+		 * (ie no longer temporary)
+		 * @see javax.servlet.http.Part#delete()
+		 */
+		public void delete() throws IOException
+		{
+			if (_file != null && _file.exists())
+				_file.delete();     
+		}
+		
+		/**
+		 * Only remove tmp files.
+		 * 
+		 * @throws IOException
+		 */
+		public void cleanUp() throws IOException
+		{
+			if (_temporary && _file != null && _file.exists())
+				_file.delete();
+		}
+		
+		
+		/**
+		 * Get the file, if any, the data has been written to.
+		 * @return
+		 */
+		public File getFile ()
+		{
+			return _file;
+		}  
+		
+		
+		/**
+		 * Get the filename from the content-disposition.
+		 * @return null or the filename
+		 */
+		public String getContentDispositionFilename ()
+		{
+			return _filename;
+		}
+	}
+	
+	
+	
+	
+	/**
+	 * @param in Request input stream 
+	 * @param contentType Content-Type header
+	 * @param config MultipartConfigElement 
+	 * @param contextTmpDir javax.servlet.context.tempdir
+	 */
+	public MultiPartInputStream (InputStream in, String contentType, MultipartConfigElement config, File contextTmpDir)
+	{
+		_in = new ReadLineInputStream(in);
+	   _contentType = contentType;
+	   _config = config;
+	   _contextTmpDir = contextTmpDir;
+	   if (_contextTmpDir == null)
+		   _contextTmpDir = new File (System.getProperty("java.io.tmpdir"));
+	   
+	   if (_config == null)
+		   _config = new MultipartConfigElement(_contextTmpDir.getAbsolutePath());
+	}
 
-    /**
-     * Get the already parsed parts.
-     * 
-     * @return
-     */
-    public Collection<Part> getParsedParts()
-    {
-        if (_parts == null)
-            return Collections.emptyList();
+	/**
+	 * Get the already parsed parts.
+	 * 
+	 * @return
+	 */
+	public Collection<Part> getParsedParts()
+	{
+		if (_parts == null)
+			return Collections.emptyList();
 
-        Collection<Object> values = _parts.values();
-        List<Part> parts = new ArrayList<Part>();
-        for (Object o: values)
-        {
-            List<Part> asList = LazyList.getList(o, false);
-            parts.addAll(asList);
-        }
-        return parts;
-    }
-    
-    /**
-     * Delete any tmp storage for parts, and clear out the parts list.
-     * 
-     * @throws MultiException
-     */
-    public void deleteParts ()
-    throws MultiException
-    {
-        Collection<Part> parts = getParsedParts();
-        MultiException err = new MultiException();
-        for (Part p:parts)
-        {
-            try
-            {
-                ((MultiPartInputStream.MultiPart)p).cleanUp();
-            } 
-            catch(Exception e)
-            {     
-                err.add(e); 
-            }
-        }
-        _parts.clear();
-        
-        err.ifExceptionThrowMulti();
-    }
+		Collection<Object> values = _parts.values();
+		List<Part> parts = new ArrayList<Part>();
+		for (Object o: values)
+		{
+			List<Part> asList = LazyList.getList(o, false);
+			parts.addAll(asList);
+		}
+		return parts;
+	}
+	
+	/**
+	 * Delete any tmp storage for parts, and clear out the parts list.
+	 * 
+	 * @throws MultiException
+	 */
+	public void deleteParts ()
+	throws MultiException
+	{
+		Collection<Part> parts = getParsedParts();
+		MultiException err = new MultiException();
+		for (Part p:parts)
+		{
+			try
+			{
+				((MultiPartInputStream.MultiPart)p).cleanUp();
+			} 
+			catch(Exception e)
+			{     
+				err.add(e); 
+			}
+		}
+		_parts.clear();
+		
+		err.ifExceptionThrowMulti();
+	}
 
    
-    /**
-     * Parse, if necessary, the multipart data and return the list of Parts.
-     * 
-     * @return
-     * @throws IOException
-     * @throws ServletException
-     */
-    public Collection<Part> getParts()
-    throws IOException, ServletException
-    {
-        parse();
-        Collection<Object> values = _parts.values();
-        List<Part> parts = new ArrayList<Part>();
-        for (Object o: values)
-        {
-            List<Part> asList = LazyList.getList(o, false);
-            parts.addAll(asList);
-        }
-        return parts;
-    }
-    
-    
-    /**
-     * Get the named Part.
-     * 
-     * @param name
-     * @return
-     * @throws IOException
-     * @throws ServletException
-     */
-    public Part getPart(String name)
-    throws IOException, ServletException
-    {
-        parse();
-        return (Part)_parts.getValue(name, 0);
-    }
-    
-    
-    /**
-     * Parse, if necessary, the multipart stream.
-     * 
-     * @throws IOException
-     * @throws ServletException
-     */
-    protected void parse ()
-    throws IOException, ServletException
-    {
-        //have we already parsed the input?
-        if (_parts != null)
-            return;
-        
-        //initialize
-        long total = 0; //keep running total of size of bytes read from input and throw an exception if exceeds MultipartConfigElement._maxRequestSize              
-        _parts = new MultiMap<String>();
+	/**
+	 * Parse, if necessary, the multipart data and return the list of Parts.
+	 * 
+	 * @return
+	 * @throws IOException
+	 * @throws ServletException
+	 */
+	public Collection<Part> getParts()
+	throws IOException, ServletException
+	{
+		parse();
+		Collection<Object> values = _parts.values();
+		List<Part> parts = new ArrayList<Part>();
+		for (Object o: values)
+		{
+			List<Part> asList = LazyList.getList(o, false);
+			parts.addAll(asList);
+		}
+		return parts;
+	}
+	
+	
+	/**
+	 * Get the named Part.
+	 * 
+	 * @param name
+	 * @return
+	 * @throws IOException
+	 * @throws ServletException
+	 */
+	public Part getPart(String name)
+	throws IOException, ServletException
+	{
+		parse();
+		return (Part)_parts.getValue(name, 0);
+	}
+	
+	
+	/**
+	 * Parse, if necessary, the multipart stream.
+	 * 
+	 * @throws IOException
+	 * @throws ServletException
+	 */
+	protected void parse ()
+	throws IOException, ServletException
+	{
+		//have we already parsed the input?
+		if (_parts != null)
+			return;
+		
+		//initialize
+		long total = 0; //keep running total of size of bytes read from input and throw an exception if exceeds MultipartConfigElement._maxRequestSize              
+		_parts = new MultiMap<String>();
 
-        //if its not a multipart request, don't parse it
-        if (_contentType == null || !_contentType.startsWith("multipart/form-data"))
-            return;
+		//if its not a multipart request, don't parse it
+		if (_contentType == null || !_contentType.startsWith("multipart/form-data"))
+			return;
  
-        //sort out the location to which to write the files
-        
-        if (_config.getLocation() == null)
-            _tmpDir = _contextTmpDir;
-        else if ("".equals(_config.getLocation()))
-            _tmpDir = _contextTmpDir;
-        else
-        {
-            File f = new File (_config.getLocation());
-            if (f.isAbsolute())
-                _tmpDir = f;
-            else
-                _tmpDir = new File (_contextTmpDir, _config.getLocation());
-        }
-      
-        if (!_tmpDir.exists())
-            _tmpDir.mkdirs();
+		//sort out the location to which to write the files
+		
+		if (_config.getLocation() == null)
+			_tmpDir = _contextTmpDir;
+		else if ("".equals(_config.getLocation()))
+			_tmpDir = _contextTmpDir;
+		else
+		{
+			File f = new File (_config.getLocation());
+			if (f.isAbsolute())
+				_tmpDir = f;
+			else
+				_tmpDir = new File (_contextTmpDir, _config.getLocation());
+		}
+	  
+		if (!_tmpDir.exists())
+			_tmpDir.mkdirs();
 
-        String contentTypeBoundary = "";
-        int bstart = _contentType.indexOf("boundary=");
-        if (bstart >= 0)
-        {
-            int bend = _contentType.indexOf(";", bstart);
-            bend = (bend < 0? _contentType.length(): bend);
-            contentTypeBoundary = QuotedStringTokenizer.unquote(value(_contentType.substring(bstart,bend), true).trim());
-        }
-        
-        String boundary="--"+contentTypeBoundary;
-        byte[] byteBoundary=(boundary+"--").getBytes(StringUtil.__ISO_8859_1);
+		String contentTypeBoundary = "";
+		int bstart = _contentType.indexOf("boundary=");
+		if (bstart >= 0)
+		{
+			int bend = _contentType.indexOf(";", bstart);
+			bend = (bend < 0? _contentType.length(): bend);
+			contentTypeBoundary = QuotedStringTokenizer.unquote(value(_contentType.substring(bstart,bend), true).trim());
+		}
+		
+		String boundary="--"+contentTypeBoundary;
+		byte[] byteBoundary=(boundary+"--").getBytes(StringUtil.__ISO_8859_1);
 
-        // Get first boundary
-        String line = null;
-        try
-        {
-            line=((ReadLineInputStream)_in).readLine();  
-        }
-        catch (IOException e)
-        {
-            LOG.warn("Badly formatted multipart request");
-            throw e;
-        }
+		// Get first boundary
+		String line = null;
+		try
+		{
+			line=((ReadLineInputStream)_in).readLine();  
+		}
+		catch (IOException e)
+		{
+			LOG.warn("Badly formatted multipart request");
+			throw e;
+		}
 
-        if (line == null)
-            throw new IOException("Missing content for multipart request");
+		if (line == null)
+			throw new IOException("Missing content for multipart request");
 
-        boolean badFormatLogged = false;
-        line=line.trim();
-        while (line != null && !line.equals(boundary))
-        {
-            if (!badFormatLogged)
-            {
-                LOG.warn("Badly formatted multipart request");
-                badFormatLogged = true;
-            }
-            line=((ReadLineInputStream)_in).readLine();
-            line=(line==null?line:line.trim());
-        }
+		boolean badFormatLogged = false;
+		line=line.trim();
+		while (line != null && !line.equals(boundary))
+		{
+			if (!badFormatLogged)
+			{
+				LOG.warn("Badly formatted multipart request");
+				badFormatLogged = true;
+			}
+			line=((ReadLineInputStream)_in).readLine();
+			line=(line==null?line:line.trim());
+		}
 
-        if (line == null)
-            throw new IOException("Missing initial multi part boundary");
+		if (line == null)
+			throw new IOException("Missing initial multi part boundary");
 
-        // Read each part
-        boolean lastPart=false;
+		// Read each part
+		boolean lastPart=false;
 
-        outer:while(!lastPart)
-        {
-            String contentDisposition=null;
-            String contentType=null;
-            String contentTransferEncoding=null;
-            
-            MultiMap<String> headers = new MultiMap<String>();
-            while(true)
-            {
-                line=((ReadLineInputStream)_in).readLine();
-                
-                //No more input
-                if(line==null)
-                    break outer;
+		outer:while(!lastPart)
+		{
+			String contentDisposition=null;
+			String contentType=null;
+			String contentTransferEncoding=null;
+			
+			MultiMap<String> headers = new MultiMap<String>();
+			while(true)
+			{
+				line=((ReadLineInputStream)_in).readLine();
+				
+				//No more input
+				if(line==null)
+					break outer;
 
-                // If blank line, end of part headers
-                if("".equals(line))
-                    break;
-                
-                total += line.length();
-                if (_config.getMaxRequestSize() > 0 && total > _config.getMaxRequestSize())
-                    throw new IllegalStateException ("Request exceeds maxRequestSize ("+_config.getMaxRequestSize()+")");
+				// If blank line, end of part headers
+				if("".equals(line))
+					break;
+				
+				total += line.length();
+				if (_config.getMaxRequestSize() > 0 && total > _config.getMaxRequestSize())
+					throw new IllegalStateException ("Request exceeds maxRequestSize ("+_config.getMaxRequestSize()+")");
 
-                //get content-disposition and content-type
-                int c=line.indexOf(':',0);
-                if(c>0)
-                {
-                    String key=line.substring(0,c).trim().toLowerCase(Locale.ENGLISH);
-                    String value=line.substring(c+1,line.length()).trim();
-                    headers.put(key, value);
-                    if (key.equalsIgnoreCase("content-disposition"))
-                        contentDisposition=value;
-                    if (key.equalsIgnoreCase("content-type"))
-                        contentType = value;
-                    if(key.equals("content-transfer-encoding"))
-                        contentTransferEncoding=value;
+				//get content-disposition and content-type
+				int c=line.indexOf(':',0);
+				if(c>0)
+				{
+					String key=line.substring(0,c).trim().toLowerCase(Locale.ENGLISH);
+					String value=line.substring(c+1,line.length()).trim();
+					headers.put(key, value);
+					if (key.equalsIgnoreCase("content-disposition"))
+						contentDisposition=value;
+					if (key.equalsIgnoreCase("content-type"))
+						contentType = value;
+					if(key.equals("content-transfer-encoding"))
+						contentTransferEncoding=value;
 
-                }
-            }
+				}
+			}
 
-            // Extract content-disposition
-            boolean form_data=false;
-            if(contentDisposition==null)
-            {
-                throw new IOException("Missing content-disposition");
-            }
+			// Extract content-disposition
+			boolean form_data=false;
+			if(contentDisposition==null)
+			{
+				throw new IOException("Missing content-disposition");
+			}
 
-            QuotedStringTokenizer tok=new QuotedStringTokenizer(contentDisposition,";", false, true);
-            String name=null;
-            String filename=null;
-            while(tok.hasMoreTokens())
-            {
-                String t=tok.nextToken().trim();
-                String tl=t.toLowerCase(Locale.ENGLISH);
-                if(t.startsWith("form-data"))
-                    form_data=true;
-                else if(tl.startsWith("name="))
-                    name=value(t, true);
-                else if(tl.startsWith("filename="))
-                    filename=filenameValue(t);
-            }
+			QuotedStringTokenizer tok=new QuotedStringTokenizer(contentDisposition,";", false, true);
+			String name=null;
+			String filename=null;
+			while(tok.hasMoreTokens())
+			{
+				String t=tok.nextToken().trim();
+				String tl=t.toLowerCase(Locale.ENGLISH);
+				if(t.startsWith("form-data"))
+					form_data=true;
+				else if(tl.startsWith("name="))
+					name=value(t, true);
+				else if(tl.startsWith("filename="))
+					filename=filenameValue(t);
+			}
 
-            // Check disposition
-            if(!form_data)
-            {
-                continue;
-            }
-            //It is valid for reset and submit buttons to have an empty name.
-            //If no name is supplied, the browser skips sending the info for that field.
-            //However, if you supply the empty string as the name, the browser sends the
-            //field, with name as the empty string. So, only continue this loop if we
-            //have not yet seen a name field.
-            if(name==null)
-            {
-                continue;
-            }
+			// Check disposition
+			if(!form_data)
+			{
+				continue;
+			}
+			//It is valid for reset and submit buttons to have an empty name.
+			//If no name is supplied, the browser skips sending the info for that field.
+			//However, if you supply the empty string as the name, the browser sends the
+			//field, with name as the empty string. So, only continue this loop if we
+			//have not yet seen a name field.
+			if(name==null)
+			{
+				continue;
+			}
 
-            //Have a new Part
-            MultiPart part = new MultiPart(name, filename);
-            part.setHeaders(headers);
-            part.setContentType(contentType);
-            _parts.add(name, part);
-            part.open();
-            
-            InputStream partInput = null;
-            if ("base64".equalsIgnoreCase(contentTransferEncoding))
-            {
-                partInput = new Base64InputStream((ReadLineInputStream)_in);
-            }
-            else if ("quoted-printable".equalsIgnoreCase(contentTransferEncoding))
-            {
-                partInput = new FilterInputStream(_in)
-                {
-                    @Override
-                    public int read() throws IOException
-                    {
-                        int c = in.read();
-                        if (c >= 0 && c == '=')
-                        {
-                            int hi = in.read();
-                            int lo = in.read();
-                            if (hi < 0 || lo < 0)
-                            {
-                                throw new IOException("Unexpected end to quoted-printable byte");
-                            }
-                            char[] chars = new char[] { (char)hi, (char)lo };
-                            c = Integer.parseInt(new String(chars),16);
-                        }
-                        return c;
-                    }
-                };
-            }
-            else
-                partInput = _in;
-            
-            try
-            { 
-                int state=-2;
-                int c;
-                boolean cr=false;
-                boolean lf=false;
+			//Have a new Part
+			MultiPart part = new MultiPart(name, filename);
+			part.setHeaders(headers);
+			part.setContentType(contentType);
+			_parts.add(name, part);
+			part.open();
+			
+			InputStream partInput = null;
+			if ("base64".equalsIgnoreCase(contentTransferEncoding))
+			{
+				partInput = Base64.getDecoder().wrap(_in);
+			}
+			else if ("quoted-printable".equalsIgnoreCase(contentTransferEncoding))
+			{
+				partInput = new FilterInputStream(_in)
+				{
+					@Override
+					public int read() throws IOException
+					{
+						int c = in.read();
+						if (c >= 0 && c == '=')
+						{
+							int hi = in.read();
+							int lo = in.read();
+							if (hi < 0 || lo < 0)
+							{
+								throw new IOException("Unexpected end to quoted-printable byte");
+							}
+							char[] chars = new char[] { (char)hi, (char)lo };
+							c = Integer.parseInt(new String(chars),16);
+						}
+						return c;
+					}
+				};
+			}
+			else
+				partInput = _in;
+			
+			try
+			{ 
+				int state=-2;
+				int c;
+				boolean cr=false;
+				boolean lf=false;
 
-                // loop for all lines
-                while(true)
-                {
-                    int b=0;
-                    while((c=(state!=-2)?state:partInput.read())!=-1)
-                    {
-                        total ++;
-                        if (_config.getMaxRequestSize() > 0 && total > _config.getMaxRequestSize())
-                            throw new IllegalStateException("Request exceeds maxRequestSize ("+_config.getMaxRequestSize()+")");
-                        
-                        state=-2;
-                        
-                        // look for CR and/or LF
-                        if(c==13||c==10)
-                        {
-                            if(c==13)
-                            {
-                                partInput.mark(1);
-                                int tmp=partInput.read();
-                                if (tmp!=10)
-                                    partInput.reset();
-                                else
-                                    state=tmp;
-                            }
-                            break;
-                        }
-                        
-                        // Look for boundary
-                        if(b>=0&&b<byteBoundary.length&&c==byteBoundary[b])
-                        {
-                            b++;
-                        }
-                        else
-                        {
-                            // Got a character not part of the boundary, so we don't have the boundary marker.
-                            // Write out as many chars as we matched, then the char we're looking at.
-                            if(cr)
-                                part.write(13);
-                    
-                            if(lf)
-                                part.write(10); 
-                            
-                            cr=lf=false;
-                            if(b>0)
-                                part.write(byteBoundary,0,b);
-                              
-                            b=-1;
-                            part.write(c);
-                        }
-                    }
-                    
-                    // Check for incomplete boundary match, writing out the chars we matched along the way
-                    if((b>0&&b<byteBoundary.length-2)||(b==byteBoundary.length-1))
-                    {
-                        if(cr)
-                            part.write(13);
+				// loop for all lines
+				while(true)
+				{
+					int b=0;
+					while((c=(state!=-2)?state:partInput.read())!=-1)
+					{
+						total ++;
+						if (_config.getMaxRequestSize() > 0 && total > _config.getMaxRequestSize())
+							throw new IllegalStateException("Request exceeds maxRequestSize ("+_config.getMaxRequestSize()+")");
+						
+						state=-2;
+						
+						// look for CR and/or LF
+						if(c==13||c==10)
+						{
+							if(c==13)
+							{
+								partInput.mark(1);
+								int tmp=partInput.read();
+								if (tmp!=10)
+									partInput.reset();
+								else
+									state=tmp;
+							}
+							break;
+						}
+						
+						// Look for boundary
+						if(b>=0&&b<byteBoundary.length&&c==byteBoundary[b])
+						{
+							b++;
+						}
+						else
+						{
+							// Got a character not part of the boundary, so we don't have the boundary marker.
+							// Write out as many chars as we matched, then the char we're looking at.
+							if(cr)
+								part.write(13);
+					
+							if(lf)
+								part.write(10); 
+							
+							cr=lf=false;
+							if(b>0)
+								part.write(byteBoundary,0,b);
+							  
+							b=-1;
+							part.write(c);
+						}
+					}
+					
+					// Check for incomplete boundary match, writing out the chars we matched along the way
+					if((b>0&&b<byteBoundary.length-2)||(b==byteBoundary.length-1))
+					{
+						if(cr)
+							part.write(13);
 
-                        if(lf)
-                            part.write(10);
+						if(lf)
+							part.write(10);
 
-                        cr=lf=false;
-                        part.write(byteBoundary,0,b);
-                        b=-1;
-                    }
-                    
-                    // Boundary match. If we've run out of input or we matched the entire final boundary marker, then this is the last part.
-                    if(b>0||c==-1)
-                    {
-                       
-                        if(b==byteBoundary.length)
-                            lastPart=true;
-                        if(state==10)
-                            state=-2;
-                        break;
-                    }
-                    
-                    // handle CR LF
-                    if(cr)
-                        part.write(13); 
+						cr=lf=false;
+						part.write(byteBoundary,0,b);
+						b=-1;
+					}
+					
+					// Boundary match. If we've run out of input or we matched the entire final boundary marker, then this is the last part.
+					if(b>0||c==-1)
+					{
+					   
+						if(b==byteBoundary.length)
+							lastPart=true;
+						if(state==10)
+							state=-2;
+						break;
+					}
+					
+					// handle CR LF
+					if(cr)
+						part.write(13); 
 
-                    if(lf)
-                        part.write(10);
+					if(lf)
+						part.write(10);
 
-                    cr=(c==13);
-                    lf=(c==10||state==10);
-                    if(state==10)
-                        state=-2;
-                }
-            }
-            finally
-            {
-                part.close();
-            }
-        }
-        if (!lastPart)
-            throw new IOException("Incomplete parts");
-    }
-    
-    public void setDeleteOnExit(boolean deleteOnExit)
-    {
-        _deleteOnExit = deleteOnExit;
-    }
+					cr=(c==13);
+					lf=(c==10||state==10);
+					if(state==10)
+						state=-2;
+				}
+			}
+			finally
+			{
+				part.close();
+			}
+		}
+		if (!lastPart)
+			throw new IOException("Incomplete parts");
+	}
+	
+	public void setDeleteOnExit(boolean deleteOnExit)
+	{
+		_deleteOnExit = deleteOnExit;
+	}
 
 
-    public boolean isDeleteOnExit()
-    {
-        return _deleteOnExit;
-    }
+	public boolean isDeleteOnExit()
+	{
+		return _deleteOnExit;
+	}
 
 
-    /* ------------------------------------------------------------ */
-    private String value(String nameEqualsValue, boolean splitAfterSpace)
-    {
-        /*
-        String value=nameEqualsValue.substring(nameEqualsValue.indexOf('=')+1).trim();
-        int i=value.indexOf(';');
-        if(i>0)
-            value=value.substring(0,i);
-        if(value.startsWith("\""))
-        {
-            value=value.substring(1,value.indexOf('"',1));
-        }
-        else if (splitAfterSpace)
-        {
-            i=value.indexOf(' ');
-            if(i>0)
-                value=value.substring(0,i);
-        }
-        return value;
-        */
-         int idx = nameEqualsValue.indexOf('=');
-         String value = nameEqualsValue.substring(idx+1).trim();
-         return QuotedStringTokenizer.unquoteOnly(value);
-    }
-    
-    
-    /* ------------------------------------------------------------ */
-    private String filenameValue(String nameEqualsValue)
-    {
-        int idx = nameEqualsValue.indexOf('=');
-        String value = nameEqualsValue.substring(idx+1).trim();   
-
-        if (value.matches(".??[a-z,A-Z]\\:\\\\[^\\\\].*"))
-        {
-            //incorrectly escaped IE filenames that have the whole path
-            //we just strip any leading & trailing quotes and leave it as is
-            char first=value.charAt(0);
-            if (first=='"' || first=='\'')
-                value=value.substring(1);
-            char last=value.charAt(value.length()-1);
-            if (last=='"' || last=='\'')
-                value = value.substring(0,value.length()-1);
+	/* ------------------------------------------------------------ */
+	private String value(String nameEqualsValue, boolean splitAfterSpace)
+	{
+		/*
+		String value=nameEqualsValue.substring(nameEqualsValue.indexOf('=')+1).trim();
+		int i=value.indexOf(';');
+		if(i>0)
+			value=value.substring(0,i);
+		if(value.startsWith("\""))
+		{
+			value=value.substring(1,value.indexOf('"',1));
+		}
+		else if (splitAfterSpace)
+		{
+			i=value.indexOf(' ');
+			if(i>0)
+				value=value.substring(0,i);
+		}
+		return value;
+		*/
+		 int idx = nameEqualsValue.indexOf('=');
+		 String value = nameEqualsValue.substring(idx+1).trim();
+		 return QuotedStringTokenizer.unquoteOnly(value);
+	}
+	
+	
+	/* ------------------------------------------------------------ */
+	private String filenameValue(String nameEqualsValue)
+	{
+		int idx = nameEqualsValue.indexOf('=');
+		String value = nameEqualsValue.substring(idx+1).trim();   
 
-            return value;
-        }
-        else
-            //unquote the string, but allow any backslashes that don't
-            //form a valid escape sequence to remain as many browsers
-            //even on *nix systems will not escape a filename containing
-            //backslashes
-            return QuotedStringTokenizer.unquoteOnly(value, true);
-    }
-    
-    private static class Base64InputStream extends InputStream
-    {
-        ReadLineInputStream _in;
-        String _line;
-        byte[] _buffer;
-        int _pos;
-
-    
-        public Base64InputStream(ReadLineInputStream rlis)
-        {
-            _in = rlis;
-        }
+		if (value.matches(".??[a-z,A-Z]\\:\\\\[^\\\\].*"))
+		{
+			//incorrectly escaped IE filenames that have the whole path
+			//we just strip any leading & trailing quotes and leave it as is
+			char first=value.charAt(0);
+			if (first=='"' || first=='\'')
+				value=value.substring(1);
+			char last=value.charAt(value.length()-1);
+			if (last=='"' || last=='\'')
+				value = value.substring(0,value.length()-1);
 
-        @Override
-        public int read() throws IOException
-        {
-            if (_buffer==null || _pos>= _buffer.length)
-            {
-                //Any CR and LF will be consumed by the readLine() call.
-                //We need to put them back into the bytes returned from this
-                //method because the parsing of the multipart content uses them
-                //as markers to determine when we've reached the end of a part.
-                _line = _in.readLine(); 
-                if (_line==null)
-                    return -1;  //nothing left
-                if (_line.startsWith("--"))
-                    _buffer=(_line+"\r\n").getBytes(); //boundary marking end of part
-                else if (_line.length()==0)
-                    _buffer="\r\n".getBytes(); //blank line
-                else
-                {
-                    ByteArrayOutputStream baos = new ByteArrayOutputStream((4*_line.length()/3)+2);
-                    B64Code.decode(_line, baos);
-                    baos.write(13);
-                    baos.write(10);
-                    _buffer = baos.toByteArray();
-                }
-
-                _pos=0;
-            }
-            
-            return _buffer[_pos++];
-        }
-    }
+			return value;
+		}
+		else
+			//unquote the string, but allow any backslashes that don't
+			//form a valid escape sequence to remain as many browsers
+			//even on *nix systems will not escape a filename containing
+			//backslashes
+			return QuotedStringTokenizer.unquoteOnly(value, true);
+	}
 }
diff -r 90bf40ba1ec2 -r 7fb7c1915788 src/org/eclipse/jetty/util/resource/Resource.java
--- a/src/org/eclipse/jetty/util/resource/Resource.java	Wed Sep 14 15:49:23 2016 -0600
+++ b/src/org/eclipse/jetty/util/resource/Resource.java	Wed Sep 14 16:38:33 2016 -0600
@@ -31,7 +31,7 @@
 import java.util.Arrays;
 import java.util.Date;
 
-import org.eclipse.jetty.util.B64Code;
+import java.util.Base64;
 import org.eclipse.jetty.util.IO;
 import org.eclipse.jetty.util.Loader;
 import org.eclipse.jetty.util.StringUtil;
@@ -46,633 +46,626 @@
  */
 public abstract class Resource implements ResourceFactory
 {
-    private static final Logger LOG = LoggerFactory.getLogger(Resource.class);
-    public static boolean __defaultUseCaches = true;
-    volatile Object _associate;
+	private static final Logger LOG = LoggerFactory.getLogger(Resource.class);
+	public static boolean __defaultUseCaches = true;
+	volatile Object _associate;
 
-    /* ------------------------------------------------------------ */
-    /**
-     * Change the default setting for url connection caches.
-     * Subsequent URLConnections will use this default.
-     * @param useCaches
-     */
-    public static void setDefaultUseCaches (boolean useCaches)
-    {
-        __defaultUseCaches=useCaches;
-    }
+	/* ------------------------------------------------------------ */
+	/**
+	 * Change the default setting for url connection caches.
+	 * Subsequent URLConnections will use this default.
+	 * @param useCaches
+	 */
+	public static void setDefaultUseCaches (boolean useCaches)
+	{
+		__defaultUseCaches=useCaches;
+	}
 
-    /* ------------------------------------------------------------ */
-    public static boolean getDefaultUseCaches ()
-    {
-        return __defaultUseCaches;
-    }
-    
-    /* ------------------------------------------------------------ */
-    /** Construct a resource from a uri.
-     * @param uri A URI.
-     * @return A Resource object.
-     * @throws IOException Problem accessing URI
-     */
-    public static Resource newResource(URI uri)
-        throws IOException
-    {
-        return newResource(uri.toURL());
-    }
-    
-    /* ------------------------------------------------------------ */
-    /** Construct a resource from a url.
-     * @param url A URL.
-     * @return A Resource object.
-     * @throws IOException Problem accessing URL
-     */
-    public static Resource newResource(URL url)
-        throws IOException
-    {
-        return newResource(url, __defaultUseCaches);
-    }
-    
-    /* ------------------------------------------------------------ */   
-    /**
-     * Construct a resource from a url.
-     * @param url the url for which to make the resource
-     * @param useCaches true enables URLConnection caching if applicable to the type of resource
-     * @return
-     */
-    static Resource newResource(URL url, boolean useCaches)
-    {
-        if (url==null)
-            return null;
+	/* ------------------------------------------------------------ */
+	public static boolean getDefaultUseCaches ()
+	{
+		return __defaultUseCaches;
+	}
+	
+	/* ------------------------------------------------------------ */
+	/** Construct a resource from a uri.
+	 * @param uri A URI.
+	 * @return A Resource object.
+	 * @throws IOException Problem accessing URI
+	 */
+	public static Resource newResource(URI uri)
+		throws IOException
+	{
+		return newResource(uri.toURL());
+	}
+	
+	/* ------------------------------------------------------------ */
+	/** Construct a resource from a url.
+	 * @param url A URL.
+	 * @return A Resource object.
+	 * @throws IOException Problem accessing URL
+	 */
+	public static Resource newResource(URL url)
+		throws IOException
+	{
+		return newResource(url, __defaultUseCaches);
+	}
+	
+	/* ------------------------------------------------------------ */   
+	/**
+	 * Construct a resource from a url.
+	 * @param url the url for which to make the resource
+	 * @param useCaches true enables URLConnection caching if applicable to the type of resource
+	 * @return
+	 */
+	static Resource newResource(URL url, boolean useCaches)
+	{
+		if (url==null)
+			return null;
 
-        String url_string=url.toExternalForm();
-        if( url_string.startsWith( "file:"))
-        {
-            try
-            {
-                FileResource fileResource= new FileResource(url);
-                return fileResource;
-            }
-            catch(Exception e)
-            {
-                LOG.debug("EXCEPTION",e);
-                return new BadResource(url,e.toString());
-            }
-        }
-        else if( url_string.startsWith( "jar:file:"))
-        {
-            return new JarFileResource(url, useCaches);
-        }
-        else if( url_string.startsWith( "jar:"))
-        {
-            return new JarResource(url, useCaches);
-        }
+		String url_string=url.toExternalForm();
+		if( url_string.startsWith( "file:"))
+		{
+			try
+			{
+				FileResource fileResource= new FileResource(url);
+				return fileResource;
+			}
+			catch(Exception e)
+			{
+				LOG.debug("EXCEPTION",e);
+				return new BadResource(url,e.toString());
+			}
+		}
+		else if( url_string.startsWith( "jar:file:"))
+		{
+			return new JarFileResource(url, useCaches);
+		}
+		else if( url_string.startsWith( "jar:"))
+		{
+			return new JarResource(url, useCaches);
+		}
 
-        return new URLResource(url,null,useCaches);
-    }
+		return new URLResource(url,null,useCaches);
+	}
 
-    
-    
-    /* ------------------------------------------------------------ */
-    /** Construct a resource from a string.
-     * @param resource A URL or filename.
-     * @return A Resource object.
-     */
-    public static Resource newResource(String resource)
-        throws MalformedURLException, IOException
-    {
-        return newResource(resource, __defaultUseCaches);
-    }
-    
-    /* ------------------------------------------------------------ */
-    /** Construct a resource from a string.
-     * @param resource A URL or filename.
-     * @param useCaches controls URLConnection caching
-     * @return A Resource object.
-     */
-    public static Resource newResource (String resource, boolean useCaches)       
-    throws MalformedURLException, IOException
-    {
-        URL url=null;
-        try
-        {
-            // Try to format as a URL?
-            url = new URL(resource);
-        }
-        catch(MalformedURLException e)
-        {
-            if(!resource.startsWith("ftp:") &&
-               !resource.startsWith("file:") &&
-               !resource.startsWith("jar:"))
-            {
-                try
-                {
-                    // It's a file.
-                    if (resource.startsWith("./"))
-                        resource=resource.substring(2);
-                    
-                    File file=new File(resource).getCanonicalFile();
-                    url=Resource.toURL(file);            
-                    
-                    URLConnection connection=url.openConnection();
-                    connection.setUseCaches(useCaches);
-                    return new FileResource(url,connection,file);
-                }
-                catch(Exception e2)
-                {
-                    LOG.debug("EXCEPTION",e2);
-                    throw e;
-                }
-            }
-            else
-            {
-                LOG.warn("Bad Resource: "+resource);
-                throw e;
-            }
-        }
+	
+	
+	/* ------------------------------------------------------------ */
+	/** Construct a resource from a string.
+	 * @param resource A URL or filename.
+	 * @return A Resource object.
+	 */
+	public static Resource newResource(String resource)
+		throws MalformedURLException, IOException
+	{
+		return newResource(resource, __defaultUseCaches);
+	}
+	
+	/* ------------------------------------------------------------ */
+	/** Construct a resource from a string.
+	 * @param resource A URL or filename.
+	 * @param useCaches controls URLConnection caching
+	 * @return A Resource object.
+	 */
+	public static Resource newResource (String resource, boolean useCaches)       
+	throws MalformedURLException, IOException
+	{
+		URL url=null;
+		try
+		{
+			// Try to format as a URL?
+			url = new URL(resource);
+		}
+		catch(MalformedURLException e)
+		{
+			if(!resource.startsWith("ftp:") &&
+			   !resource.startsWith("file:") &&
+			   !resource.startsWith("jar:"))
+			{
+				try
+				{
+					// It's a file.
+					if (resource.startsWith("./"))
+						resource=resource.substring(2);
+					
+					File file=new File(resource).getCanonicalFile();
+					url=Resource.toURL(file);            
+					
+					URLConnection connection=url.openConnection();
+					connection.setUseCaches(useCaches);
+					return new FileResource(url,connection,file);
+				}
+				catch(Exception e2)
+				{
+					LOG.debug("EXCEPTION",e2);
+					throw e;
+				}
+			}
+			else
+			{
+				LOG.warn("Bad Resource: "+resource);
+				throw e;
+			}
+		}
 
-        return newResource(url);
-    }
+		return newResource(url);
+	}
 
-    /* ------------------------------------------------------------ */
-    public static Resource newResource (File file)
-    throws MalformedURLException, IOException
-    {
-        file = file.getCanonicalFile();
-        URL url = Resource.toURL(file);
+	/* ------------------------------------------------------------ */
+	public static Resource newResource (File file)
+	throws MalformedURLException, IOException
+	{
+		file = file.getCanonicalFile();
+		URL url = Resource.toURL(file);
 
-        URLConnection connection = url.openConnection();
-        FileResource fileResource = new FileResource(url, connection, file);
-        return fileResource;
-    }
+		URLConnection connection = url.openConnection();
+		FileResource fileResource = new FileResource(url, connection, file);
+		return fileResource;
+	}
 
-    /* ------------------------------------------------------------ */
-    /** Construct a system resource from a string.
-     * The resource is tried as classloader resource before being
-     * treated as a normal resource.
-     * @param resource Resource as string representation 
-     * @return The new Resource
-     * @throws IOException Problem accessing resource.
-     */
-    public static Resource newSystemResource(String resource)
-        throws IOException
-    {
-        URL url=null;
-        // Try to format as a URL?
-        ClassLoader loader=Thread.currentThread().getContextClassLoader();
-        if (loader!=null)
-        {
-            try
-            {
-                url = loader.getResource(resource);
-                if (url == null && resource.startsWith("/"))
-                    url = loader.getResource(resource.substring(1));
-            }
-            catch (IllegalArgumentException e)
-            {
-                // Catches scenario where a bad Windows path like "C:\dev" is
-                // improperly escaped, which various downstream classloaders
-                // tend to have a problem with
-                url = null;
-            }
-        }
-        if (url==null)
-        {
-            loader=Resource.class.getClassLoader();
-            if (loader!=null)
-            {
-                url=loader.getResource(resource);
-                if (url==null && resource.startsWith("/"))
-                    url=loader.getResource(resource.substring(1));
-            }
-        }
-        
-        if (url==null)
-        {
-            url=ClassLoader.getSystemResource(resource);
-            if (url==null && resource.startsWith("/"))
-                url=ClassLoader.getSystemResource(resource.substring(1));
-        }
-        
-        if (url==null)
-            return null;
-        
-        return newResource(url);
-    }
+	/* ------------------------------------------------------------ */
+	/** Construct a system resource from a string.
+	 * The resource is tried as classloader resource before being
+	 * treated as a normal resource.
+	 * @param resource Resource as string representation 
+	 * @return The new Resource
+	 * @throws IOException Problem accessing resource.
+	 */
+	public static Resource newSystemResource(String resource)
+		throws IOException
+	{
+		URL url=null;
+		// Try to format as a URL?
+		ClassLoader loader=Thread.currentThread().getContextClassLoader();
+		if (loader!=null)
+		{
+			try
+			{
+				url = loader.getResource(resource);
+				if (url == null && resource.startsWith("/"))
+					url = loader.getResource(resource.substring(1));
+			}
+			catch (IllegalArgumentException e)
+			{
+				// Catches scenario where a bad Windows path like "C:\dev" is
+				// improperly escaped, which various downstream classloaders
+				// tend to have a problem with
+				url = null;
+			}
+		}
+		if (url==null)
+		{
+			loader=Resource.class.getClassLoader();
+			if (loader!=null)
+			{
+				url=loader.getResource(resource);
+				if (url==null && resource.startsWith("/"))
+					url=loader.getResource(resource.substring(1));
+			}
+		}
+		
+		if (url==null)
+		{
+			url=ClassLoader.getSystemResource(resource);
+			if (url==null && resource.startsWith("/"))
+				url=ClassLoader.getSystemResource(resource.substring(1));
+		}
+		
+		if (url==null)
+			return null;
+		
+		return newResource(url);
+	}
 
-    /* ------------------------------------------------------------ */
-    /** Find a classpath resource.
-     */
-    public static Resource newClassPathResource(String resource)
-    {
-        return newClassPathResource(resource,true,false);
-    }
+	/* ------------------------------------------------------------ */
+	/** Find a classpath resource.
+	 */
+	public static Resource newClassPathResource(String resource)
+	{
+		return newClassPathResource(resource,true,false);
+	}
 
-    /* ------------------------------------------------------------ */
-    /** Find a classpath resource.
-     * The {@link java.lang.Class#getResource(String)} method is used to lookup the resource. If it is not
-     * found, then the {@link Loader#getResource(Class, String, boolean)} method is used.
-     * If it is still not found, then {@link ClassLoader#getSystemResource(String)} is used.
-     * Unlike {@link ClassLoader#getSystemResource(String)} this method does not check for normal resources.
-     * @param name The relative name of the resource
-     * @param useCaches True if URL caches are to be used.
-     * @param checkParents True if forced searching of parent Classloaders is performed to work around 
-     * loaders with inverted priorities
-     * @return Resource or null
-     */
-    public static Resource newClassPathResource(String name,boolean useCaches,boolean checkParents)
-    {
-        URL url=Resource.class.getResource(name);
-        
-        if (url==null)
-            url=Loader.getResource(Resource.class,name,checkParents);
-        if (url==null)
-            return null;
-        return newResource(url,useCaches);
-    }
-    
-    /* ------------------------------------------------------------ */
-    public static boolean isContainedIn (Resource r, Resource containingResource) throws MalformedURLException
-    {
-        return r.isContainedIn(containingResource);
-    }
+	/* ------------------------------------------------------------ */
+	/** Find a classpath resource.
+	 * The {@link java.lang.Class#getResource(String)} method is used to lookup the resource. If it is not
+	 * found, then the {@link Loader#getResource(Class, String, boolean)} method is used.
+	 * If it is still not found, then {@link ClassLoader#getSystemResource(String)} is used.
+	 * Unlike {@link ClassLoader#getSystemResource(String)} this method does not check for normal resources.
+	 * @param name The relative name of the resource
+	 * @param useCaches True if URL caches are to be used.
+	 * @param checkParents True if forced searching of parent Classloaders is performed to work around 
+	 * loaders with inverted priorities
+	 * @return Resource or null
+	 */
+	public static Resource newClassPathResource(String name,boolean useCaches,boolean checkParents)
+	{
+		URL url=Resource.class.getResource(name);
+		
+		if (url==null)
+			url=Loader.getResource(Resource.class,name,checkParents);
+		if (url==null)
+			return null;
+		return newResource(url,useCaches);
+	}
+	
+	/* ------------------------------------------------------------ */
+	public static boolean isContainedIn (Resource r, Resource containingResource) throws MalformedURLException
+	{
+		return r.isContainedIn(containingResource);
+	}
 
-    /* ------------------------------------------------------------ */
-    @Override
-    protected void finalize()
-    {
-        release();
-    }
-    
-    /* ------------------------------------------------------------ */
-    public abstract boolean isContainedIn (Resource r) throws MalformedURLException;
-    
-    
-    /* ------------------------------------------------------------ */
-    /** Release any temporary resources held by the resource.
-     */
-    public abstract void release();
-    
+	/* ------------------------------------------------------------ */
+	@Override
+	protected void finalize()
+	{
+		release();
+	}
+	
+	/* ------------------------------------------------------------ */
+	public abstract boolean isContainedIn (Resource r) throws MalformedURLException;
+	
+	
+	/* ------------------------------------------------------------ */
+	/** Release any temporary resources held by the resource.
+	 */
+	public abstract void release();
+	
 
-    /* ------------------------------------------------------------ */
-    /**
-     * Returns true if the respresened resource exists.
-     */
-    public abstract boolean exists();
-    
+	/* ------------------------------------------------------------ */
+	/**
+	 * Returns true if the respresened resource exists.
+	 */
+	public abstract boolean exists();
+	
 
-    /* ------------------------------------------------------------ */
-    /**
-     * Returns true if the respresenetd resource is a container/directory.
-     * If the resource is not a file, resources ending with "/" are
-     * considered directories.
-     */
-    public abstract boolean isDirectory();
+	/* ------------------------------------------------------------ */
+	/**
+	 * Returns true if the respresenetd resource is a container/directory.
+	 * If the resource is not a file, resources ending with "/" are
+	 * considered directories.
+	 */
+	public abstract boolean isDirectory();
 
-    /* ------------------------------------------------------------ */
-    /**
-     * Returns the last modified time
-     */
-    public abstract long lastModified();
+	/* ------------------------------------------------------------ */
+	/**
+	 * Returns the last modified time
+	 */
+	public abstract long lastModified();
 
 
-    /* ------------------------------------------------------------ */
-    /**
-     * Return the length of the resource
-     */
-    public abstract long length();
-    
+	/* ------------------------------------------------------------ */
+	/**
+	 * Return the length of the resource
+	 */
+	public abstract long length();
+	
 
-    /* ------------------------------------------------------------ */
-    /**
-     * Returns an URL representing the given resource
-     */
-    public abstract URL getURL();
+	/* ------------------------------------------------------------ */
+	/**
+	 * Returns an URL representing the given resource
+	 */
+	public abstract URL getURL();
 
-    /* ------------------------------------------------------------ */
-    /**
-     * Returns an URI representing the given resource
-     */
-    public URI getURI()
-    {
-        try
-        {
-            return getURL().toURI();
-        }
-        catch(Exception e)
-        {
-            throw new RuntimeException(e);
-        }
-    }
-    
+	/* ------------------------------------------------------------ */
+	/**
+	 * Returns an URI representing the given resource
+	 */
+	public URI getURI()
+	{
+		try
+		{
+			return getURL().toURI();
+		}
+		catch(Exception e)
+		{
+			throw new RuntimeException(e);
+		}
+	}
+	
 
-    /* ------------------------------------------------------------ */
-    /**
-     * Returns an File representing the given resource or NULL if this
-     * is not possible.
-     */
-    public abstract File getFile()
-        throws IOException;
-    
+	/* ------------------------------------------------------------ */
+	/**
+	 * Returns an File representing the given resource or NULL if this
+	 * is not possible.
+	 */
+	public abstract File getFile()
+		throws IOException;
+	
 
-    /* ------------------------------------------------------------ */
-    /**
-     * Returns the name of the resource
-     */
-    public abstract String getName();
-    
+	/* ------------------------------------------------------------ */
+	/**
+	 * Returns the name of the resource
+	 */
+	public abstract String getName();
+	
 
-    /* ------------------------------------------------------------ */
-    /**
-     * Returns an input stream to the resource
-     */
-    public abstract InputStream getInputStream()
-        throws java.io.IOException;
+	/* ------------------------------------------------------------ */
+	/**
+	 * Returns an input stream to the resource
+	 */
+	public abstract InputStream getInputStream()
+		throws java.io.IOException;
 
-    /* ------------------------------------------------------------ */
-    /**
-     * Returns an output stream to the resource
-     */
-    public abstract OutputStream getOutputStream()
-        throws java.io.IOException, SecurityException;
-    
-    /* ------------------------------------------------------------ */
-    /**
-     * Deletes the given resource
-     */
-    public abstract boolean delete()
-        throws SecurityException;
-    
-    /* ------------------------------------------------------------ */
-    /**
-     * Rename the given resource
-     */
-    public abstract boolean renameTo( Resource dest)
-        throws SecurityException;
-    
-    /* ------------------------------------------------------------ */
-    /**
-     * Returns a list of resource names contained in the given resource
-     * The resource names are not URL encoded.
-     */
-    public abstract String[] list();
+	/* ------------------------------------------------------------ */
+	/**
+	 * Returns an output stream to the resource
+	 */
+	public abstract OutputStream getOutputStream()
+		throws java.io.IOException, SecurityException;
+	
+	/* ------------------------------------------------------------ */
+	/**
+	 * Deletes the given resource
+	 */
+	public abstract boolean delete()
+		throws SecurityException;
+	
+	/* ------------------------------------------------------------ */
+	/**
+	 * Rename the given resource
+	 */
+	public abstract boolean renameTo( Resource dest)
+		throws SecurityException;
+	
+	/* ------------------------------------------------------------ */
+	/**
+	 * Returns a list of resource names contained in the given resource
+	 * The resource names are not URL encoded.
+	 */
+	public abstract String[] list();
 
-    /* ------------------------------------------------------------ */
-    /**
-     * Returns the resource contained inside the current resource with the
-     * given name.
-     * @param path The path segment to add, which should be encoded by the
-     * encode method. 
-     */
-    public abstract Resource addPath(String path)
-        throws IOException,MalformedURLException;
+	/* ------------------------------------------------------------ */
+	/**
+	 * Returns the resource contained inside the current resource with the
+	 * given name.
+	 * @param path The path segment to add, which should be encoded by the
+	 * encode method. 
+	 */
+	public abstract Resource addPath(String path)
+		throws IOException,MalformedURLException;
 
-    /* ------------------------------------------------------------ */
-    /** Get a resource from withing this resource.
-     * <p>
-     * This method is essentially an alias for {@link #addPath(String)}, but without checked exceptions.
-     * This method satisfied the {@link ResourceFactory} interface.
-     * @see org.eclipse.jetty.util.resource.ResourceFactory#getResource(java.lang.String)
-     */
-    public Resource getResource(String path)
-    {
-        try
-        {
-            return addPath(path);
-        }
-        catch(Exception e)
-        {
-            LOG.debug("",e);
-            return null;
-        }
-    }
+	/* ------------------------------------------------------------ */
+	/** Get a resource from withing this resource.
+	 * <p>
+	 * This method is essentially an alias for {@link #addPath(String)}, but without checked exceptions.
+	 * This method satisfied the {@link ResourceFactory} interface.
+	 * @see org.eclipse.jetty.util.resource.ResourceFactory#getResource(java.lang.String)
+	 */
+	public Resource getResource(String path)
+	{
+		try
+		{
+			return addPath(path);
+		}
+		catch(Exception e)
+		{
+			LOG.debug("",e);
+			return null;
+		}
+	}
 
-    /* ------------------------------------------------------------ */
-    /** Encode according to this resource type.
-     * The default implementation calls URI.encodePath(uri)
-     * @param uri 
-     * @return String encoded for this resource type.
-     */
-    public String encode(String uri)
-    {
-        return URIUtil.encodePath(uri);
-    }
-        
-    /* ------------------------------------------------------------ */
-    public Object getAssociate()
-    {
-        return _associate;
-    }
+	/* ------------------------------------------------------------ */
+	/** Encode according to this resource type.
+	 * The default implementation calls URI.encodePath(uri)
+	 * @param uri 
+	 * @return String encoded for this resource type.
+	 */
+	public String encode(String uri)
+	{
+		return URIUtil.encodePath(uri);
+	}
+		
+	/* ------------------------------------------------------------ */
+	public Object getAssociate()
+	{
+		return _associate;
+	}
 
-    /* ------------------------------------------------------------ */
-    public void setAssociate(Object o)
-    {
-        _associate=o;
-    }
-    
-    /* ------------------------------------------------------------ */
-    /**
-     * @return The canonical Alias of this resource or null if none.
-     */
-    public URL getAlias()
-    {
-        return null;
-    }
-    
-    /* ------------------------------------------------------------ */
-    /** Get the resource list as a HTML directory listing.
-     * @param base The base URL
-     * @param parent True if the parent directory should be included
-     * @return String of HTML
-     */
-    public String getListHTML(String base,boolean parent)
-        throws IOException
-    {
-        base=URIUtil.canonicalPath(base);
-        if (base==null || !isDirectory())
-            return null;
-        
-        String[] ls = list();
-        if (ls==null)
-            return null;
-        Arrays.sort(ls);
-        
-        String decodedBase = URIUtil.decodePath(base);
-        String title = "Directory: "+deTag(decodedBase);
+	/* ------------------------------------------------------------ */
+	public void setAssociate(Object o)
+	{
+		_associate=o;
+	}
+	
+	/* ------------------------------------------------------------ */
+	/**
+	 * @return The canonical Alias of this resource or null if none.
+	 */
+	public URL getAlias()
+	{
+		return null;
+	}
+	
+	/* ------------------------------------------------------------ */
+	/** Get the resource list as a HTML directory listing.
+	 * @param base The base URL
+	 * @param parent True if the parent directory should be included
+	 * @return String of HTML
+	 */
+	public String getListHTML(String base,boolean parent)
+		throws IOException
+	{
+		base=URIUtil.canonicalPath(base);
+		if (base==null || !isDirectory())
+			return null;
+		
+		String[] ls = list();
+		if (ls==null)
+			return null;
+		Arrays.sort(ls);
+		
+		String decodedBase = URIUtil.decodePath(base);
+		String title = "Directory: "+deTag(decodedBase);
 
-        StringBuilder buf=new StringBuilder(4096);
-        buf.append("<HTML><HEAD>");
-        buf.append("<LINK HREF=\"").append("jetty-dir.css").append("\" REL=\"stylesheet\" TYPE=\"text/css\"/><TITLE>");
-        buf.append(title);
-        buf.append("</TITLE></HEAD><BODY>\n<H1>");
-        buf.append(title);
-        buf.append("</H1>\n<TABLE BORDER=0>\n");
-        
-        if (parent)
-        {
-            buf.append("<TR><TD><A HREF=\"");
-            buf.append(URIUtil.addPaths(base,"../"));
-            buf.append("\">Parent Directory</A></TD><TD></TD><TD></TD></TR>\n");
-        }
-        
-        String encodedBase = hrefEncodeURI(base);
-        
-        DateFormat dfmt=DateFormat.getDateTimeInstance(DateFormat.MEDIUM,
-                                                       DateFormat.MEDIUM);
-        for (int i=0 ; i< ls.length ; i++)
-        {
-            Resource item = addPath(ls[i]);
-            
-            buf.append("\n<TR><TD><A HREF=\"");
-            String path=URIUtil.addPaths(encodedBase,URIUtil.encodePath(ls[i]));
-            
-            buf.append(path);
-            
-            if (item.isDirectory() && !path.endsWith("/"))
-                buf.append(URIUtil.SLASH);
-            
-            // URIUtil.encodePath(buf,path);
-            buf.append("\">");
-            buf.append(deTag(ls[i]));
-            buf.append("&nbsp;");
-            buf.append("</A></TD><TD ALIGN=right>");
-            buf.append(item.length());
-            buf.append(" bytes&nbsp;</TD><TD>");
-            buf.append(dfmt.format(new Date(item.lastModified())));
-            buf.append("</TD></TR>");
-        }
-        buf.append("</TABLE>\n");
+		StringBuilder buf=new StringBuilder(4096);
+		buf.append("<HTML><HEAD>");
+		buf.append("<LINK HREF=\"").append("jetty-dir.css").append("\" REL=\"stylesheet\" TYPE=\"text/css\"/><TITLE>");
+		buf.append(title);
+		buf.append("</TITLE></HEAD><BODY>\n<H1>");
+		buf.append(title);
+		buf.append("</H1>\n<TABLE BORDER=0>\n");
+		
+		if (parent)
+		{
+			buf.append("<TR><TD><A HREF=\"");
+			buf.append(URIUtil.addPaths(base,"../"));
+			buf.append("\">Parent Directory</A></TD><TD></TD><TD></TD></TR>\n");
+		}
+		
+		String encodedBase = hrefEncodeURI(base);
+		
+		DateFormat dfmt=DateFormat.getDateTimeInstance(DateFormat.MEDIUM,
+													   DateFormat.MEDIUM);
+		for (int i=0 ; i< ls.length ; i++)
+		{
+			Resource item = addPath(ls[i]);
+			
+			buf.append("\n<TR><TD><A HREF=\"");
+			String path=URIUtil.addPaths(encodedBase,URIUtil.encodePath(ls[i]));
+			
+			buf.append(path);
+			
+			if (item.isDirectory() && !path.endsWith("/"))
+				buf.append(URIUtil.SLASH);
+			
+			// URIUtil.encodePath(buf,path);
+			buf.append("\">");
+			buf.append(deTag(ls[i]));
+			buf.append("&nbsp;");
+			buf.append("</A></TD><TD ALIGN=right>");
+			buf.append(item.length());
+			buf.append(" bytes&nbsp;</TD><TD>");
+			buf.append(dfmt.format(new Date(item.lastModified())));
+			buf.append("</TD></TR>");
+		}
+		buf.append("</TABLE>\n");
 	buf.append("</BODY></HTML>\n");
-        
-        return buf.toString();
-    }
-    
-    /**
-     * Encode any characters that could break the URI string in an HREF.
-     * Such as <a href="/path/to;<script>Window.alert("XSS"+'%20'+"here");</script>">Link</a>
-     * 
-     * The above example would parse incorrectly on various browsers as the "<" or '"' characters
-     * would end the href attribute value string prematurely.
-     * 
-     * @param raw the raw text to encode.
-     * @return the defanged text.
-     */
-    private static String hrefEncodeURI(String raw) 
-    {
-        StringBuffer buf = null;
+		
+		return buf.toString();
+	}
+	
+	/**
+	 * Encode any characters that could break the URI string in an HREF.
+	 * Such as <a href="/path/to;<script>Window.alert("XSS"+'%20'+"here");</script>">Link</a>
+	 * 
+	 * The above example would parse incorrectly on various browsers as the "<" or '"' characters
+	 * would end the href attribute value string prematurely.
+	 * 
+	 * @param raw the raw text to encode.
+	 * @return the defanged text.
+	 */
+	private static String hrefEncodeURI(String raw) 
+	{
+		StringBuffer buf = null;
 
-        loop:
-        for (int i=0;i<raw.length();i++)
-        {
-            char c=raw.charAt(i);
-            switch(c)
-            {
-                case '\'':
-                case '"':
-                case '<':
-                case '>':
-                    buf=new StringBuffer(raw.length()<<1);
-                    break loop;
-            }
-        }
-        if (buf==null)
-            return raw;
+		loop:
+		for (int i=0;i<raw.length();i++)
+		{
+			char c=raw.charAt(i);
+			switch(c)
+			{
+				case '\'':
+				case '"':
+				case '<':
+				case '>':
+					buf=new StringBuffer(raw.length()<<1);
+					break loop;
+			}
+		}
+		if (buf==null)
+			return raw;
 
-        for (int i=0;i<raw.length();i++)
-        {
-            char c=raw.charAt(i);       
-            switch(c)
-            {
-              case '"':
-                  buf.append("%22");
-                  continue;
-              case '\'':
-                  buf.append("%27");
-                  continue;
-              case '<':
-                  buf.append("%3C");
-                  continue;
-              case '>':
-                  buf.append("%3E");
-                  continue;
-              default:
-                  buf.append(c);
-                  continue;
-            }
-        }
+		for (int i=0;i<raw.length();i++)
+		{
+			char c=raw.charAt(i);       
+			switch(c)
+			{
+			  case '"':
+				  buf.append("%22");
+				  continue;
+			  case '\'':
+				  buf.append("%27");
+				  continue;
+			  case '<':
+				  buf.append("%3C");
+				  continue;
+			  case '>':
+				  buf.append("%3E");
+				  continue;
+			  default:
+				  buf.append(c);
+				  continue;
+			}
+		}
 
-        return buf.toString();
-    }
-    
-    private static String deTag(String raw) 
-    {
-        return StringUtil.replace( StringUtil.replace(raw,"<","&lt;"), ">", "&gt;");
-    }
-    
-    /* ------------------------------------------------------------ */
-    /** 
-     * @param out 
-     * @param start First byte to write
-     * @param count Bytes to write or -1 for all of them.
-     */
-    public void writeTo(OutputStream out,long start,long count)
-        throws IOException
-    {
-        InputStream in = getInputStream();
-        try
-        {
-            in.skip(start);
-            if (count<0)
-                IO.copy(in,out);
-            else
-                IO.copy(in,out,count);
-        }
-        finally
-        {
-            in.close();
-        }
-    }    
-    
-    /* ------------------------------------------------------------ */
-    public void copyTo(File destination)
-        throws IOException
-    {
-        if (destination.exists())
-            throw new IllegalArgumentException(destination+" exists");
-        writeTo(new FileOutputStream(destination),0,-1);
-    }
+		return buf.toString();
+	}
+	
+	private static String deTag(String raw) 
+	{
+		return StringUtil.replace( StringUtil.replace(raw,"<","&lt;"), ">", "&gt;");
+	}
+	
+	/* ------------------------------------------------------------ */
+	/** 
+	 * @param out 
+	 * @param start First byte to write
+	 * @param count Bytes to write or -1 for all of them.
+	 */
+	public void writeTo(OutputStream out,long start,long count)
+		throws IOException
+	{
+		InputStream in = getInputStream();
+		try
+		{
+			in.skip(start);
+			if (count<0)
+				IO.copy(in,out);
+			else
+				IO.copy(in,out,count);
+		}
+		finally
+		{
+			in.close();
+		}
+	}    
+	
+	/* ------------------------------------------------------------ */
+	public void copyTo(File destination)
+		throws IOException
+	{
+		if (destination.exists())
+			throw new IllegalArgumentException(destination+" exists");
+		writeTo(new FileOutputStream(destination),0,-1);
+	}
 
-    /* ------------------------------------------------------------ */
-    public String getWeakETag()
-    {
-        try
-        {
-            StringBuilder b = new StringBuilder(32);
-            b.append("W/\"");
-            
-            String name=getName();
-            int length=name.length();
-            long lhash=0;
-            for (int i=0; i<length;i++)
-                lhash=31*lhash+name.charAt(i);
-            
-            B64Code.encode(lastModified()^lhash,b);
-            B64Code.encode(length()^lhash,b);
-            b.append('"');
-            return b.toString();
-        } 
-        catch (IOException e)
-        {
-            throw new RuntimeException(e);
-        }
-    }
-    
-    /* ------------------------------------------------------------ */
-    /** Generate a properly encoded URL from a {@link File} instance.
-     * @param file Target file. 
-     * @return URL of the target file.
-     * @throws MalformedURLException 
-     */
-    public static URL toURL(File file) throws MalformedURLException
-    {
-        return file.toURI().toURL();
-    }
+	/* ------------------------------------------------------------ */
+	public String getWeakETag()
+	{
+		StringBuilder b = new StringBuilder(32);
+		b.append("W/\"");
+		
+		long lhash = lastModified() ^ getName().hashCode() ^ length();
+		byte[] a = new byte[Long.BYTES];
+		for( int i=0; i<a.length; i++ ) {
+			a[i] = (byte)lhash;
+			lhash >>= 8;
+		}
+		b.append( Base64.getEncoder().encodeToString(a) );
+
+		b.append('"');
+		return b.toString();
+	}
+	
+	/* ------------------------------------------------------------ */
+	/** Generate a properly encoded URL from a {@link File} instance.
+	 * @param file Target file. 
+	 * @return URL of the target file.
+	 * @throws MalformedURLException 
+	 */
+	public static URL toURL(File file) throws MalformedURLException
+	{
+		return file.toURI().toURL();
+	}
 }