Merged branch 'jetty-9.3.x' into 'master'.

This commit is contained in:
Simone Bordet 2015-10-27 15:33:21 +01:00
commit 3789c73ba3
4 changed files with 340 additions and 51 deletions

View File

@ -0,0 +1,270 @@
//
// ========================================================================
// Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.client.util;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Random;
import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import org.eclipse.jetty.client.AbstractHttpClientServerTest;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.util.Fields;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.junit.Assert;
import org.junit.Test;
public class MultiPartContentProviderTest extends AbstractHttpClientServerTest
{
public MultiPartContentProviderTest(SslContextFactory sslContextFactory)
{
super(sslContextFactory);
}
@Test
public void testEmptyMultiPart() throws Exception
{
start(new AbstractMultiPartHandler()
{
@Override
protected void handle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
Collection<Part> parts = request.getParts();
Assert.assertEquals(0, parts.size());
}
});
MultiPartContentProvider multiPart = new MultiPartContentProvider();
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
.method(HttpMethod.POST)
.content(multiPart)
.send();
Assert.assertEquals(200, response.getStatus());
}
@Test
public void testSimpleField() throws Exception
{
String name = "field";
String value = "value";
start(new AbstractMultiPartHandler()
{
@Override
protected void handle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
Collection<Part> parts = request.getParts();
Assert.assertEquals(1, parts.size());
Part part = parts.iterator().next();
Assert.assertEquals(name, part.getName());
Assert.assertEquals(value, IO.toString(part.getInputStream()));
}
});
MultiPartContentProvider multiPart = new MultiPartContentProvider();
multiPart.addPart(new MultiPartContentProvider.FieldPart(name, value, null));
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
.method(HttpMethod.POST)
.content(multiPart)
.send();
Assert.assertEquals(200, response.getStatus());
}
@Test
public void testFieldWithContentType() throws Exception
{
String name = "field";
String value = "\u20ac";
Charset encoding = StandardCharsets.UTF_8;
start(new AbstractMultiPartHandler()
{
@Override
protected void handle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
Collection<Part> parts = request.getParts();
Assert.assertEquals(1, parts.size());
Part part = parts.iterator().next();
Assert.assertEquals(name, part.getName());
String contentType = part.getContentType();
Assert.assertNotNull(contentType);
int equal = contentType.lastIndexOf('=');
Charset charset = Charset.forName(contentType.substring(equal + 1));
Assert.assertEquals(encoding, charset);
Assert.assertEquals(value, IO.toString(part.getInputStream(), charset));
}
});
MultiPartContentProvider multiPart = new MultiPartContentProvider();
multiPart.addPart(new MultiPartContentProvider.FieldPart(name, value, encoding));
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
.method(HttpMethod.POST)
.content(multiPart)
.send();
Assert.assertEquals(200, response.getStatus());
}
@Test
public void testOnlyFile() throws Exception
{
// Prepare a file to upload.
String data = "multipart_test_\u20ac";
Path tmpDir = MavenTestingUtils.getTargetTestingPath();
Path tmpPath = Files.createTempFile(tmpDir, "multipart_", ".txt");
Charset encoding = StandardCharsets.UTF_8;
try (BufferedWriter writer = Files.newBufferedWriter(tmpPath, encoding, StandardOpenOption.CREATE))
{
writer.write(data);
}
String name = "file";
String contentType = "text/plain; charset=" + encoding.name();
start(new AbstractMultiPartHandler()
{
@Override
protected void handle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
Collection<Part> parts = request.getParts();
Assert.assertEquals(1, parts.size());
Part part = parts.iterator().next();
Assert.assertEquals(name, part.getName());
Assert.assertEquals(contentType, part.getContentType());
Assert.assertEquals(tmpPath.getFileName().toString(), part.getSubmittedFileName());
Assert.assertEquals(Files.size(tmpPath), part.getSize());
Assert.assertEquals(data, IO.toString(part.getInputStream(), encoding));
}
});
MultiPartContentProvider multiPart = new MultiPartContentProvider();
multiPart.addPart(new MultiPartContentProvider.PathPart(name, tmpPath, contentType));
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
.method(HttpMethod.POST)
.content(multiPart)
.send();
Assert.assertEquals(200, response.getStatus());
Files.delete(tmpPath);
}
@Test
public void testFieldWithFile() throws Exception
{
// Prepare a file to upload.
byte[] data = new byte[1024];
new Random().nextBytes(data);
Path tmpDir = MavenTestingUtils.getTargetTestingPath();
Path tmpPath = Files.createTempFile(tmpDir, "multipart_", ".txt");
try (OutputStream output = Files.newOutputStream(tmpPath, StandardOpenOption.CREATE))
{
output.write(data);
}
String field = "field";
String value = "\u20ac";
String fileField = "file";
Charset encoding = StandardCharsets.UTF_8;
String contentType = "text/plain; charset=" + encoding.name();
String headerName = "foo";
String headerValue = "bar";
start(new AbstractMultiPartHandler()
{
@Override
protected void handle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
List<Part> parts = new ArrayList<>(request.getParts());
Assert.assertEquals(2, parts.size());
Part fieldPart = parts.get(0);
Part filePart = parts.get(1);
if (!field.equals(fieldPart.getName()))
{
Part swap = filePart;
filePart = fieldPart;
fieldPart = swap;
}
Assert.assertEquals(field, fieldPart.getName());
Assert.assertEquals(contentType, fieldPart.getContentType());
Assert.assertEquals(value, IO.toString(fieldPart.getInputStream(), encoding));
Assert.assertEquals(headerValue, fieldPart.getHeader(headerName));
Assert.assertEquals(fileField, filePart.getName());
Assert.assertEquals("application/octet-stream", filePart.getContentType());
Assert.assertEquals(tmpPath.getFileName().toString(), filePart.getSubmittedFileName());
Assert.assertEquals(Files.size(tmpPath), filePart.getSize());
Assert.assertArrayEquals(data, IO.readBytes(filePart.getInputStream()));
}
});
MultiPartContentProvider multiPart = new MultiPartContentProvider();
Fields fields = new Fields();
fields.put("Content-Type", contentType);
fields.put(headerName, headerValue);
multiPart.addPart(new MultiPartContentProvider.FieldPart(field, encoding.encode(value), fields));
multiPart.addPart(new MultiPartContentProvider.PathPart(fileField, tmpPath));
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
.method(HttpMethod.POST)
.content(multiPart)
.send();
Assert.assertEquals(200, response.getStatus());
Files.delete(tmpPath);
}
private static abstract class AbstractMultiPartHandler extends AbstractHandler
{
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
baseRequest.setHandled(true);
File tmpDir = MavenTestingUtils.getTargetTestingDir();
request.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, new MultipartConfigElement(tmpDir.getAbsolutePath()));
handle(request, response);
}
protected abstract void handle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
}
}

