Use UTF-8 for part headers
Signed-off-by: Greg Wilkins <gregw@webtide.com>
This commit is contained in:
parent
6ac5970680
commit
a756ac50c6
|
@ -26,87 +26,15 @@ import java.util.EnumSet;
|
||||||
import org.eclipse.jetty.http.HttpParser.RequestHandler;
|
import org.eclipse.jetty.http.HttpParser.RequestHandler;
|
||||||
import org.eclipse.jetty.util.BufferUtil;
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
import org.eclipse.jetty.util.SearchPattern;
|
import org.eclipse.jetty.util.SearchPattern;
|
||||||
|
import org.eclipse.jetty.util.Utf8StringBuilder;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/*
|
/** A parser for MultiPart content type.
|
||||||
* RFC2046 and RFC7578
|
|
||||||
*
|
*
|
||||||
* example:
|
* @see https://tools.ietf.org/html/rfc2046#section-5.1
|
||||||
------WebKitFormBoundaryzWHSH95mOxQwmKln
|
* @see https://tools.ietf.org/html/rfc2045
|
||||||
Content-Disposition: form-data; name="TextField"
|
|
||||||
|
|
||||||
Text value:;"' x ----
|
|
||||||
------WebKitFormBoundaryzWHSH95mOxQwmKln
|
|
||||||
Content-Disposition: form-data; name="file1"; filename="file with :%22; in name.txt"
|
|
||||||
Content-Type: text/plain
|
|
||||||
|
|
||||||
|
|
||||||
------WebKitFormBoundaryzWHSH95mOxQwmKln
|
|
||||||
Content-Disposition: form-data; name="file2"; filename="ManagedSelector.java"
|
|
||||||
Content-Type: text/x-java
|
|
||||||
|
|
||||||
|
|
||||||
------WebKitFormBoundaryzWHSH95mOxQwmKln
|
|
||||||
Content-Disposition: form-data; name="Action"
|
|
||||||
|
|
||||||
Submit
|
|
||||||
------WebKitFormBoundaryzWHSH95mOxQwmKln--
|
|
||||||
*
|
|
||||||
* BNF:
|
|
||||||
*
|
|
||||||
boundary := 0*69<bchars> bcharsnospace
|
|
||||||
|
|
||||||
bchars := bcharsnospace / " "
|
|
||||||
|
|
||||||
bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
|
|
||||||
"+" / "_" / "," / "-" / "." /
|
|
||||||
"/" / ":" / "=" / "?"
|
|
||||||
|
|
||||||
dash-boundary := "--" boundary
|
|
||||||
; boundary taken from the value of
|
|
||||||
; boundary parameter of the
|
|
||||||
; Content-Type field.
|
|
||||||
|
|
||||||
multipart-body := [preamble CRLF]
|
|
||||||
dash-boundary transport-padding CRLF
|
|
||||||
body-part *encapsulation
|
|
||||||
close-delimiter transport-padding
|
|
||||||
[CRLF epilogue]
|
|
||||||
|
|
||||||
|
|
||||||
transport-padding := *LWSP-char
|
|
||||||
; Composers MUST NOT generate
|
|
||||||
; non-zero length transport
|
|
||||||
; padding, but receivers MUST
|
|
||||||
; be able to handle padding
|
|
||||||
; added by message transports.
|
|
||||||
|
|
||||||
encapsulation := delimiter transport-padding
|
|
||||||
CRLF body-part
|
|
||||||
|
|
||||||
delimiter := CRLF dash-boundary
|
|
||||||
|
|
||||||
close-delimiter := delimiter "--"
|
|
||||||
|
|
||||||
preamble := discard-text
|
|
||||||
|
|
||||||
epilogue := discard-text
|
|
||||||
|
|
||||||
discard-text := *(*text CRLF) *text
|
|
||||||
; May be ignored or discarded.
|
|
||||||
|
|
||||||
body-part := MIME-part-headers [CRLF *OCTET]
|
|
||||||
; Lines in a body-part must not start
|
|
||||||
; with the specified dash-boundary and
|
|
||||||
; the delimiter must not appear anywhere
|
|
||||||
; in the body part. Note that the
|
|
||||||
; semantics of a body-part differ from
|
|
||||||
; the semantics of a message, as
|
|
||||||
; described in the text.
|
|
||||||
|
|
||||||
OCTET := <any 0-255 octet value>
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class MultiPartParser
|
public class MultiPartParser
|
||||||
|
@ -161,7 +89,7 @@ public class MultiPartParser
|
||||||
private boolean _cr;
|
private boolean _cr;
|
||||||
private ByteBuffer _patternBuffer;
|
private ByteBuffer _patternBuffer;
|
||||||
|
|
||||||
private final StringBuilder _string = new StringBuilder();
|
private final Utf8StringBuilder _string = new Utf8StringBuilder();
|
||||||
private int _length;
|
private int _length;
|
||||||
|
|
||||||
private int _totalHeaderLineLength = -1;
|
private int _totalHeaderLineLength = -1;
|
||||||
|
@ -303,17 +231,22 @@ public class MultiPartParser
|
||||||
/* ------------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------------- */
|
||||||
private void setString(String s)
|
private void setString(String s)
|
||||||
{
|
{
|
||||||
_string.setLength(0);
|
_string.reset();
|
||||||
_string.append(s);
|
_string.append(s);
|
||||||
_length = s.length();
|
_length = s.length();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------------- */
|
||||||
|
/*
|
||||||
|
* Mime Field strings are treated as UTF-8 as per https://tools.ietf.org/html/rfc7578#section-5.1
|
||||||
|
*/
|
||||||
private String takeString()
|
private String takeString()
|
||||||
{
|
{
|
||||||
_string.setLength(_length);
|
|
||||||
String s = _string.toString();
|
String s = _string.toString();
|
||||||
_string.setLength(0);
|
// trim trailing whitespace.
|
||||||
|
if (s.length()>_length)
|
||||||
|
s = s.substring(0,_length);
|
||||||
|
_string.reset();
|
||||||
_length = -1;
|
_length = -1;
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
@ -497,7 +430,7 @@ public class MultiPartParser
|
||||||
|
|
||||||
if (_fieldValue == null)
|
if (_fieldValue == null)
|
||||||
{
|
{
|
||||||
_string.setLength(0);
|
_string.reset();
|
||||||
_length = 0;
|
_length = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -528,8 +461,8 @@ public class MultiPartParser
|
||||||
|
|
||||||
// New header
|
// New header
|
||||||
setState(FieldState.IN_NAME);
|
setState(FieldState.IN_NAME);
|
||||||
_string.setLength(0);
|
_string.reset();
|
||||||
_string.append((char)b);
|
_string.append(b);
|
||||||
_length = 1;
|
_length = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -550,7 +483,7 @@ public class MultiPartParser
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
_string.append((char)b);
|
_string.append(b);
|
||||||
_length = _string.length();
|
_length = _string.length();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -567,7 +500,7 @@ public class MultiPartParser
|
||||||
|
|
||||||
case LINE_FEED:
|
case LINE_FEED:
|
||||||
_fieldName = takeString();
|
_fieldName = takeString();
|
||||||
_string.setLength(0);
|
_string.reset();
|
||||||
_fieldValue = "";
|
_fieldValue = "";
|
||||||
_length = -1;
|
_length = -1;
|
||||||
break;
|
break;
|
||||||
|
@ -584,7 +517,7 @@ public class MultiPartParser
|
||||||
switch (b)
|
switch (b)
|
||||||
{
|
{
|
||||||
case LINE_FEED:
|
case LINE_FEED:
|
||||||
_string.setLength(0);
|
_string.reset();
|
||||||
_fieldValue = "";
|
_fieldValue = "";
|
||||||
_length = -1;
|
_length = -1;
|
||||||
|
|
||||||
|
@ -596,7 +529,7 @@ public class MultiPartParser
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
_string.append((char)(0xff & b));
|
_string.append(b);
|
||||||
_length = _string.length();
|
_length = _string.length();
|
||||||
setState(FieldState.IN_VALUE);
|
setState(FieldState.IN_VALUE);
|
||||||
break;
|
break;
|
||||||
|
@ -607,7 +540,7 @@ public class MultiPartParser
|
||||||
switch (b)
|
switch (b)
|
||||||
{
|
{
|
||||||
case SPACE:
|
case SPACE:
|
||||||
_string.append((char)(0xff & b));
|
_string.append(b);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LINE_FEED:
|
case LINE_FEED:
|
||||||
|
@ -621,7 +554,7 @@ public class MultiPartParser
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
_string.append((char)(0xff & b));
|
_string.append(b);
|
||||||
if (b > SPACE || b < 0)
|
if (b > SPACE || b < 0)
|
||||||
_length = _string.length();
|
_length = _string.length();
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -231,10 +231,8 @@ public class MultiPartCaptureTest
|
||||||
// Evaluate expected contents checksums
|
// Evaluate expected contents checksums
|
||||||
for (NameValue expected : multipartExpectations.partSha1sums)
|
for (NameValue expected : multipartExpectations.partSha1sums)
|
||||||
{
|
{
|
||||||
System.err.println(expected.name);
|
|
||||||
Part part = getPart.apply(expected.name);
|
Part part = getPart.apply(expected.name);
|
||||||
assertThat("Part[" + expected.name + "]", part, is(notNullValue()));
|
assertThat("Part[" + expected.name + "]", part, is(notNullValue()));
|
||||||
System.err.println(BufferUtil.toDetailString(BufferUtil.toBuffer(IO.readBytes(part.getInputStream()))));
|
|
||||||
MessageDigest digest = MessageDigest.getInstance("SHA1");
|
MessageDigest digest = MessageDigest.getInstance("SHA1");
|
||||||
try (InputStream partInputStream = part.getInputStream();
|
try (InputStream partInputStream = part.getInputStream();
|
||||||
NoOpOutputStream noop = new NoOpOutputStream();
|
NoOpOutputStream noop = new NoOpOutputStream();
|
||||||
|
|
Loading…
Reference in New Issue