Jetty: Refactor (rather than remove) RequestWrapper to use Servlet 3.0 API.

Remove old org.mortbay helper classes copied from Jetty 5, saving about 24 KB.
Large attachments will now be written to temp files. (ticket #2109)
This commit is contained in:
zzz
2017-12-04 16:08:03 +00:00
parent 14941d0dda
commit c299976165
10 changed files with 118 additions and 1424 deletions

View File

@ -30,12 +30,21 @@ import java.util.Hashtable;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
import javax.servlet.http.Part;
import org.mortbay.servlet.MultiPartRequest; import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.util.Log;
/** /**
* Refactored in 0.9.33 to use Servlet 3.0 API and remove dependency
* on old Jetty 5 MultiPartRequest code. See ticket 2109.
*
* Previous history:
*
* Required major changes for Jetty 6 * Required major changes for Jetty 6
* to support change from MultiPartRequest to MultiPartFilter. * to support change from MultiPartRequest to MultiPartFilter.
* See http://docs.codehaus.org/display/JETTY/File+Upload+in+jetty6 * See http://docs.codehaus.org/display/JETTY/File+Upload+in+jetty6
@ -52,15 +61,14 @@ import org.mortbay.servlet.MultiPartRequest;
* *
* @author user * @author user
* @since 0.9.19 moved from susimail so it may be used by routerconsole too * @since 0.9.19 moved from susimail so it may be used by routerconsole too
* @deprecated scheduled for removal in 0.9.34, see ticket #2109
*/ */
@Deprecated
public class RequestWrapper { public class RequestWrapper {
private final HttpServletRequest httpRequest; private final HttpServletRequest httpRequest;
private final MultiPartRequest multiPartRequest; private final boolean isMultiPartRequest;
private final Hashtable<String, String> cache; private final Hashtable<String, String> cache;
private Hashtable<String, Integer> cachedParameterNames; private Hashtable<String, Integer> cachedParameterNames;
private static final int MAX_STRING_SIZE = 64*1024;
/** /**
* @param httpRequest * @param httpRequest
@ -69,19 +77,7 @@ public class RequestWrapper {
cache = new Hashtable<String, String>(); cache = new Hashtable<String, String>();
this.httpRequest = httpRequest; this.httpRequest = httpRequest;
String contentType = httpRequest.getContentType(); String contentType = httpRequest.getContentType();
MultiPartRequest mpr = null; isMultiPartRequest = contentType != null && contentType.toLowerCase(Locale.US).startsWith("multipart/form-data");
if( contentType != null && contentType.toLowerCase(Locale.US).startsWith( "multipart/form-data" ) ) {
try {
mpr = new MultiPartRequest( httpRequest );
} catch (OutOfMemoryError oome) {
// TODO Throw ioe from constructor?
oome.printStackTrace();
} catch (IOException e) {
// TODO Throw ioe from constructor?
e.printStackTrace();
}
}
multiPartRequest = mpr;
} }
/** /**
@ -106,20 +102,28 @@ public class RequestWrapper {
/** /**
* @return List of request parameter names * @return List of request parameter names
*/ */
@SuppressWarnings("unchecked") // TODO-Java6: Remove, type is correct
public Enumeration<String> getParameterNames() { public Enumeration<String> getParameterNames() {
if( multiPartRequest != null ) { if (isMultiPartRequest) {
if( cachedParameterNames == null ) { if( cachedParameterNames == null ) {
cachedParameterNames = new Hashtable<String, Integer>(); cachedParameterNames = new Hashtable<String, Integer>();
String[] partNames = multiPartRequest.getPartNames(); try {
for( int i = 0; i < partNames.length; i++ ) Integer DUMMY = Integer.valueOf(0);
cachedParameterNames.put( partNames[i], Integer.valueOf( i ) ); for (Part p : httpRequest.getParts()) {
cachedParameterNames.put(p.getName(), DUMMY);
}
} catch (IOException ioe) {
log(ioe);
} catch (ServletException se) {
log(se);
} catch (IllegalStateException ise) {
log(ise);
}
} }
return cachedParameterNames.keys(); return cachedParameterNames.keys();
} } else {
else
return httpRequest.getParameterNames(); return httpRequest.getParameterNames();
} }
}
/** /**
* @return The total length of the content. * @return The total length of the content.
@ -138,18 +142,17 @@ public class RequestWrapper {
public String getContentType( String partName ) public String getContentType( String partName )
{ {
String result = null; String result = null;
if( multiPartRequest != null ) { if (isMultiPartRequest) {
Hashtable<String, String> params = multiPartRequest.getParams( partName ); try {
for( Map.Entry<String, String> e : params.entrySet() ) { Part p = httpRequest.getPart(partName);
String key = e.getKey(); if (p != null)
if( key.toLowerCase(Locale.US).compareToIgnoreCase( "content-type") == 0 ) { result = p.getContentType();
String value = e.getValue(); } catch (IOException ioe) {
int i = value.indexOf( ';' ); log(ioe);
if( i != -1 ) } catch (ServletException se) {
result = value.substring( 0, i ); log(se);
else } catch (IllegalStateException ise) {
result = value; log(ise);
}
} }
} }
return result; return result;
@ -162,25 +165,38 @@ public class RequestWrapper {
public String getParameter( String name, String defaultValue ) public String getParameter( String name, String defaultValue )
{ {
String result = defaultValue; String result = defaultValue;
if( multiPartRequest != null ) { if (isMultiPartRequest) {
String str = cache.get(name); String str = cache.get(name);
if( str != null ) { if( str != null ) {
result = str; result = str;
} } else {
else { InputStream in = null;
String[] partNames = multiPartRequest.getPartNames(); try {
for( int i = 0; i < partNames.length; i++ ) Part p = httpRequest.getPart(name);
if( partNames[i].compareToIgnoreCase( name ) == 0 ) { if (p != null) {
str = multiPartRequest.getString( partNames[i] ); long len = p.getSize();
if( str != null ) { if (len > MAX_STRING_SIZE)
result = str; throw new IOException("String too big: " + len);
in = p.getInputStream();
byte[] data = new byte[(int) len];
DataHelper.read(in, data);
String enc = httpRequest.getCharacterEncoding();
if (enc == null)
enc = "UTF-8";
result = new String(data, enc);
cache.put( name, result ); cache.put( name, result );
break; }
} catch (IOException ioe) {
log(ioe);
} catch (ServletException se) {
log(se);
} catch (IllegalStateException ise) {
log(ise);
} finally {
if (in != null) try { in.close(); } catch (IOException ioe) {}
} }
} }
} } else {
}
else {
String str = httpRequest.getParameter( name ); String str = httpRequest.getParameter( name );
if( str != null ) if( str != null )
result = str; result = str;
@ -191,10 +207,18 @@ public class RequestWrapper {
public String getFilename(String partName ) public String getFilename(String partName )
{ {
String result = null; String result = null;
if( multiPartRequest != null ) { if (isMultiPartRequest) {
String str = multiPartRequest.getFilename( partName ); try {
if( str != null ) Part p = httpRequest.getPart(partName);
result = str; if (p != null)
result = p.getSubmittedFileName();
} catch (IOException ioe) {
log(ioe);
} catch (ServletException se) {
log(se);
} catch (IllegalStateException ise) {
log(ise);
}
} }
return result; return result;
} }
@ -202,10 +226,25 @@ public class RequestWrapper {
public InputStream getInputStream(String partName ) public InputStream getInputStream(String partName )
{ {
InputStream result = null; InputStream result = null;
if( multiPartRequest != null ) { if (isMultiPartRequest) {
result = multiPartRequest.getInputStream( partName ); try {
Part p = httpRequest.getPart(partName);
if (p != null)
result = p.getInputStream();
} catch (IOException ioe) {
log(ioe);
} catch (ServletException se) {
log(se);
} catch (IllegalStateException ise) {
log(ise);
}
} }
return result; return result;
} }
/** @since 0.9.33 */
private static void log(Exception e) {
Log log = I2PAppContext.getGlobalContext().logManager().getLog(RequestWrapper.class);
log.error("Multipart form error", e);
}
} }

View File

@ -1,449 +0,0 @@
// ========================================================================
// $Id: MultiPartRequest.java,v 1.16 2005/12/02 20:13:52 gregwilkins Exp $
// Copyright 1996-2004 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========================================================================
package org.mortbay.servlet;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.Hashtable;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import javax.servlet.http.HttpServletRequest;
//import org.apache.commons.logging.Log;
//import org.mortbay.log.LogFactory;
//import org.eclipse.jetty.util.MultiMap;
import org.mortbay.util.MultiMap;
import org.mortbay.util.LineInput;
/* ------------------------------------------------------------ */
/** Multipart Form Data request.
* <p>
* This class decodes the multipart/form-data stream sent by
* a HTML form that uses a file input item.
* </p>
*
* <h3>Usage</h3>
* Each part of the form data is named from the HTML form and
* is available either via getString(name) or getInputStream(name).
* Furthermore the MIME parameters and filename can be requested for
* each part.
* <pre>
* </pre>
*
* Modded to compile with Jetty 6 for I2P
* Modded to make fields private and final
*
* @version $Id: MultiPartRequest.java,v 1.16 2005/12/02 20:13:52 gregwilkins Exp $
* @author Greg Wilkins
* @author Jim Crossley
*/
public class MultiPartRequest
{
//private static Log log = LogFactory.getLog(MultiPartRequest.class);
/* ------------------------------------------------------------ */
private final HttpServletRequest _request;
private final LineInput _in;
private final String _boundary;
private final String _encoding;
private final byte[] _byteBoundary;
private final MultiMap<String> _partMap = new MultiMap<String>(10);
private int _char=-2;
private boolean _lastPart=false;
/* ------------------------------------------------------------ */
/** Constructor.
* @param request The request containing a multipart/form-data
* request
* @throws IOException IOException
*/
public MultiPartRequest(HttpServletRequest request)
throws IOException
{
_request=request;
String content_type = request.getHeader("Content-Type");
if (!content_type.startsWith("multipart/form-data"))
throw new IOException("Not multipart/form-data request");
//if(log.isDebugEnabled())log.debug("Multipart content type = "+content_type);
_encoding = request.getCharacterEncoding();
if (_encoding != null)
_in = new LineInput(request.getInputStream(), 2048, _encoding);
else
_in = new LineInput(request.getInputStream());
// Extract boundary string
_boundary="--"+
value(content_type.substring(content_type.indexOf("boundary=")));
//if(log.isDebugEnabled())log.debug("Boundary="+_boundary);
_byteBoundary= (_boundary+"--").getBytes("UTF-8");
loadAllParts();
}
/* ------------------------------------------------------------ */
/** Get the part names.
* @return an array of part names
*/
public String[] getPartNames()
{
Set<String> s = _partMap.keySet();
return s.toArray(new String[s.size()]);
}
/* ------------------------------------------------------------ */
/** Check if a named part is present
* @param name The part
* @return true if it was included
*/
public boolean contains(String name)
{
Part part = (Part)_partMap.get(name);
return (part!=null);
}
/* ------------------------------------------------------------ */
/** Get the data of a part as a string.
* @param name The part name
* @return The part data
*/
public String getString(String name)
{
List<Object> part = _partMap.getValues(name);
if (part==null)
return null;
if (_encoding != null)
{
try
{
return new String(((Part)part.get(0))._data, _encoding);
}
catch (UnsupportedEncodingException uee)
{
//if (log.isDebugEnabled())log.debug("Invalid character set: " + uee);
return null;
}
}
else
return new String(((Part)part.get(0))._data);
}
/* ------------------------------------------------------------ */
/**
* @param name The part name
* @return The parts data
*/
public String[] getStrings(String name)
{
List<Object> parts = _partMap.getValues(name);
if (parts==null)
return null;
String[] strings = new String[parts.size()];
if (_encoding == null)
{
for (int i=0; i<strings.length; i++)
strings[i] = new String(((Part)parts.get(i))._data);
}
else
{
try
{
for (int i=0; i<strings.length; i++)
strings[i] = new String(((Part)parts.get(i))._data, _encoding);
}
catch (UnsupportedEncodingException uee)
{
//if (log.isDebugEnabled())log.debug("Invalid character set: " + uee);
return null;
}
}
return strings;
}
/* ------------------------------------------------------------ */
/** Get the data of a part as a stream.
* @param name The part name
* @return Stream providing the part data
*/
public InputStream getInputStream(String name)
{
List<Object> part = _partMap.getValues(name);
if (part==null)
return null;
return new ByteArrayInputStream(((Part)part.get(0))._data);
}
/* ------------------------------------------------------------ */
public InputStream[] getInputStreams(String name)
{
List<Object> parts = _partMap.getValues(name);
if (parts==null)
return null;
InputStream[] streams = new InputStream[parts.size()];
for (int i=0; i<streams.length; i++) {
streams[i] = new ByteArrayInputStream(((Part)parts.get(i))._data);
}
return streams;
}
/* ------------------------------------------------------------ */
/** Get the MIME parameters associated with a part.
* @param name The part name
* @return Hashtable of parameters
*/
public Hashtable<String, String> getParams(String name)
{
List<Object> part = _partMap.getValues(name);
if (part==null)
return null;
return ((Part)part.get(0))._headers;
}
/* ------------------------------------------------------------ */
@SuppressWarnings("rawtypes")
public Hashtable[] getMultipleParams(String name)
{
List<Object> parts = _partMap.getValues(name);
if (parts==null)
return null;
Hashtable[] params = new Hashtable[parts.size()];
for (int i=0; i<params.length; i++) {
params[i] = ((Part)parts.get(i))._headers;
}
return params;
}
/* ------------------------------------------------------------ */
/** Get any file name associated with a part.
* @param name The part name
* @return The filename
*/
public String getFilename(String name)
{
List<Object> part = _partMap.getValues(name);
if (part==null)
return null;
return ((Part)part.get(0))._filename;
}
/* ------------------------------------------------------------ */
public String[] getFilenames(String name)
{
List<Object> parts = _partMap.getValues(name);
if (parts==null)
return null;
String[] filenames = new String[parts.size()];
for (int i=0; i<filenames.length; i++) {
filenames[i] = ((Part)parts.get(i))._filename;
}
return filenames;
}
/* ------------------------------------------------------------ */
private void loadAllParts()
throws IOException
{
// Get first boundary
String line = _in.readLine();
if (line == null || !line.equals(_boundary))
{
//log.warn(line);
throw new IOException("Missing initial multi part boundary");
}
// Read each part
while (!_lastPart)
{
// Read Part headers
Part part = new Part();
String content_disposition=null;
while ((line=_in.readLine())!=null)
{
// If blank line, end of part headers
if (line.length()==0)
break;
//if(log.isDebugEnabled())log.debug("LINE="+line);
// place part header key and value in map
int c = line.indexOf(':',0);
if (c>0)
{
String key = line.substring(0,c).trim().toLowerCase();
String value = line.substring(c+1,line.length()).trim();
String ev = part._headers.get(key);
part._headers.put(key,(ev!=null)?(ev+';'+value):value);
//if(log.isDebugEnabled())log.debug(key+": "+value);
if (key.equals("content-disposition"))
content_disposition=value;
}
}
// Extract content-disposition
boolean form_data=false;
if (content_disposition==null)
{
throw new IOException("Missing content-disposition");
}
StringTokenizer tok =
new StringTokenizer(content_disposition,";");
while (tok.hasMoreTokens())
{
String t = tok.nextToken().trim();
String tl = t.toLowerCase();
if (t.startsWith("form-data"))
form_data=true;
else if (tl.startsWith("name="))
part._name=value(t);
else if (tl.startsWith("filename="))
part._filename=value(t);
}
// Check disposition
if (!form_data)
{
//log.warn("Non form-data part in multipart/form-data");
continue;
}
if (part._name==null || part._name.length()==0)
{
//log.warn("Part with no name in multipart/form-data");
continue;
}
//if(log.isDebugEnabled())log.debug("name="+part._name);
//if(log.isDebugEnabled())log.debug("filename="+part._filename);
_partMap.add(part._name,part);
part._data=readBytes();
}
}
/* ------------------------------------------------------------ */
private byte[] readBytes()
throws IOException
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int c;
boolean cr=false;
boolean lf=false;
// loop for all lines`
while (true)
{
int b=0;
while ((c=(_char!=-2)?_char:_in.read())!=-1)
{
_char=-2;
// look for CR and/or LF
if (c==13 || c==10)
{
if (c==13) _char=_in.read();
break;
}
// look for boundary
if (b>=0 && b<_byteBoundary.length && c==_byteBoundary[b])
b++;
else
{
// this is not a boundary
if (cr) baos.write(13);
if (lf) baos.write(10);
cr=lf=false;
if (b>0)
baos.write(_byteBoundary,0,b);
b=-1;
baos.write(c);
}
}
// check partial boundary
if ((b>0 && b<_byteBoundary.length-2) ||
(b==_byteBoundary.length-1))
{
if (cr) baos.write(13);
if (lf) baos.write(10);
cr=lf=false;
baos.write(_byteBoundary,0,b);
b=-1;
}
// boundary match
if (b>0 || c==-1)
{
if (b==_byteBoundary.length)
_lastPart=true;
if (_char==10) _char=-2;
break;
}
// handle CR LF
if (cr) baos.write(13);
if (lf) baos.write(10);
cr=(c==13);
lf=(c==10 || _char==10);
if (_char==10) _char=-2;
}
//if(log.isTraceEnabled())log.trace(baos.toString());
return baos.toByteArray();
}
/* ------------------------------------------------------------ */
private String value(String nameEqualsValue)
{
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
{
i=value.indexOf(' ');
if (i>0)
value=value.substring(0,i);
}
return value;
}
/* ------------------------------------------------------------ */
private static class Part
{
String _name;
String _filename;
Hashtable<String, String> _headers= new Hashtable<String, String>(10);
byte[] _data;
}
};

