Resolved Charset Issues and Reworked Request.MultiPartInputStream

Changed Request.MultiPartInputStream to an interface called MultiParts where there is an implementation for both the HTTP and UTIL parsers.

Resolved some issues with default charsets in regards to request.setCharacterEncoding and the _charset_ part for issue #2398.

Changed HTTP parser to operate the same as UTIL parser in situtions with parts not of type form-data or without name field. HTTP parser was ignoring these parts, UTIL parser was throwing exceptions.

Replaced the context attribute with a field in MultiParts.

Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
This commit is contained in:
Lachlan Roberts 2018-04-03 15:24:54 +10:00
parent be2d6ebb29
commit 13b15e3566
9 changed files with 291 additions and 144 deletions

View File

@ -35,7 +35,6 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletInputStream;
@ -73,6 +72,7 @@ public class MultiPartFormInputStream
protected File _contextTmpDir;
protected boolean _deleteOnExit;
protected boolean _writeFilesWithFilenames;
protected boolean _parsed;
public class MultiPart implements Part
{
@ -384,17 +384,37 @@ public class MultiPartFormInputStream
if (((ServletInputStream)in).isFinished())
{
_parts = EMPTY_MAP;
_parsed = true;
return;
}
}
_in = new BufferedInputStream(in);
}
/**
* @return whether the list of parsed parts is empty
*/
public boolean isEmpty()
{
if (_parts == null)
return true;
Collection<List<Part>> values = _parts.values();
for (List<Part> partList : values)
{
if(partList.size() != 0)
return false;
}
return true;
}
/**
* Get the already parsed parts.
*
* @return the parts that were parsed
*/
@Deprecated
public Collection<Part> getParsedParts()
{
if (_parts == null)
@ -412,14 +432,23 @@ public class MultiPartFormInputStream
/**
* Delete any tmp storage for parts, and clear out the parts list.
*
* @throws MultiException
* if unable to delete the parts
*/
public void deleteParts() throws MultiException
public void deleteParts()
{
Collection<Part> parts = getParsedParts();
if (!_parsed)
return;
Collection<Part> parts;
try
{
parts = getParts();
}
catch (IOException e)
{
throw new RuntimeException(e);
}
MultiException err = new MultiException();
for (Part p : parts)
{
try
@ -433,7 +462,7 @@ public class MultiPartFormInputStream
}
_parts.clear();
err.ifExceptionThrowMulti();
err.ifExceptionThrowRuntime();
}
/**
@ -445,6 +474,7 @@ public class MultiPartFormInputStream
*/
public Collection<Part> getParts() throws IOException
{
if (!_parsed)
parse();
throwIfError();
@ -469,6 +499,7 @@ public class MultiPartFormInputStream
*/
public Part getPart(String name) throws IOException
{
if(!_parsed)
parse();
throwIfError();
return _parts.getValue(name,0);
@ -500,8 +531,10 @@ public class MultiPartFormInputStream
protected void parse()
{
// have we already parsed the input?
if (_parts != null || _err != null)
if (_parsed)
return;
_parsed = true;
try
{
@ -612,7 +645,6 @@ public class MultiPartFormInputStream
class Handler implements MultiPartParser.Handler
{
private MultiPart _part = null;
private String contentDisposition = null;
private String contentType = null;
@ -673,18 +705,16 @@ public class MultiPartFormInputStream
// Check disposition
if (!form_data)
{
return false;
}
throw new IOException("Part not form-data");
// 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)
{
return false;
}
throw new IOException("No name in part");
// create the new part
_part = new MultiPart(name,filename);
@ -766,12 +796,6 @@ public class MultiPartFormInputStream
contentType = null;
headers = new MultiMap<>();
}
@Override
public String toString() {
return("contentDisposition: "+contentDisposition+" contentType:"+contentType);
}
}
public void setDeleteOnExit(boolean deleteOnExit)

View File

@ -24,6 +24,7 @@ import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;

View File

@ -19,18 +19,18 @@
package org.eclipse.jetty.http;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.*;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import org.eclipse.jetty.http.MultiPartParser.State;
import org.eclipse.jetty.util.BufferUtil;
import org.hamcrest.Matchers;
import org.junit.Ignore;
import org.junit.Test;
public class MultiPartParserTest

View File

@ -51,7 +51,7 @@ public class HttpChannelOverHttp extends HttpChannel implements HttpParser.Reque
{
private static final Logger LOG = Log.getLogger(HttpChannelOverHttp.class);
private final static HttpField PREAMBLE_UPGRADE_H2C = new HttpField(HttpHeader.UPGRADE, "h2c");
private static final String ATTR_COMPLIANCE_VIOLATIONS = "org.eclipse.jetty.http.compliance.violations";
public static final String ATTR_COMPLIANCE_VIOLATIONS = "org.eclipse.jetty.http.compliance.violations";
private final HttpFields _fields = new HttpFields();
private final MetaData.Request _metadata = new MetaData.Request(_fields);

View File

@ -38,19 +38,19 @@ public class MultiPartCleanerListener implements ServletRequestListener
public void requestDestroyed(ServletRequestEvent sre)
{
//Clean up any tmp files created by MultiPartInputStream
Request.MultiPartInputStream mpis = (Request.MultiPartInputStream)sre.getServletRequest().getAttribute(Request.__MULTIPART_INPUT_STREAM);
if (mpis != null)
Request.MultiParts parts = (Request.MultiParts)sre.getServletRequest().getAttribute(Request.__MULTIPARTS);
if (parts != null)
{
ContextHandler.Context context = (ContextHandler.Context)sre.getServletRequest().getAttribute(Request.__MULTIPART_CONTEXT);
ContextHandler.Context context = parts.getContext();
//Only do the cleanup if we are exiting from the context in which a servlet parsed the multipart files
if (context == sre.getServletContext())
{
try
{
mpis.deleteParts();
parts.close();
}
catch (MultiException e)
catch (Exception e)
{
sre.getServletContext().log("Errors deleting multipart tmp files", e);
}

View File

@ -20,6 +20,7 @@ package org.eclipse.jetty.server;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@ -34,6 +35,7 @@ import java.security.Principal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Enumeration;
import java.util.EventListener;
import java.util.List;
@ -84,9 +86,9 @@ import org.eclipse.jetty.server.session.SessionHandler;
import org.eclipse.jetty.util.Attributes;
import org.eclipse.jetty.util.AttributesMap;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.MultiException;
import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.util.MultiPartInputStreamParser;
import org.eclipse.jetty.util.MultiPartInputStreamParser.NonCompliance;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.UrlEncoded;
@ -136,8 +138,7 @@ import org.eclipse.jetty.util.log.Logger;
public class Request implements HttpServletRequest
{
public static final String __MULTIPART_CONFIG_ELEMENT = "org.eclipse.jetty.multipartConfig";
public static final String __MULTIPART_INPUT_STREAM = "org.eclipse.jetty.multiPartInputStream";
public static final String __MULTIPART_CONTEXT = "org.eclipse.jetty.multiPartContext";
public static final String __MULTIPARTS = "org.eclipse.jetty.multiPartInputStream";
private static final Logger LOG = Log.getLogger(Request.class);
private static final Collection<Locale> __defaultLocale = Collections.singleton(Locale.getDefault());
@ -216,7 +217,7 @@ public class Request implements HttpServletRequest
private HttpSession _session;
private SessionHandler _sessionHandler;
private long _timeStamp;
private MultiPartInputStream _multiPartInputStream; //if the request is a multi-part mime
private MultiParts _multiParts; //if the request is a multi-part mime
private AsyncContextState _async;
/* ------------------------------------------------------------ */
@ -476,7 +477,7 @@ public class Request implements HttpServletRequest
}
else if (MimeTypes.Type.MULTIPART_FORM_DATA.is(contentType) &&
getAttribute(__MULTIPART_CONFIG_ELEMENT) != null &&
_multiPartInputStream == null)
_multiParts == null)
{
try
{
@ -1869,7 +1870,7 @@ public class Request implements HttpServletRequest
_parameters = null;
_contentParamsExtracted = false;
_inputState = __NONE;
_multiPartInputStream = null;
_multiParts = null;
_remote=null;
_input.recycle();
}
@ -2310,7 +2311,7 @@ public class Request implements HttpServletRequest
{
getParts();
return _multiPartInputStream.getPart(name);
return _multiParts.getPart(name);
}
/* ------------------------------------------------------------ */
@ -2325,26 +2326,52 @@ public class Request implements HttpServletRequest
private Collection<Part> getParts(MultiMap<String> params) throws IOException, ServletException
{
if (_multiPartInputStream == null)
_multiPartInputStream = (MultiPartInputStream)getAttribute(__MULTIPART_INPUT_STREAM);
if (_multiParts == null)
_multiParts = (MultiParts)getAttribute(__MULTIPARTS);
if (_multiPartInputStream == null)
if (_multiParts == null)
{
MultipartConfigElement config = (MultipartConfigElement)getAttribute(__MULTIPART_CONFIG_ELEMENT);
if (config == null)
throw new IllegalStateException("No multipart config for servlet");
_multiPartInputStream = new MultiPartInputStream(getInputStream(),
_multiParts = newMultiParts(getInputStream(),
getContentType(), config,
(_context != null?(File)_context.getAttribute("javax.servlet.context.tempdir"):null));
(_context != null?(File)_context.getAttribute("javax.servlet.context.tempdir"):null),
true);
setAttribute(__MULTIPARTS, _multiParts);
Collection<Part> parts = _multiParts.getParts(); //causes parsing
String _charset_ = null;
Part charsetPart = _multiParts.getPart("_charset_");
if(charsetPart != null)
{
try (InputStream is = charsetPart.getInputStream())
{
ByteArrayOutputStream os = new ByteArrayOutputStream();
IO.copy(is, os);
_charset_ = new String(os.toByteArray(),StandardCharsets.UTF_8);
}
}
// charset should be:
// 1. the charset set in the parts content type; else
// 2. the default charset set in the _charset_ part; else
// 3. the default charset set in the request.setCharacterEncoding; else
// 4. the default charset set to UTF_8
Charset defaultCharset;
if (_charset_ != null)
defaultCharset = Charset.forName(_charset_);
else if (getCharacterEncoding() != null)
defaultCharset = Charset.forName(getCharacterEncoding());
else
defaultCharset = StandardCharsets.UTF_8;
setAttribute(__MULTIPART_INPUT_STREAM, _multiPartInputStream);
setAttribute(__MULTIPART_CONTEXT, _context);
Collection<Part> parts = _multiPartInputStream.getParts(); //causes parsing
ByteArrayOutputStream os = null;
for (Part p:parts)
{
if (_multiPartInputStream.getContentDispositionFilename(p) == null)
if (p.getSubmittedFileName() == null)
{
// Servlet Spec 3.0 pg 23, parts without filename must be put into params.
String charset = null;
@ -2356,7 +2383,8 @@ public class Request implements HttpServletRequest
if (os == null)
os = new ByteArrayOutputStream();
IO.copy(is, os);
String content=new String(os.toByteArray(),charset==null?StandardCharsets.UTF_8:Charset.forName(charset));
String content=new String(os.toByteArray(),charset==null?defaultCharset:Charset.forName(charset));
if (_contentParameters == null)
_contentParameters = params == null ? new MultiMap<>() : params;
_contentParameters.add(p.getName(), content);
@ -2366,10 +2394,28 @@ public class Request implements HttpServletRequest
}
}
return _multiPartInputStream.getParts();
return _multiParts.getParts();
}
private MultiParts newMultiParts(ServletInputStream inputStream, String contentType, MultipartConfigElement config, Object object, boolean useNewParser) throws IOException
{
MultiParts multiParts;
if(useNewParser)
{
multiParts = new MultiPartsHttpParser(getInputStream(), getContentType(), config,
(_context != null?(File)_context.getAttribute("javax.servlet.context.tempdir"):null));
}
else
{
multiParts = new MultiPartsUtilParser(getInputStream(), getContentType(), config,
(_context != null?(File)_context.getAttribute("javax.servlet.context.tempdir"):null));
}
return multiParts;
}
/* ------------------------------------------------------------ */
@Override
public void login(String username, String password) throws ServletException
@ -2477,83 +2523,144 @@ public class Request implements HttpServletRequest
* Used to switch between the old and new implementation of MultiPart Form InputStream Parsing.
* The new implementation is prefered will be used as default unless specified otherwise constructor.
*/
public interface MultiParts extends Closeable
{
public Collection<Part> getParts();
public Part getPart(String name);
public boolean isEmpty();
public ContextHandler.Context getContext();
}
public class MultiPartsHttpParser implements MultiParts
{
private final MultiPartFormInputStream _httpParser;
private final ContextHandler.Context _context;
public MultiPartsHttpParser(InputStream in, String contentType, MultipartConfigElement config, File contextTmpDir) throws IOException
{
_httpParser = new MultiPartFormInputStream(in, contentType, config, contextTmpDir);
_context = Request.this._context;
_httpParser.getParts();
}
@Override
public Collection<Part> getParts()
{
try
{
return _httpParser.getParts();
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
@Override
public Part getPart(String name) {
try
{
return _httpParser.getPart(name);
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
@Override
public void close()
{
_httpParser.deleteParts();
}
@Override
public boolean isEmpty()
{
return _httpParser.isEmpty();
}
@Override
public Context getContext()
{
return _context;
}
}
@SuppressWarnings("deprecation")
public class MultiPartInputStream
public class MultiPartsUtilParser implements MultiParts
{
private boolean usingNewParser = true;
private MultiPartFormInputStream _newParser = null;
private MultiPartInputStreamParser _oldParser = null;
private final MultiPartInputStreamParser _utilParser;
private final ContextHandler.Context _context;
public MultiPartInputStream(InputStream in, String contentType, MultipartConfigElement config, File contextTmpDir)
public MultiPartsUtilParser(InputStream in, String contentType, MultipartConfigElement config, File contextTmpDir) throws IOException
{
this(in, contentType, config, contextTmpDir, false);
}
_utilParser = new MultiPartInputStreamParser(in, contentType, config, contextTmpDir);
_context = Request.this._context;
_utilParser.getParts();
public MultiPartInputStream(InputStream in, String contentType, MultipartConfigElement config, File contextTmpDir, boolean useOldParser)
EnumSet<NonCompliance> nonComplianceWarnings = _utilParser.getNonComplianceWarnings();
if (!nonComplianceWarnings.isEmpty())
{
if(useOldParser)
usingNewParser = false;
else
usingNewParser = true;
if(usingNewParser)
_newParser = new MultiPartFormInputStream(in, contentType, config, contextTmpDir);
else
_oldParser = new MultiPartInputStreamParser(in, contentType, config, contextTmpDir);
}
public Collection<Part> getParts() throws IOException {
Collection<Part> parts = null;
if(usingNewParser)
parts = _newParser.getParts();
else
parts = _oldParser.getParts();
return parts;
}
public Part getPart(String name) throws IOException {
Part part = null;
if(usingNewParser)
part = _newParser.getPart(name);
else
part = _oldParser.getPart(name);
return part;
}
public String getContentDispositionFilename(Part p)
@SuppressWarnings("unchecked")
List<String> violations = (List<String>)getAttribute(HttpChannelOverHttp.ATTR_COMPLIANCE_VIOLATIONS);
if (violations==null)
{
String contentDisposition = null;
if(usingNewParser)
contentDisposition = ((MultiPartFormInputStream.MultiPart)p).getContentDispositionFilename();
else
contentDisposition = ((MultiPartInputStreamParser.MultiPart)p).getContentDispositionFilename();
return contentDisposition;
violations = new ArrayList<>();
setAttribute(HttpChannelOverHttp.ATTR_COMPLIANCE_VIOLATIONS,violations);
}
public void deleteParts() throws MultiException
for(NonCompliance nc : nonComplianceWarnings)
violations.add(nc.name()+": "+nc.getURL());
}
}
@Override
public Collection<Part> getParts()
{
if(usingNewParser)
_newParser.deleteParts();
else
_oldParser.deleteParts();
}
public Collection<Part> getParsedParts()
try
{
Collection<Part> parsedParts = null;
return _utilParser.getParts();
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
if(usingNewParser)
parsedParts = _newParser.getParsedParts();
else
parsedParts = _oldParser.getParsedParts();
@Override
public Part getPart(String name)
{
try
{
return _utilParser.getPart(name);
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
@Override
public void close()
{
_utilParser.deleteParts();
}
@Override
public boolean isEmpty()
{
return _utilParser.getParsedParts().isEmpty();
}
@Override
public Context getContext()
{
return _context;
}
return parsedParts;
}
}
}

View File

@ -44,7 +44,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@ -70,7 +69,6 @@ import org.eclipse.jetty.toolchain.test.FS;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.MultiPartInputStreamParser;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.log.StacklessLogging;
@ -162,7 +160,7 @@ public class RequestTest
@Override
public boolean check(HttpServletRequest request,HttpServletResponse response)
{
Map<String, String[]> map = request.getParameterMap();
request.getParameterMap();
// should have thrown a BadMessageException
return false;
}
@ -189,7 +187,7 @@ public class RequestTest
@Override
public boolean check(HttpServletRequest request,HttpServletResponse response)
{
Map<String, String[]> map = request.getParameterMap();
request.getParameterMap();
// should have thrown a BadMessageException
return false;
}
@ -364,12 +362,12 @@ public class RequestTest
@Override
public void requestDestroyed(ServletRequestEvent sre)
{
Request.MultiPartInputStream m = (Request.MultiPartInputStream)sre.getServletRequest().getAttribute(Request.__MULTIPART_INPUT_STREAM);
ContextHandler.Context c = (ContextHandler.Context)sre.getServletRequest().getAttribute(Request.__MULTIPART_CONTEXT);
Request.MultiParts m = (Request.MultiParts)sre.getServletRequest().getAttribute(Request.__MULTIPARTS);
assertNotNull (m);
ContextHandler.Context c = m.getContext();
assertNotNull (c);
assertTrue(c == sre.getServletContext());
assertTrue(!m.getParsedParts().isEmpty());
assertTrue(!m.isEmpty());
assertTrue(testTmpDir.list().length == 2);
super.requestDestroyed(sre);
String[] files = testTmpDir.list();
@ -426,9 +424,9 @@ public class RequestTest
@Override
public void requestDestroyed(ServletRequestEvent sre)
{
Request.MultiPartInputStream m = (Request.MultiPartInputStream)sre.getServletRequest().getAttribute(Request.__MULTIPART_INPUT_STREAM);
ContextHandler.Context c = (ContextHandler.Context)sre.getServletRequest().getAttribute(Request.__MULTIPART_CONTEXT);
Request.MultiParts m = (Request.MultiParts)sre.getServletRequest().getAttribute(Request.__MULTIPARTS);
assertNotNull (m);
ContextHandler.Context c = m.getContext();
assertNotNull (c);
assertTrue(c == sre.getServletContext());
super.requestDestroyed(sre);

View File

@ -78,15 +78,28 @@ public class MultiPartInputStreamParser
protected File _contextTmpDir;
protected boolean _deleteOnExit;
protected boolean _writeFilesWithFilenames;
protected boolean _parsed;
private EnumSet<NonCompliance> nonComplianceWarnings = EnumSet.noneOf(NonCompliance.class);
public enum NonCompliance
{
CR_TERMINATION,
LF_TERMINATION,
NO_INITIAL_CRLF,
BASE64_TRANSFER_ENCODING,
QUOTED_PRINTABLE_TRANSFER_ENCODING
CR_LINE_TERMINATION("https://tools.ietf.org/html/rfc2046#section-4.1.1"),
LF_LINE_TERMINATION("https://tools.ietf.org/html/rfc2046#section-4.1.1"),
NO_CRLF_AFTER_PREAMBLE("https://tools.ietf.org/html/rfc2046#section-5.1.1"),
BASE64_TRANSFER_ENCODING("https://tools.ietf.org/html/rfc7578#section-4.7"),
QUOTED_PRINTABLE_TRANSFER_ENCODING("https://tools.ietf.org/html/rfc7578#section-4.7");
final String _rfcRef;
NonCompliance(String rfcRef)
{
_rfcRef = rfcRef;
}
public String getURL()
{
return _rfcRef;
}
}
/**
@ -414,6 +427,7 @@ public class MultiPartInputStreamParser
if (((ServletInputStream)in).isFinished())
{
_parts = EMPTY_MAP;
_parsed = true;
return;
}
}
@ -441,12 +455,12 @@ public class MultiPartInputStreamParser
/**
* Delete any tmp storage for parts, and clear out the parts list.
*
* @throws MultiException if unable to delete the parts
*/
public void deleteParts ()
throws MultiException
{
if(!_parsed)
return;
Collection<Part> parts = getParsedParts();
MultiException err = new MultiException();
for (Part p:parts)
@ -462,7 +476,7 @@ public class MultiPartInputStreamParser
}
_parts.clear();
err.ifExceptionThrowMulti();
err.ifExceptionThrowRuntime();
}
@ -475,6 +489,7 @@ public class MultiPartInputStreamParser
public Collection<Part> getParts()
throws IOException
{
if(!_parsed)
parse();
throwIfError();
@ -500,6 +515,7 @@ public class MultiPartInputStreamParser
public Part getPart(String name)
throws IOException
{
if(_parsed)
parse();
throwIfError();
return _parts.getValue(name, 0);
@ -530,8 +546,9 @@ public class MultiPartInputStreamParser
protected void parse ()
{
//have we already parsed the input?
if (_parts != null || _err != null)
if (_parsed)
return;
_parsed = true;
//initialize
@ -616,7 +633,7 @@ public class MultiPartInputStreamParser
// check compliance of preamble
if (Character.isWhitespace(untrimmed.charAt(0)))
nonComplianceWarnings.add(NonCompliance.NO_INITIAL_CRLF);
nonComplianceWarnings.add(NonCompliance.NO_CRLF_AFTER_PREAMBLE);
// Read each part
boolean lastPart=false;
@ -847,9 +864,9 @@ public class MultiPartInputStreamParser
EnumSet<Termination> term = ((ReadLineInputStream)_in).getLineTerminations();
if(term.contains(Termination.CR))
nonComplianceWarnings.add(NonCompliance.CR_TERMINATION);
nonComplianceWarnings.add(NonCompliance.CR_LINE_TERMINATION);
if(term.contains(Termination.LF))
nonComplianceWarnings.add(NonCompliance.LF_TERMINATION);
nonComplianceWarnings.add(NonCompliance.LF_LINE_TERMINATION);
}
else
throw new IOException("Incomplete parts");

View File

@ -378,7 +378,7 @@ public class MultiPartInputStreamTest
IO.copy(stuff.getInputStream(), baos);
assertTrue(baos.toString("US-ASCII").contains("aaaa"));
assertEquals(EnumSet.of(NonCompliance.LF_TERMINATION), mpis.getNonComplianceWarnings());
assertEquals(EnumSet.of(NonCompliance.LF_LINE_TERMINATION), mpis.getNonComplianceWarnings());
}
@ -420,7 +420,7 @@ public class MultiPartInputStreamTest
IO.copy(stuff.getInputStream(), baos);
assertTrue(baos.toString("US-ASCII").contains("bbbbb"));
assertEquals(EnumSet.of(NonCompliance.NO_INITIAL_CRLF), mpis.getNonComplianceWarnings());
assertEquals(EnumSet.of(NonCompliance.NO_CRLF_AFTER_PREAMBLE), mpis.getNonComplianceWarnings());
}
@ -631,7 +631,7 @@ public class MultiPartInputStreamTest
IO.copy(p2.getInputStream(), baos);
assertThat(baos.toString("UTF-8"), is("Other"));
assertEquals(EnumSet.of(NonCompliance.LF_TERMINATION), mpis.getNonComplianceWarnings());
assertEquals(EnumSet.of(NonCompliance.LF_LINE_TERMINATION), mpis.getNonComplianceWarnings());
}
@Test
@ -671,7 +671,7 @@ public class MultiPartInputStreamTest
IO.copy(p2.getInputStream(), baos);
assertThat(baos.toString("UTF-8"), is("Other"));
assertEquals(EnumSet.of(NonCompliance.CR_TERMINATION), mpis.getNonComplianceWarnings());
assertEquals(EnumSet.of(NonCompliance.CR_LINE_TERMINATION), mpis.getNonComplianceWarnings());
}
@Test
@ -710,7 +710,7 @@ public class MultiPartInputStreamTest
IO.copy(p2.getInputStream(), baos);
assertThat(baos.toString("UTF-8"), is("Other"));
assertEquals(EnumSet.of(NonCompliance.CR_TERMINATION), mpis.getNonComplianceWarnings());
assertEquals(EnumSet.of(NonCompliance.CR_LINE_TERMINATION), mpis.getNonComplianceWarnings());
}
@Test