diff src/org/eclipse/jetty/util/MultiPartInputStream.java @ 825:7fb7c1915788

remove jetty.util.B64Code
author Franklin Schmidt <fschmidt@gmail.com>
date Wed, 14 Sep 2016 16:38:33 -0600
parents 8e9db0bbf4f9
children 3dcc52e17535
line wrap: on
line diff
--- 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);
+	}
 }