View File

@ -1,11 +0,0 @@
<html>
<body>
<p>
Old Jetty 5 classes for multipart form requests, moved to susimail and modded when we moved to Jetty 6,
then moved from susimail to jetty-i2p.jar when we needed them in the router console also.
As of 0.9.19.
Not a public API, not for direct use.
These are requirements for net.i2p.servlet.RequestWrapper.
</p>
</body>
</html>

View File

@ -1,114 +0,0 @@
// ========================================================================
// $Id: ByteArrayPool.java,v 1.9 2004/05/09 20:32:49 gregwilkins Exp $
// Copyright 2002-2004 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========================================================================
package org.mortbay.util;
import java.util.concurrent.atomic.AtomicInteger;
/* ------------------------------------------------------------ */
/** Byte Array Pool
* Simple pool for recycling byte arrays of a fixed size.
*
* @version $Id: ByteArrayPool.java,v 1.9 2004/05/09 20:32:49 gregwilkins Exp $
* @author Greg Wilkins (gregw)
*/
public class ByteArrayPool
{
public static final int __POOL_SIZE=
Integer.getInteger("org.mortbay.util.ByteArrayPool.pool_size",8).intValue();
public static final ThreadLocal<byte[][]> __pools = new BAThreadLocal();
public static final AtomicInteger __slot = new AtomicInteger();
/* ------------------------------------------------------------ */
/** Get a byte array from the pool of known size.
* @param size Size of the byte array.
* @return Byte array of known size.
*/
public static byte[] getByteArray(int size)
{
byte[][] pool = __pools.get();
boolean full=true;
for (int i=pool.length;i-->0;)
{
if (pool[i]!=null && pool[i].length==size)
{
byte[]b = pool[i];
pool[i]=null;
return b;
}
else
full=false;
}
if (full)
for (int i=pool.length;i-->0;)
pool[i]=null;
return new byte[size];
}
/* ------------------------------------------------------------ */
public static byte[] getByteArrayAtLeast(int minSize)
{
byte[][] pool = __pools.get();
for (int i=pool.length;i-->0;)
{
if (pool[i]!=null && pool[i].length>=minSize)
{
byte[]b = pool[i];
pool[i]=null;
return b;
}
}
return new byte[minSize];
}
/* ------------------------------------------------------------ */
public static void returnByteArray(final byte[] b)
{
if (b==null)
return;
byte[][] pool = __pools.get();
for (int i=pool.length;i-->0;)
{
if (pool[i]==null)
{
pool[i]=b;
return;
}
}
// slot.
int s = __slot.getAndIncrement();
if (s<0)s=-s;
pool[s%pool.length]=b;
}
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
private static final class BAThreadLocal extends ThreadLocal<byte[][]>
{
@Override
protected byte[][] initialValue()
{
return new byte[__POOL_SIZE][];
}
}
}

