Use UTF-8 for part headers

Signed-off-by: Greg Wilkins <gregw@webtide.com>
This commit is contained in:
Greg Wilkins 2018-03-27 11:45:38 +11:00
parent 6ac5970680
commit a756ac50c6
2 changed files with 22 additions and 91 deletions

View File

@ -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;

View File

@ -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();