View File

@ -20,8 +20,11 @@ package org.eclipse.jetty.fcgi.server.proxy;
import java.net.URI; import java.net.URI;
import java.util.List; import java.util.List;
import java.util.TreeMap;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.servlet.RequestDispatcher; import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig; import javax.servlet.ServletConfig;
import javax.servlet.ServletException; import javax.servlet.ServletException;
@ -32,6 +35,7 @@ import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.fcgi.FCGI; import org.eclipse.jetty.fcgi.FCGI;
import org.eclipse.jetty.fcgi.client.http.HttpClientTransportOverFCGI; import org.eclipse.jetty.fcgi.client.http.HttpClientTransportOverFCGI;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.http.HttpScheme;
@ -212,6 +216,16 @@ public class FastCGIProxyServlet extends AsyncProxyServlet.Transparent
{ {
super.customize(request, fastCGIHeaders); super.customize(request, fastCGIHeaders);
customizeFastCGIHeaders(request, fastCGIHeaders); customizeFastCGIHeaders(request, fastCGIHeaders);
if (_log.isDebugEnabled())
{
TreeMap<String, String> fcgi = new TreeMap<>();
for (HttpField field : fastCGIHeaders)
fcgi.put(field.getName(), field.getValue());
String eol = System.lineSeparator();
_log.debug("FastCGI variables{}{}", eol, fcgi.entrySet().stream()
.map(entry -> String.format("%s: %s", entry.getKey(), entry.getValue()))
.collect(Collectors.joining(eol)));
}
} }
} }
} }

View File