View File

@ -1,719 +0,0 @@
// ========================================================================
// $Id: LineInput.java,v 1.17 2005/10/05 11:32:40 gregwilkins Exp $
// Copyright 1996-2004 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========================================================================
package org.mortbay.util;
import java.io.ByteArrayInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
//import org.apache.commons.logging.Log;
//import org.mortbay.log.LogFactory;
/* ------------------------------------------------------------ */
/** Fast LineInput InputStream.
* This buffered InputStream provides methods for reading lines
* of bytes. The lines can be converted to String or character
* arrays either using the default encoding or a user supplied
* encoding.
*
* Buffering and data copying are highly optimized, making this
* an ideal class for protocols that mix character encoding lines
* with arbitrary byte data (eg HTTP).
*
* The buffer size is also the maximum line length in bytes and/or
* characters. If the byte length of a line is less than the max,
* but the character length is greater, than then trailing characters
* are lost.
*
* Line termination is forgiving and accepts CR, LF, CRLF or EOF.
* Line input uses the mark/reset mechanism, so any marks set
* prior to a readLine call are lost.
*
* @version $Id: LineInput.java,v 1.17 2005/10/05 11:32:40 gregwilkins Exp $
* @author Greg Wilkins (gregw)
*/
public class LineInput extends FilterInputStream
{
//private static Log log = LogFactory.getLog(LineInput.class);
/* ------------------------------------------------------------ */
private byte _buf[];
private ByteBuffer _byteBuffer;
private InputStreamReader _reader;
private int _mark=-1; // reset marker
private int _pos; // Start marker
private int _avail; // Available back marker, may be byte limited
private int _contents; // Absolute back marker of buffer
private int _byteLimit=-1;
private boolean _newByteLimit;
private LineBuffer _lineBuffer;
private String _encoding;
private boolean _eof=false;
private boolean _lastCr=false;
private boolean _seenCrLf=false;
private final static int LF=10;
private final static int CR=13;
/* ------------------------------------------------------------ */
/** Constructor.
* Default buffer and maximum line size is 2048.
* @param in The underlying input stream.
*/
public LineInput(InputStream in)
{
this(in,0);
}
/* ------------------------------------------------------------ */
/** Constructor.
* @param in The underlying input stream.
* @param bufferSize The buffer size and maximum line length.
*/
public LineInput(InputStream in, int bufferSize)
{
super(in);
_mark=-1;
if (bufferSize==0)
bufferSize=8192;
_buf=ByteArrayPool.getByteArray(bufferSize);
_byteBuffer=new ByteBuffer(_buf);
_lineBuffer=new LineBuffer(bufferSize);
try
{
_reader=new InputStreamReader(_byteBuffer,"UTF-8");
}
catch (UnsupportedEncodingException e)
{
_reader=new InputStreamReader(_byteBuffer);
}
}
/* ------------------------------------------------------------ */
/** Constructor.
* @param in The underlying input stream.
* @param bufferSize The buffer size and maximum line length.
* @param encoding the character encoding to use for readLine methods.
* @throws UnsupportedEncodingException
*/
public LineInput(InputStream in, int bufferSize, String encoding)
throws UnsupportedEncodingException
{
super(in);
_mark=-1;
if (bufferSize==0)
bufferSize=2048;
_buf=ByteArrayPool.getByteArray(bufferSize);
_byteBuffer=new ByteBuffer(_buf);
_lineBuffer=new LineBuffer(bufferSize);
_reader=new InputStreamReader(_byteBuffer,encoding);
_encoding=encoding;
}
/* ------------------------------------------------------------ */
public InputStream getInputStream()
{
return in;
}
/* ------------------------------------------------------------ */
/** Set the byte limit.
* If set, only this number of bytes are read before EOF.
* @param bytes Limit number of bytes, or -1 for no limit.
*/
public void setByteLimit(int bytes)
{
_byteLimit=bytes;
if (bytes>=0)
{
_newByteLimit=true;
_byteLimit-=_contents-_pos;
if (_byteLimit<0)
{
_avail+=_byteLimit;
_byteLimit=0;
}
}
else
{
_newByteLimit=false;
_avail=_contents;
_eof=false;
}
}
/* ------------------------------------------------------------ */
/** Get the byte limit.
* @return Number of bytes until EOF is returned or -1 for no limit.
*/
public int getByteLimit()
{
if (_byteLimit<0)
return _byteLimit;
return _byteLimit+_avail-_pos;
}
/* ------------------------------------------------------------ */
/** Read a line ended by CR, LF or CRLF.
* The default or supplied encoding is used to convert bytes to
* characters.
* @return The line as a String or null for EOF.
* @throws IOException
*/
public synchronized String readLine()
throws IOException
{
int len=fillLine(_buf.length);
if (len<0)
return null;
String s=null;
if (_encoding==null)
s=new String(_buf,_mark,len);
else
{
try
{
s=new String(_buf,_mark,len,_encoding);
}
catch(UnsupportedEncodingException e)
{
//log.warn(LogSupport.EXCEPTION,e);
}
}
_mark=-1;
return s;
}
/* ------------------------------------------------------------ */
/** Read a line ended by CR, LF or CRLF.
* The default or supplied encoding is used to convert bytes to
* characters.
* @param c Character buffer to place the line into.
* @param off Offset into the buffer.
* @param len Maximum length of line.
* @return The length of the line or -1 for EOF.
* @throws IOException
*/
public int readLine(char[] c,int off,int len)
throws IOException
{
int blen=fillLine(len);
if (blen<0)
return -1;
if (blen==0)
return 0;
_byteBuffer.setStream(_mark,blen);
int read=0;
while(read<len && _reader.ready())
{
int r = _reader.read(c,off+read,len-read);
if (r<=0)
break;
read+=r;
}
_mark=-1;
return read;
}
/* ------------------------------------------------------------ */
/** Read a line ended by CR, LF or CRLF.
* @param b Byte array to place the line into.
* @param off Offset into the buffer.
* @param len Maximum length of line.
* @return The length of the line or -1 for EOF.
* @throws IOException
*/
public int readLine(byte[] b,int off,int len)
throws IOException
{
len=fillLine(len);
if (len<0)
return -1;
if (len==0)
return 0;
System.arraycopy(_buf,_mark, b, off, len);
_mark=-1;
return len;
}
/* ------------------------------------------------------------ */
/** Read a Line ended by CR, LF or CRLF.
* Read a line into a shared LineBuffer instance. The LineBuffer is
* resused between calls and should not be held by the caller.
* The default or supplied encoding is used to convert bytes to
* characters.
* @return LineBuffer instance or null for EOF.
* @throws IOException
*/
public LineBuffer readLineBuffer()
throws IOException
{
return readLineBuffer(_buf.length);
}
/* ------------------------------------------------------------ */
/** Read a Line ended by CR, LF or CRLF.
* Read a line into a shared LineBuffer instance. The LineBuffer is
* resused between calls and should not be held by the caller.
* The default or supplied encoding is used to convert bytes to
* characters.
* @param len Maximum length of a line, or 0 for default
* @return LineBuffer instance or null for EOF.
* @throws IOException
*/
public LineBuffer readLineBuffer(int len)
throws IOException
{
len=fillLine(len>0?len:_buf.length);
if (len<0)
return null;
if (len==0)
{
_lineBuffer.size=0;
return _lineBuffer;
}
_byteBuffer.setStream(_mark,len);
_lineBuffer.size=0;
int read=0;
while(read<len && _reader.ready())
{
int r = _reader.read(_lineBuffer.buffer,
read,
len-read);
if (r<=0)
break;
read+=r;
}
_lineBuffer.size=read;
_mark=-1;
return _lineBuffer;
}
/* ------------------------------------------------------------ */
public synchronized int read() throws IOException
{
int b;
if (_pos >=_avail)
fill();
if (_pos >=_avail)
b=-1;
else
b=_buf[_pos++]&255;
return b;
}
/* ------------------------------------------------------------ */
public synchronized int read(byte b[], int off, int len) throws IOException
{
int avail=_avail-_pos;
if (avail <=0)
{
fill();
avail=_avail-_pos;
}
if (avail <=0)
len=-1;
else
{
len=(avail < len) ? avail : len;
System.arraycopy(_buf,_pos,b,off,len);
_pos +=len;
}
return len;
}
/* ------------------------------------------------------------ */
public long skip(long n) throws IOException
{
int avail=_avail-_pos;
if (avail <=0)
{
fill();
avail=_avail-_pos;
}
if (avail <=0)
n=0;
else
{
n=(avail < n) ? avail : n;
_pos +=n;
}
return n;
}
/* ------------------------------------------------------------ */
public synchronized int available()
throws IOException
{
int in_stream=in.available();
if (_byteLimit>=0 && in_stream>_byteLimit)
in_stream=_byteLimit;
return _avail - _pos + in_stream;
}
/* ------------------------------------------------------------ */
public synchronized void mark(int limit)
throws IllegalArgumentException
{
if (limit>_buf.length)
{
byte[] new_buf=new byte[limit];
System.arraycopy(_buf,_pos,new_buf,_pos,_avail-_pos);
_buf=new_buf;
if (_byteBuffer!=null)
_byteBuffer.setBuffer(_buf);
}
_mark=_pos;
}
/* ------------------------------------------------------------ */
public synchronized void reset()
throws IOException
{
if (_mark < 0)
throw new IOException("Resetting to invalid mark");
_pos=_mark;
_mark=-1;
}
/* ------------------------------------------------------------ */
public boolean markSupported()
{
return true;
}
/* ------------------------------------------------------------ */
private void fill()
throws IOException
{
// if the mark is in the middle of the buffer
if (_mark > 0)
{
// moved saved bytes to start of buffer
int saved=_contents - _mark;
System.arraycopy(_buf, _mark, _buf, 0, saved);
_pos-=_mark;
_avail-=_mark;
_contents=saved;
_mark=0;
}
else if (_mark<0 && _pos>0)
{
// move remaining bytes to start of buffer
int saved=_contents-_pos;
System.arraycopy(_buf,_pos, _buf, 0, saved);
_avail-=_pos;
_contents=saved;
_pos=0;
}
else if (_mark==0 && _pos>0 && _contents==_buf.length)
{
// Discard the mark as we need the space.
_mark=-1;
fill();
return;
}
// Get ready to top up the buffer
int n=0;
_eof=false;
// Handle byte limited EOF
if (_byteLimit==0)
_eof=true;
// else loop until something is read.
else while (!_eof && n==0 && _buf.length>_contents)
{
// try to read as much as will fit.
int space=_buf.length-_contents;
n=in.read(_buf,_contents,space);
if (n<=0)
{
// If no bytes - we could be NBIO, so we want to avoid
// a busy loop.
if (n==0)
{
// Yield to give a chance for some bytes to turn up
Thread.yield();
// Do a byte read as that is blocking
int b = in.read();
if (b>=0)
{
n=1;
_buf[_contents++]=(byte)b;
}
else
_eof=true;
}
else
_eof=true;
}
else
_contents+=n;
_avail=_contents;
// If we have a byte limit
if (_byteLimit>0)
{
// adjust the bytes available
if (_contents-_pos >=_byteLimit)
_avail=_byteLimit+_pos;
if (n>_byteLimit)
_byteLimit=0;
else if (n>=0)
_byteLimit-=n;
else if (n==-1)
throw new IOException("Premature EOF");
}
}
// If we have some characters and the last read was a CR and
// the first char is a LF, skip it
if (_avail-_pos>0 && _lastCr && _buf[_pos]==LF)
{
_seenCrLf=true;
_pos++;
if (_mark>=0)
_mark++;
_lastCr=false;
// If the byte limit has just been imposed, dont count
// LF as content.
if(_byteLimit>=0 && _newByteLimit)
{
if (_avail<_contents)
_avail++;
else
_byteLimit++;
}
// If we ate all that ws filled, fill some more
if (_pos==_avail)
fill();
}
_newByteLimit=false;
}
/* ------------------------------------------------------------ */
private int fillLine(int maxLen)
throws IOException
{
_mark=_pos;
if (_pos>=_avail)
fill();
if (_pos>=_avail)
return -1;
byte b;
boolean cr=_lastCr;
boolean lf=false;
_lastCr=false;
int len=0;
LineLoop:
while (_pos<=_avail)
{
// if we have gone past the end of the buffer
while (_pos==_avail)
{
// If EOF or no more space in the buffer,
// return a line.
if (_eof || (_mark==0 && _contents==_buf.length))
{
_lastCr=!_eof && _buf[_avail-1]==CR;
cr=true;
lf=true;
break LineLoop;
}
// If we have a CR and no more characters are available
if (cr && in.available()==0 && !_seenCrLf)
{
_lastCr=true;
cr=true;
lf=true;
break LineLoop;
}
else
{
// Else just wait for more...
_pos=_mark;
fill();
_pos=len;
cr=false;
}
}
// Get the byte
b=_buf[_pos++];
switch(b)
{
case LF:
if (cr) _seenCrLf=true;
lf=true;
break LineLoop;
case CR:
if (cr)
{
// Double CR
if (_pos>1)
{
_pos--;
break LineLoop;
}
}
cr=true;
break;
default:
if(cr)
{
if (_pos==1)
cr=false;
else
{
_pos--;
break LineLoop;
}
}
len++;
if (len==maxLen)
{
// look for EOL
if (_mark!=0 && _pos+2>=_avail && _avail<_buf.length)
fill();
if (_pos<_avail && _buf[_pos]==CR)
{
cr=true;
_pos++;
}
if (_pos<_avail && _buf[_pos]==LF)
{
lf=true;
_pos++;
}
if (!cr && !lf)
{
// fake EOL
lf=true;
cr=true;
}
break LineLoop;
}
break;
}
}
if (!cr && !lf && len==0)
len=-1;
return len;
}
/* ------------------------------------------------------------ */
private static class ByteBuffer extends ByteArrayInputStream
{
ByteBuffer(byte[] buffer)
{
super(buffer);
}
void setBuffer(byte[] buffer)
{
buf=buffer;
}
void setStream(int offset,int length)
{
pos=offset;
count=offset+length;
mark=-1;
}
}
/* ------------------------------------------------------------ */
/** Reusable LineBuffer.
* Externalized LineBuffer for fast line parsing.
*/
public static class LineBuffer
{
public char[] buffer;
public int size;
public LineBuffer(int maxLineLength)
{buffer=new char[maxLineLength];}
public String toString(){return new String(buffer,0,size);}
}
/* ------------------------------------------------------------ */
public void destroy()
{
ByteArrayPool.returnByteArray(_buf);
_byteBuffer=null;
_reader=null;
_lineBuffer=null;
_encoding=null;
}
}

View File

@ -1,63 +0,0 @@
package org.mortbay.util;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
/**
* A multi valued Map.
* Simple I2P replacement for org.eclipse.jetty.util.MultiMap
* so we don't depend on Jetty utils.
*
* Contains only the methods required by MultiPartRequest.
* Does not implement Map. Unsynchronized.
*
* @since 0.9.12
*/
public class MultiMap<T>
{
private final HashMap<T, LinkedList<Object>> data;
public MultiMap(int capacity)
{
data = new HashMap<T, LinkedList<Object>>(capacity);
}
public Set<T> keySet()
{
return data.keySet();
}
/**
* This returns the first item or null.
* The Jetty version appears to return the item if only one,
* or the entire list if more than one.
* Only used by MultiPartRequest.contains() which is unused.
* contains() would fail with a ClassCastException if we returned a list here,
* which is a bug in MultiPartRequest?
*/
public Object get(T key)
{
List<Object> tmp = getValues(key);
return tmp != null ? tmp.get( 0 ) : null;
}
public List<Object> getValues(T key)
{
return data.get( key );
}
public void add(T key, Object value )
{
LinkedList<Object> list = data.get( key );
if( list == null ) {
list = new LinkedList<Object>();
data.put( key, list );
}
list.add( value );
}
}

View File