@ -127,7 +127,7 @@ public class MultiPartInputStreamParser
if (MultiPartInputStreamParser.this._config.getFileSizeThreshold() > 0 && _size + length > MultiPartInputStreamParser.this._config.getFileSizeThreshold() && _file==null) if (MultiPartInputStreamParser.this._config.getFileSizeThreshold() > 0 && _size + length > MultiPartInputStreamParser.this._config.getFileSizeThreshold() && _file==null)
createFile(); createFile();
_out.write(bytes, offset, length); _out.write(bytes, offset, length);
_size += length; _size += length;
} }
@ -136,7 +136,7 @@ public class MultiPartInputStreamParser
throws IOException throws IOException
{ {
_file = File.createTempFile("MultiPart", "", MultiPartInputStreamParser.this._tmpDir); _file = File.createTempFile("MultiPart", "", MultiPartInputStreamParser.this._tmpDir);
if (_deleteOnExit) if (_deleteOnExit)
_file.deleteOnExit(); _file.deleteOnExit();
FileOutputStream fos = new FileOutputStream(_file); FileOutputStream fos = new FileOutputStream(_file);
@ -175,7 +175,7 @@ public class MultiPartInputStreamParser
{ {
if (name == null) if (name == null)
return null; return null;
return (String)_headers.getValue(name.toLowerCase(Locale.ENGLISH), 0); return _headers.getValue(name.toLowerCase(Locale.ENGLISH), 0);
} }
/** /**
@ -211,8 +211,8 @@ public class MultiPartInputStreamParser
} }
} }
/** /**
* @see javax.servlet.http.Part#getSubmittedFileName() * @see javax.servlet.http.Part#getSubmittedFileName()
*/ */
@Override @Override
@ -241,7 +241,7 @@ public class MultiPartInputStreamParser
*/ */
public long getSize() public long getSize()
{ {
return _size; return _size;
} }
/** /**
@ -252,7 +252,7 @@ public class MultiPartInputStreamParser
if (_file == null) if (_file == null)
{ {
_temporary = false; _temporary = false;
//part data is only in the ByteArrayOutputStream and never been written to disk //part data is only in the ByteArrayOutputStream and never been written to disk
_file = new File (_tmpDir, fileName); _file = new File (_tmpDir, fileName);
@ -290,12 +290,12 @@ public class MultiPartInputStreamParser
public void delete() throws IOException public void delete() throws IOException
{ {
if (_file != null && _file.exists()) if (_file != null && _file.exists())
_file.delete(); _file.delete();
} }
/** /**
* Only remove tmp files. * Only remove tmp files.
* *
* @throws IOException if unable to delete the file * @throws IOException if unable to delete the file
*/ */
public void cleanUp() throws IOException public void cleanUp() throws IOException
@ -342,7 +342,7 @@ public class MultiPartInputStreamParser
_contextTmpDir = contextTmpDir; _contextTmpDir = contextTmpDir;
if (_contextTmpDir == null) if (_contextTmpDir == null)
_contextTmpDir = new File (System.getProperty("java.io.tmpdir")); _contextTmpDir = new File (System.getProperty("java.io.tmpdir"));
if (_config == null) if (_config == null)
_config = new MultipartConfigElement(_contextTmpDir.getAbsolutePath()); _config = new MultipartConfigElement(_contextTmpDir.getAbsolutePath());
} }
@ -357,7 +357,7 @@ public class MultiPartInputStreamParser
return Collections.emptyList(); return Collections.emptyList();
Collection<List<Part>> values = _parts.values(); Collection<List<Part>> values = _parts.values();
List<Part> parts = new ArrayList<Part>(); List<Part> parts = new ArrayList<>();
for (List<Part> o: values) for (List<Part> o: values)
{ {
List<Part> asList = LazyList.getList(o, false); List<Part> asList = LazyList.getList(o, false);
@ -368,7 +368,7 @@ public class MultiPartInputStreamParser
/** /**
* Delete any tmp storage for parts, and clear out the parts list. * Delete any tmp storage for parts, and clear out the parts list.
* *
* @throws MultiException if unable to delete the parts * @throws MultiException if unable to delete the parts
*/ */
public void deleteParts () public void deleteParts ()
@ -381,22 +381,22 @@ public class MultiPartInputStreamParser
try try
{ {
((MultiPartInputStreamParser.MultiPart)p).cleanUp(); ((MultiPartInputStreamParser.MultiPart)p).cleanUp();
} }
catch(Exception e) catch(Exception e)
{ {
err.add(e); err.add(e);
} }
} }
_parts.clear(); _parts.clear();
err.ifExceptionThrowMulti(); err.ifExceptionThrowMulti();
} }
/** /**
* Parse, if necessary, the multipart data and return the list of Parts. * Parse, if necessary, the multipart data and return the list of Parts.
* *
* @return the parts * @return the parts
* @throws IOException if unable to get the parts * @throws IOException if unable to get the parts
*/ */
public Collection<Part> getParts() public Collection<Part> getParts()
@ -404,7 +404,7 @@ public class MultiPartInputStreamParser
{ {
parse(); parse();
Collection<List<Part>> values = _parts.values(); Collection<List<Part>> values = _parts.values();
List<Part> parts = new ArrayList<Part>(); List<Part> parts = new ArrayList<>();
for (List<Part> o: values) for (List<Part> o: values)
{ {
List<Part> asList = LazyList.getList(o, false); List<Part> asList = LazyList.getList(o, false);
@ -416,7 +416,7 @@ public class MultiPartInputStreamParser
/** /**
* Get the named Part. * Get the named Part.
* *
* @param name the part name * @param name the part name
* @return the parts * @return the parts
* @throws IOException if unable to get the part * @throws IOException if unable to get the part
@ -425,13 +425,13 @@ public class MultiPartInputStreamParser
throws IOException throws IOException
{ {
parse(); parse();
return (Part)_parts.getValue(name, 0); return _parts.getValue(name, 0);
} }
/** /**
* Parse, if necessary, the multipart stream. * Parse, if necessary, the multipart stream.
* *
* @throws IOException if unable to parse * @throws IOException if unable to parse
*/ */
protected void parse () protected void parse ()
@ -443,7 +443,7 @@ public class MultiPartInputStreamParser
//initialize //initialize
long total = 0; //keep running total of size of bytes read from input and throw an exception if exceeds MultipartConfigElement._maxRequestSize long total = 0; //keep running total of size of bytes read from input and throw an exception if exceeds MultipartConfigElement._maxRequestSize
_parts = new MultiMap<Part>(); _parts = new MultiMap<>();
//if its not a multipart request, don't parse it //if its not a multipart request, don't parse it
if (_contentType == null || !_contentType.startsWith("multipart/form-data")) if (_contentType == null || !_contentType.startsWith("multipart/form-data"))
@ -475,28 +475,29 @@ public class MultiPartInputStreamParser
bend = (bend < 0? _contentType.length(): bend); bend = (bend < 0? _contentType.length(): bend);
contentTypeBoundary = QuotedStringTokenizer.unquote(value(_contentType.substring(bstart,bend)).trim()); contentTypeBoundary = QuotedStringTokenizer.unquote(value(_contentType.substring(bstart,bend)).trim());
} }
String boundary="--"+contentTypeBoundary; String boundary="--"+contentTypeBoundary;
byte[] byteBoundary=(boundary+"--").getBytes(StandardCharsets.ISO_8859_1); String lastBoundary=boundary+"--";
byte[] byteBoundary=lastBoundary.getBytes(StandardCharsets.ISO_8859_1);
// Get first boundary // Get first boundary
String line = null; String line = null;
try try
{ {
line=((ReadLineInputStream)_in).readLine(); line=((ReadLineInputStream)_in).readLine();
} }
catch (IOException e) catch (IOException e)
{ {
LOG.warn("Badly formatted multipart request"); LOG.warn("Badly formatted multipart request");
throw e; throw e;
} }
if (line == null) if (line == null)
throw new IOException("Missing content for multipart request"); throw new IOException("Missing content for multipart request");
boolean badFormatLogged = false; boolean badFormatLogged = false;
line=line.trim(); line=line.trim();
while (line != null && !line.equals(boundary)) while (line != null && !line.equals(boundary) && !line.equals(lastBoundary))
{ {
if (!badFormatLogged) if (!badFormatLogged)
{ {
@ -510,6 +511,10 @@ public class MultiPartInputStreamParser
if (line == null) if (line == null)
throw new IOException("Missing initial multi part boundary"); throw new IOException("Missing initial multi part boundary");
// Empty multipart.
if (line.equals(lastBoundary))
return;
// Read each part // Read each part
boolean lastPart=false; boolean lastPart=false;
@ -518,20 +523,20 @@ public class MultiPartInputStreamParser
String contentDisposition=null; String contentDisposition=null;
String contentType=null; String contentType=null;
String contentTransferEncoding=null; String contentTransferEncoding=null;
MultiMap<String> headers = new MultiMap<String>(); MultiMap<String> headers = new MultiMap<>();
while(true) while(true)
{ {
line=((ReadLineInputStream)_in).readLine(); line=((ReadLineInputStream)_in).readLine();
//No more input //No more input
if(line==null) if(line==null)
break outer; break outer;
//end of headers: //end of headers:
if("".equals(line)) if("".equals(line))
break; break;
total += line.length(); total += line.length();
if (_config.getMaxRequestSize() > 0 && total > _config.getMaxRequestSize()) if (_config.getMaxRequestSize() > 0 && total > _config.getMaxRequestSize())
throw new IllegalStateException ("Request exceeds maxRequestSize ("+_config.getMaxRequestSize()+")"); throw new IllegalStateException ("Request exceeds maxRequestSize ("+_config.getMaxRequestSize()+")");
@ -595,7 +600,7 @@ public class MultiPartInputStreamParser
part.setContentType(contentType); part.setContentType(contentType);
_parts.add(name, part); _parts.add(name, part);
part.open(); part.open();
InputStream partInput = null; InputStream partInput = null;
if ("base64".equalsIgnoreCase(contentTransferEncoding)) if ("base64".equalsIgnoreCase(contentTransferEncoding))
{ {
@ -627,7 +632,7 @@ public class MultiPartInputStreamParser
else else
partInput = _in; partInput = _in;
try try
{ {
int state=-2; int state=-2;
@ -646,7 +651,7 @@ public class MultiPartInputStreamParser
throw new IllegalStateException("Request exceeds maxRequestSize ("+_config.getMaxRequestSize()+")"); throw new IllegalStateException("Request exceeds maxRequestSize ("+_config.getMaxRequestSize()+")");
state=-2; state=-2;
// look for CR and/or LF // look for CR and/or LF
if(c==13||c==10) if(c==13||c==10)
{ {
@ -661,7 +666,7 @@ public class MultiPartInputStreamParser
} }
break; break;
} }
// Look for boundary // Look for boundary
if(b>=0&&b<byteBoundary.length&&c==byteBoundary[b]) if(b>=0&&b<byteBoundary.length&&c==byteBoundary[b])
{ {
@ -685,7 +690,7 @@ public class MultiPartInputStreamParser
part.write(c); part.write(c);
} }
} }
// Check for incomplete boundary match, writing out the chars we matched along the way // 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((b>0&&b<byteBoundary.length-2)||(b==byteBoundary.length-1))
{ {
@ -699,18 +704,18 @@ public class MultiPartInputStreamParser
part.write(byteBoundary,0,b); part.write(byteBoundary,0,b);
b=-1; 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. // 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>0||c==-1)
{ {
if(b==byteBoundary.length) if(b==byteBoundary.length)
lastPart=true; lastPart=true;
if(state==10) if(state==10)
state=-2; state=-2;
break; break;
} }
// handle CR LF // handle CR LF
if(cr) if(cr)
part.write(13); part.write(13);
@ -733,7 +738,7 @@ public class MultiPartInputStreamParser
if (!lastPart) if (!lastPart)
throw new IOException("Incomplete parts"); throw new IOException("Incomplete parts");
} }
public void setDeleteOnExit(boolean deleteOnExit) public void setDeleteOnExit(boolean deleteOnExit)
{ {
_deleteOnExit = deleteOnExit; _deleteOnExit = deleteOnExit;
@ -753,8 +758,8 @@ public class MultiPartInputStreamParser
String value = nameEqualsValue.substring(idx+1).trim(); String value = nameEqualsValue.substring(idx+1).trim();
return QuotedStringTokenizer.unquoteOnly(value); return QuotedStringTokenizer.unquoteOnly(value);
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
private String filenameValue(String nameEqualsValue) private String filenameValue(String nameEqualsValue)
{ {
@ -782,7 +787,7 @@ public class MultiPartInputStreamParser
return QuotedStringTokenizer.unquoteOnly(value, true); return QuotedStringTokenizer.unquoteOnly(value, true);
} }
private static class Base64InputStream extends InputStream private static class Base64InputStream extends InputStream
{ {
@ -791,7 +796,7 @@ public class MultiPartInputStreamParser
byte[] _buffer; byte[] _buffer;
int _pos; int _pos;
public Base64InputStream(ReadLineInputStream rlis) public Base64InputStream(ReadLineInputStream rlis)
{ {
_in = rlis; _in = rlis;
@ -806,7 +811,7 @@ public class MultiPartInputStreamParser
//We need to put them back into the bytes returned from this //We need to put them back into the bytes returned from this
//method because the parsing of the multipart content uses them //method because the parsing of the multipart content uses them
//as markers to determine when we've reached the end of a part. //as markers to determine when we've reached the end of a part.
_line = _in.readLine(); _line = _in.readLine();
if (_line==null) if (_line==null)
return -1; //nothing left return -1; //nothing left
if (_line.startsWith("--")) if (_line.startsWith("--"))
@ -824,7 +829,7 @@ public class MultiPartInputStreamParser
_pos=0; _pos=0;
} }
return _buffer[_pos++]; return _buffer[_pos++];
} }
} }