@ -1,11 +0,0 @@
<html>
<body>
<p>
Old Jetty 5 classes for multipart form requests, moved to susimail and modded when we moved to Jetty 6,
then moved from susimail to jetty-i2p.jar when we needed them in the router console also.
As of 0.9.19.
Not a public API, not for direct use.
These are requirements for net.i2p.servlet.RequestWrapper.
</p>
</body>
</html>

View File

@ -445,6 +445,23 @@
<replace file="../jsp/web-out.xml"> <replace file="../jsp/web-out.xml">
<replacefilter token="&lt;!-- precompiled servlets --&gt;" value="${jspc.web.fragment}" /> <replacefilter token="&lt;!-- precompiled servlets --&gt;" value="${jspc.web.fragment}" />
</replace> </replace>
<!-- Add multipart config to servlets that need them -->
<property name="__match1" value="&lt;servlet-class&gt;net.i2p.router.web.jsp." />
<property name="__match2" value="_jsp&lt;/servlet-class&gt;" />
<property name="__class1" value="${__match1}configclients${__match2}" />
<property name="__class2" value="${__match1}configfamily${__match2}" />
<property name="__class3" value="${__match1}configreseed${__match2}" />
<property name="__multipart" value="&#10;
&lt;multipart-config&gt;&#10;
&lt;max-file-size&gt;67108864&lt;/max-file-size&gt;&#10;
&lt;max-request-size&gt;67108864&lt;/max-request-size&gt;&#10;
&lt;file-size-threshold&gt;262144&lt;/file-size-threshold&gt;&#10;
&lt;/multipart-config&gt;" />
<replace file="../jsp/web-out.xml">
<replacefilter token="${__class1}" value="${__class1}${__multipart}" />
<replacefilter token="${__class2}" value="${__class2}${__multipart}" />
<replacefilter token="${__class3}" value="${__class3}${__multipart}" />
</replace>
</target> </target>
<uptodate property="precompilejsp.uptodate" targetfile="../jsp/web-out.xml"> <uptodate property="precompilejsp.uptodate" targetfile="../jsp/web-out.xml">

View File

@ -16,6 +16,11 @@
<servlet> <servlet>
<servlet-name>SusiMail</servlet-name> <servlet-name>SusiMail</servlet-name>
<servlet-class>i2p.susi.webmail.WebMail</servlet-class> <servlet-class>i2p.susi.webmail.WebMail</servlet-class>
<multipart-config>
<max-file-size>67108864</max-file-size>
<max-request-size>67108864</max-request-size>
<file-size-threshold>262144</file-size-threshold>
</multipart-config>
</servlet> </servlet>
<servlet-mapping> <servlet-mapping>
<servlet-name>SusiMail</servlet-name> <servlet-name>SusiMail</servlet-name>

View File

@ -757,7 +757,7 @@
<group title="I2PTunnel Application" packages="net.i2p.i2ptunnel:net.i2p.i2ptunnel.*" /> <group title="I2PTunnel Application" packages="net.i2p.i2ptunnel:net.i2p.i2ptunnel.*" />
<group title="Imagegen Application" packages="com.docuverse.identicon:com.google.zxing:com.google.zxing.*:net.i2p.imagegen" /> <group title="Imagegen Application" packages="com.docuverse.identicon:com.google.zxing:com.google.zxing.*:net.i2p.imagegen" />
<group title="Installer Utilities" packages="net.i2p.installer" /> <group title="Installer Utilities" packages="net.i2p.installer" />
<group title="Jetty Utilities" packages="net.i2p.jetty:net.i2p.servlet:net.i2p.servlet.*:org.mortbay.servlet:org.mortbay.util" /> <group title="Jetty Utilities" packages="net.i2p.jetty:net.i2p.servlet:net.i2p.servlet.*" />
<group title="JRobin Library" packages="org.jrobin:org.jrobin.*:engine.misc" /> <group title="JRobin Library" packages="org.jrobin:org.jrobin.*:engine.misc" />
<group title="SAM Bridge" packages="net.i2p.sam" /> <group title="SAM Bridge" packages="net.i2p.sam" />
<group title="SAM Demos" packages="net.i2p.sam.client" /> <group title="SAM Demos" packages="net.i2p.sam.client" />