Modified:
websites/production/commons/content/proper/commons-fileupload/commons-fileupload2-core/jacoco/org.apache.commons.fileupload2.core/MultipartInput.java.html
==============================================================================
---
websites/production/commons/content/proper/commons-fileupload/commons-fileupload2-core/jacoco/org.apache.commons.fileupload2.core/MultipartInput.java.html
(original)
+++
websites/production/commons/content/proper/commons-fileupload/commons-fileupload2-core/jacoco/org.apache.commons.fileupload2.core/MultipartInput.java.html
Mon Jun 16 14:44:13 2025
@@ -110,12 +110,16 @@ public final class MultipartInput {
*/
private ProgressNotifier progressNotifier;
+ /** The per part size limit for headers.
+ */
+<span class="fc" id="L115"> private int partHeaderSizeMax =
DEFAULT_PART_HEADER_SIZE_MAX;</span>
+
/**
* Constructs a new instance.
*/
-<span class="fc" id="L116"> public Builder() {</span>
-<span class="fc" id="L117">
setBufferSizeDefault(DEFAULT_BUFSIZE);</span>
-<span class="fc" id="L118"> }</span>
+<span class="fc" id="L120"> public Builder() {</span>
+<span class="fc" id="L121">
setBufferSizeDefault(DEFAULT_BUFSIZE);</span>
+<span class="fc" id="L122"> }</span>
/**
* Constructs a new instance.
@@ -134,7 +138,15 @@ public final class MultipartInput {
*/
@Override
public MultipartInput get() throws IOException {
-<span class="fc" id="L137"> return new
MultipartInput(getInputStream(), boundary, getBufferSize(),
progressNotifier);</span>
+<span class="fc" id="L141"> return new
MultipartInput(getInputStream(), boundary, getBufferSize(),
getPartHeaderSizeMax(), progressNotifier);</span>
+ }
+
+ /** Returns the per part size limit for headers.
+ * @return The maximum size of the headers in bytes.
+ * @since 2.0.0-M4
+ */
+ public int getPartHeaderSizeMax() {
+<span class="fc" id="L149"> return partHeaderSizeMax;</span>
}
/**
@@ -144,20 +156,30 @@ public final class MultipartInput {
* @return {@code this} instance.
*/
public Builder setBoundary(final byte[] boundary) {
-<span class="fc" id="L147"> this.boundary = boundary;</span>
-<span class="fc" id="L148"> return this;</span>
+<span class="fc" id="L159"> this.boundary = boundary;</span>
+<span class="fc" id="L160"> return this;</span>
}
+ /** Sets the per part size limit for headers.
+ * @param partHeaderSizeMax The maximum size of the headers in bytes.
+ * @return This builder.
+ * @since 2.0.0-M4
+ */
+ public Builder setPartHeaderSizeMax(final int partHeaderSizeMax) {
+<span class="fc" id="L169"> this.partHeaderSizeMax =
partHeaderSizeMax;</span>
+<span class="fc" id="L170"> return this;</span>
+ }
+
/**
- * Sets the progress notifier.
- *
- * @param progressNotifier progress notifier.
- * @return {@code this} instance.
- */
- public Builder setProgressNotifier(final ProgressNotifier
progressNotifier) {
-<span class="fc" id="L158"> this.progressNotifier =
progressNotifier;</span>
-<span class="fc" id="L159"> return this;</span>
- }
+ * Sets the progress notifier.
+ *
+ * @param progressNotifier progress notifier.
+ * @return {@code this} instance.
+ */
+ public Builder setProgressNotifier(final ProgressNotifier
progressNotifier) {
+<span class="fc" id="L180"> this.progressNotifier =
progressNotifier;</span>
+<span class="fc" id="L181"> return this;</span>
+ }
}
@@ -177,8 +199,8 @@ public final class MultipartInput {
* @param message The detail message (which is saved for later
retrieval by the {@link #getMessage()} method)
*/
public FileUploadBoundaryException(final String message) {
-<span class="nc" id="L180"> super(message);</span>
-<span class="nc" id="L181"> }</span>
+<span class="nc" id="L202"> super(message);</span>
+<span class="nc" id="L203"> }</span>
}
@@ -215,9 +237,9 @@ public final class MultipartInput {
/**
* Creates a new instance.
*/
-<span class="nc" id="L218"> ItemInputStream() {</span>
-<span class="nc" id="L219"> findSeparator();</span>
-<span class="nc" id="L220"> }</span>
+<span class="fc" id="L240"> ItemInputStream() {</span>
+<span class="fc" id="L241"> findSeparator();</span>
+<span class="fc" id="L242"> }</span>
/**
* Returns the number of bytes, which are currently available, without
blocking.
@@ -227,17 +249,17 @@ public final class MultipartInput {
*/
@Override
public int available() throws IOException {
-<span class="nc bnc" id="L230" title="All 2 branches missed."> if
(pos == -1) {</span>
-<span class="nc" id="L231"> return tail - head - pad;</span>
+<span class="fc bfc" id="L252" title="All 2 branches covered."> if
(pos == -1) {</span>
+<span class="fc" id="L253"> return tail - head - pad;</span>
}
-<span class="nc" id="L233"> return pos - head;</span>
+<span class="fc" id="L255"> return pos - head;</span>
}
private void checkOpen() throws ItemSkippedException {
-<span class="nc bnc" id="L237" title="All 2 branches missed."> if
(closed) {</span>
-<span class="nc" id="L238"> throw new
FileItemInput.ItemSkippedException("checkOpen()");</span>
+<span class="pc bpc" id="L259" title="1 of 2 branches missed."> if
(closed) {</span>
+<span class="nc" id="L260"> throw new
FileItemInput.ItemSkippedException("checkOpen()");</span>
}
-<span class="nc" id="L240"> }</span>
+<span class="fc" id="L262"> }</span>
/**
* Closes the input stream.
@@ -246,8 +268,8 @@ public final class MultipartInput {
*/
@Override
public void close() throws IOException {
-<span class="nc" id="L249"> close(false);</span>
-<span class="nc" id="L250"> }</span>
+<span class="fc" id="L271"> close(false);</span>
+<span class="fc" id="L272"> }</span>
/**
* Closes the input stream.
@@ -256,42 +278,42 @@ public final class MultipartInput {
* @throws IOException An I/O error occurred.
*/
public void close(final boolean closeUnderlying) throws IOException {
-<span class="nc bnc" id="L259" title="All 2 branches missed."> if
(closed) {</span>
-<span class="nc" id="L260"> return;</span>
+<span class="pc bpc" id="L281" title="1 of 2 branches missed."> if
(closed) {</span>
+<span class="nc" id="L282"> return;</span>
}
-<span class="nc bnc" id="L262" title="All 2 branches missed."> if
(closeUnderlying) {</span>
-<span class="nc" id="L263"> closed = true;</span>
-<span class="nc" id="L264"> input.close();</span>
+<span class="pc bpc" id="L284" title="1 of 2 branches missed."> if
(closeUnderlying) {</span>
+<span class="nc" id="L285"> closed = true;</span>
+<span class="nc" id="L286"> input.close();</span>
} else {
for (;;) {
-<span class="nc" id="L267"> var avail = available();</span>
-<span class="nc bnc" id="L268" title="All 2 branches missed.">
if (avail == 0) {</span>
-<span class="nc" id="L269"> avail =
makeAvailable();</span>
-<span class="nc bnc" id="L270" title="All 2 branches missed.">
if (avail == 0) {</span>
-<span class="nc" id="L271"> break;</span>
+<span class="fc" id="L289"> var avail = available();</span>
+<span class="pc bpc" id="L290" title="1 of 2 branches missed.">
if (avail == 0) {</span>
+<span class="fc" id="L291"> avail =
makeAvailable();</span>
+<span class="pc bpc" id="L292" title="1 of 2 branches missed.">
if (avail == 0) {</span>
+<span class="fc" id="L293"> break;</span>
}
}
-<span class="nc bnc" id="L274" title="All 2 branches missed.">
if (skip(avail) != avail) {</span>
+<span class="nc bnc" id="L296" title="All 2 branches missed.">
if (skip(avail) != avail) {</span>
// TODO What to do?
}
-<span class="nc" id="L277"> }</span>
+<span class="nc" id="L299"> }</span>
}
-<span class="nc" id="L279"> closed = true;</span>
-<span class="nc" id="L280"> }</span>
+<span class="fc" id="L301"> closed = true;</span>
+<span class="fc" id="L302"> }</span>
/**
* Called for finding the separator.
*/
private void findSeparator() {
-<span class="nc" id="L286"> pos =
MultipartInput.this.findSeparator();</span>
-<span class="nc bnc" id="L287" title="All 2 branches missed."> if
(pos == -1) {</span>
-<span class="nc bnc" id="L288" title="All 2 branches missed.">
if (tail - head > keepRegion) {</span>
-<span class="nc" id="L289"> pad = keepRegion;</span>
+<span class="fc" id="L308"> pos =
MultipartInput.this.findSeparator();</span>
+<span class="fc bfc" id="L309" title="All 2 branches covered."> if
(pos == -1) {</span>
+<span class="pc bpc" id="L310" title="1 of 2 branches missed.">
if (tail - head > keepRegion) {</span>
+<span class="nc" id="L311"> pad = keepRegion;</span>
} else {
-<span class="nc" id="L291"> pad = tail - head;</span>
+<span class="fc" id="L313"> pad = tail - head;</span>
}
}
-<span class="nc" id="L294"> }</span>
+<span class="fc" id="L316"> }</span>
/**
* Gets the number of bytes, which have been read by the stream.
@@ -299,7 +321,7 @@ public final class MultipartInput {
* @return Number of bytes, which have been read so far.
*/
public long getBytesRead() {
-<span class="nc" id="L302"> return total;</span>
+<span class="nc" id="L324"> return total;</span>
}
/**
@@ -308,7 +330,7 @@ public final class MultipartInput {
* @return whether this instance is closed.
*/
public boolean isClosed() {
-<span class="nc" id="L311"> return closed;</span>
+<span class="nc" id="L333"> return closed;</span>
}
/**
@@ -318,39 +340,39 @@ public final class MultipartInput {
* @throws IOException An I/O error occurred.
*/
private int makeAvailable() throws IOException {
-<span class="nc bnc" id="L321" title="All 2 branches missed."> if
(pos != -1) {</span>
-<span class="nc" id="L322"> return 0;</span>
+<span class="fc bfc" id="L343" title="All 2 branches covered."> if
(pos != -1) {</span>
+<span class="fc" id="L344"> return 0;</span>
}
// Move the data to the beginning of the buffer.
-<span class="nc" id="L326"> total += tail - head - pad;</span>
-<span class="nc" id="L327"> System.arraycopy(buffer, tail - pad,
buffer, 0, pad);</span>
+<span class="fc" id="L348"> total += tail - head - pad;</span>
+<span class="fc" id="L349"> System.arraycopy(buffer, tail - pad,
buffer, 0, pad);</span>
// Refill buffer with new data.
-<span class="nc" id="L330"> head = 0;</span>
-<span class="nc" id="L331"> tail = pad;</span>
+<span class="fc" id="L352"> head = 0;</span>
+<span class="fc" id="L353"> tail = pad;</span>
for (;;) {
-<span class="nc" id="L334"> final var bytesRead =
input.read(buffer, tail, bufSize - tail);</span>
-<span class="nc bnc" id="L335" title="All 2 branches missed.">
if (bytesRead == -1) {</span>
+<span class="fc" id="L356"> final var bytesRead =
input.read(buffer, tail, bufSize - tail);</span>
+<span class="pc bpc" id="L357" title="1 of 2 branches missed.">
if (bytesRead == -1) {</span>
// The last pad amount is left in the buffer.
// Boundary can't be in there so signal an error
// condition.
-<span class="nc" id="L339"> final var msg = "Stream
ended unexpectedly";</span>
-<span class="nc" id="L340"> throw new
MalformedStreamException(msg);</span>
+<span class="nc" id="L361"> final var msg = "Stream
ended unexpectedly";</span>
+<span class="nc" id="L362"> throw new
MalformedStreamException(msg);</span>
}
-<span class="nc bnc" id="L342" title="All 2 branches missed.">
if (notifier != null) {</span>
-<span class="nc" id="L343">
notifier.noteBytesRead(bytesRead);</span>
+<span class="pc bpc" id="L364" title="1 of 2 branches missed.">
if (notifier != null) {</span>
+<span class="nc" id="L365">
notifier.noteBytesRead(bytesRead);</span>
}
-<span class="nc" id="L345"> tail += bytesRead;</span>
+<span class="fc" id="L367"> tail += bytesRead;</span>
-<span class="nc" id="L347"> findSeparator();</span>
-<span class="nc" id="L348"> final var av = available();</span>
+<span class="fc" id="L369"> findSeparator();</span>
+<span class="fc" id="L370"> final var av = available();</span>
-<span class="nc bnc" id="L350" title="All 4 branches missed.">
if (av > 0 || pos != -1) {</span>
-<span class="nc" id="L351"> return av;</span>
+<span class="pc bpc" id="L372" title="2 of 4 branches missed.">
if (av > 0 || pos != -1) {</span>
+<span class="fc" id="L373"> return av;</span>
}
-<span class="nc" id="L353"> }</span>
+<span class="nc" id="L375"> }</span>
}
/**
@@ -361,16 +383,16 @@ public final class MultipartInput {
*/
@Override
public int read() throws IOException {
-<span class="nc" id="L364"> checkOpen();</span>
-<span class="nc bnc" id="L365" title="All 4 branches missed."> if
(available() == 0 && makeAvailable() == 0) {</span>
-<span class="nc" id="L366"> return -1;</span>
- }
-<span class="nc" id="L368"> ++total;</span>
-<span class="nc" id="L369"> final int b = buffer[head++];</span>
-<span class="nc bnc" id="L370" title="All 2 branches missed."> if
(b >= 0) {</span>
-<span class="nc" id="L371"> return b;</span>
+<span class="nc" id="L386"> checkOpen();</span>
+<span class="nc bnc" id="L387" title="All 4 branches missed."> if
(available() == 0 && makeAvailable() == 0) {</span>
+<span class="nc" id="L388"> return -1;</span>
+ }
+<span class="nc" id="L390"> ++total;</span>
+<span class="nc" id="L391"> final int b = buffer[head++];</span>
+<span class="nc bnc" id="L392" title="All 2 branches missed."> if
(b >= 0) {</span>
+<span class="nc" id="L393"> return b;</span>
}
-<span class="nc" id="L373"> return b + BYTE_POSITIVE_OFFSET;</span>
+<span class="nc" id="L395"> return b + BYTE_POSITIVE_OFFSET;</span>
}
/**
@@ -384,22 +406,22 @@ public final class MultipartInput {
*/
@Override
public int read(final byte[] b, final int off, final int len) throws
IOException {
-<span class="nc" id="L387"> checkOpen();</span>
-<span class="nc bnc" id="L388" title="All 2 branches missed."> if
(len == 0) {</span>
-<span class="nc" id="L389"> return 0;</span>
- }
-<span class="nc" id="L391"> var res = available();</span>
-<span class="nc bnc" id="L392" title="All 2 branches missed."> if
(res == 0) {</span>
-<span class="nc" id="L393"> res = makeAvailable();</span>
-<span class="nc bnc" id="L394" title="All 2 branches missed.">
if (res == 0) {</span>
-<span class="nc" id="L395"> return -1;</span>
+<span class="fc" id="L409"> checkOpen();</span>
+<span class="pc bpc" id="L410" title="1 of 2 branches missed."> if
(len == 0) {</span>
+<span class="nc" id="L411"> return 0;</span>
+ }
+<span class="fc" id="L413"> var res = available();</span>
+<span class="pc bpc" id="L414" title="1 of 2 branches missed."> if
(res == 0) {</span>
+<span class="fc" id="L415"> res = makeAvailable();</span>
+<span class="pc bpc" id="L416" title="1 of 2 branches missed.">
if (res == 0) {</span>
+<span class="fc" id="L417"> return -1;</span>
}
}
-<span class="nc" id="L398"> res = Math.min(res, len);</span>
-<span class="nc" id="L399"> System.arraycopy(buffer, head, b, off,
res);</span>
-<span class="nc" id="L400"> head += res;</span>
-<span class="nc" id="L401"> total += res;</span>
-<span class="nc" id="L402"> return res;</span>
+<span class="nc" id="L420"> res = Math.min(res, len);</span>
+<span class="nc" id="L421"> System.arraycopy(buffer, head, b, off,
res);</span>
+<span class="nc" id="L422"> head += res;</span>
+<span class="nc" id="L423"> total += res;</span>
+<span class="nc" id="L424"> return res;</span>
}
/**
@@ -411,20 +433,20 @@ public final class MultipartInput {
*/
@Override
public long skip(final long bytes) throws IOException {
-<span class="nc" id="L414"> checkOpen();</span>
-<span class="nc" id="L415"> var available = available();</span>
-<span class="nc bnc" id="L416" title="All 2 branches missed."> if
(available == 0) {</span>
-<span class="nc" id="L417"> available = makeAvailable();</span>
-<span class="nc bnc" id="L418" title="All 2 branches missed.">
if (available == 0) {</span>
-<span class="nc" id="L419"> return 0;</span>
+<span class="nc" id="L436"> checkOpen();</span>
+<span class="nc" id="L437"> var available = available();</span>
+<span class="nc bnc" id="L438" title="All 2 branches missed."> if
(available == 0) {</span>
+<span class="nc" id="L439"> available = makeAvailable();</span>
+<span class="nc bnc" id="L440" title="All 2 branches missed.">
if (available == 0) {</span>
+<span class="nc" id="L441"> return 0;</span>
}
}
// Fix "Implicit narrowing conversion in compound
assignment"
//
https://github.com/apache/commons-fileupload/security/code-scanning/118
// Math.min always returns an int because available is an int.
-<span class="nc" id="L425"> final var res =
Math.toIntExact(Math.min(available, bytes));</span>
-<span class="nc" id="L426"> head += res;</span>
-<span class="nc" id="L427"> return res;</span>
+<span class="nc" id="L447"> final var res =
Math.toIntExact(Math.min(available, bytes));</span>
+<span class="nc" id="L448"> head += res;</span>
+<span class="nc" id="L449"> return res;</span>
}
}
@@ -445,8 +467,8 @@ public final class MultipartInput {
* @param message The detail message.
*/
public MalformedStreamException(final String message) {
-<span class="nc" id="L448"> super(message);</span>
-<span class="nc" id="L449"> }</span>
+<span class="nc" id="L470"> super(message);</span>
+<span class="nc" id="L471"> }</span>
/**
* Constructs an {@code MalformedStreamException} with the specified
detail message.
@@ -456,8 +478,8 @@ public final class MultipartInput {
* cause is nonexistent or unknown.)
*/
public MalformedStreamException(final String message, final Throwable
cause) {
-<span class="nc" id="L459"> super(message, cause);</span>
-<span class="nc" id="L460"> }</span>
+<span class="nc" id="L481"> super(message, cause);</span>
+<span class="nc" id="L482"> }</span>
}
@@ -492,10 +514,10 @@ public final class MultipartInput {
* @param progressListener The listener to invoke.
* @param contentLength The expected content length.
*/
-<span class="fc" id="L495"> public ProgressNotifier(final
ProgressListener progressListener, final long contentLength) {</span>
-<span class="pc bpc" id="L496" title="1 of 2 branches missed.">
this.progressListener = progressListener != null ? progressListener :
ProgressListener.NOP;</span>
-<span class="fc" id="L497"> this.contentLength =
contentLength;</span>
-<span class="fc" id="L498"> }</span>
+<span class="fc" id="L517"> public ProgressNotifier(final
ProgressListener progressListener, final long contentLength) {</span>
+<span class="pc bpc" id="L518" title="1 of 2 branches missed.">
this.progressListener = progressListener != null ? progressListener :
ProgressListener.NOP;</span>
+<span class="fc" id="L519"> this.contentLength =
contentLength;</span>
+<span class="fc" id="L520"> }</span>
/**
* Called to indicate that bytes have been read.
@@ -506,24 +528,24 @@ public final class MultipartInput {
//
// Indicates, that the given number of bytes have been read from
the input stream.
//
-<span class="nc" id="L509"> bytesRead += byteCount;</span>
-<span class="nc" id="L510"> notifyListener();</span>
-<span class="nc" id="L511"> }</span>
+<span class="nc" id="L531"> bytesRead += byteCount;</span>
+<span class="nc" id="L532"> notifyListener();</span>
+<span class="nc" id="L533"> }</span>
/**
* Called to indicate, that a new file item has been detected.
*/
public void noteItem() {
-<span class="nc" id="L517"> ++items;</span>
-<span class="nc" id="L518"> notifyListener();</span>
-<span class="nc" id="L519"> }</span>
+<span class="nc" id="L539"> ++items;</span>
+<span class="nc" id="L540"> notifyListener();</span>
+<span class="nc" id="L541"> }</span>
/**
* Called for notifying the listener.
*/
private void notifyListener() {
-<span class="nc" id="L525"> progressListener.update(bytesRead,
contentLength, items);</span>
-<span class="nc" id="L526"> }</span>
+<span class="nc" id="L547"> progressListener.update(bytesRead,
contentLength, items);</span>
+<span class="nc" id="L548"> }</span>
}
@@ -543,34 +565,35 @@ public final class MultipartInput {
public static final byte DASH = 0x2D;
/**
- * The maximum length of {@code header-part} that will be processed (10
kilobytes = 10240 bytes.).
+ * The default length of the buffer used for processing a request.
*/
- public static final int HEADER_PART_SIZE_MAX = 10_240;
+ static final int DEFAULT_BUFSIZE = 4096;
/**
- * The default length of the buffer used for processing a request.
+ * Default per part header size limit in bytes.
+ * @since 2.0.0-M4
*/
- static final int DEFAULT_BUFSIZE = 4096;
+ public static final int DEFAULT_PART_HEADER_SIZE_MAX = 512;
/**
* A byte sequence that marks the end of {@code header-part} ({@code
CRLFCRLF}).
*/
-<span class="fc" id="L558"> static final byte[] HEADER_SEPARATOR = { CR,
LF, CR, LF };</span>
+<span class="fc" id="L581"> static final byte[] HEADER_SEPARATOR = { CR,
LF, CR, LF };</span>
/**
* A byte sequence that that follows a delimiter that will be followed by
an encapsulation ({@code CRLF}).
*/
-<span class="fc" id="L563"> static final byte[] FIELD_SEPARATOR = { CR, LF
};</span>
+<span class="fc" id="L586"> static final byte[] FIELD_SEPARATOR = { CR, LF
};</span>
/**
* A byte sequence that that follows a delimiter of the last encapsulation
in the stream ({@code --}).
*/
-<span class="fc" id="L568"> static final byte[] STREAM_TERMINATOR = { DASH,
DASH };</span>
+<span class="fc" id="L591"> static final byte[] STREAM_TERMINATOR = { DASH,
DASH };</span>
/**
* A byte sequence that precedes a boundary ({@code CRLF--}).
*/
-<span class="fc" id="L573"> static final byte[] BOUNDARY_PREFIX = { CR, LF,
DASH, DASH };</span>
+<span class="fc" id="L596"> static final byte[] BOUNDARY_PREFIX = { CR, LF,
DASH, DASH };</span>
/**
* Compares {@code count} first bytes in the arrays {@code a} and {@code
b}.
@@ -581,12 +604,12 @@ public final class MultipartInput {
* @return {@code true} if {@code count} first bytes in arrays {@code a}
and {@code b} are equal.
*/
static boolean arrayEquals(final byte[] a, final byte[] b, final int
count) {
-<span class="nc bnc" id="L584" title="All 2 branches missed."> for (var
i = 0; i < count; i++) {</span>
-<span class="nc bnc" id="L585" title="All 2 branches missed."> if
(a[i] != b[i]) {</span>
-<span class="nc" id="L586"> return false;</span>
+<span class="fc bfc" id="L607" title="All 2 branches covered."> for
(var i = 0; i < count; i++) {</span>
+<span class="fc bfc" id="L608" title="All 2 branches covered."> if
(a[i] != b[i]) {</span>
+<span class="fc" id="L609"> return false;</span>
}
}
-<span class="nc" id="L589"> return true;</span>
+<span class="fc" id="L612"> return true;</span>
}
/**
@@ -595,7 +618,7 @@ public final class MultipartInput {
* @return a new {@link Builder}.
*/
public static Builder builder() {
-<span class="fc" id="L598"> return new Builder();</span>
+<span class="fc" id="L621"> return new Builder();</span>
}
/**
@@ -656,6 +679,11 @@ public final class MultipartInput {
private final ProgressNotifier notifier;
/**
+ * The maximum size of the headers in bytes.
+ */
+ private final int partHeaderSizeMax;
+
+ /**
* Constructs a {@code MultipartInput} with a custom size buffer.
* <p>
* Note that the buffer must be at least big enough to contain the
boundary string, plus 4 characters for CR/LF and double dash, plus at least one
byte of
@@ -668,57 +696,58 @@ public final class MultipartInput {
* @param notifier The notifier, which is used for calling the progress
listener, if any.
* @throws IllegalArgumentException If the buffer size is too small.
*/
-<span class="fc" id="L671"> private MultipartInput(final InputStream input,
final byte[] boundary, final int bufferSize, final ProgressNotifier notifier)
{</span>
-<span class="pc bpc" id="L672" title="1 of 2 branches missed."> if
(boundary == null) {</span>
-<span class="nc" id="L673"> throw new
IllegalArgumentException("boundary may not be null");</span>
+<span class="fc" id="L699"> private MultipartInput(final InputStream input,
final byte[] boundary, final int bufferSize, final int partHeaderSizeMax, final
ProgressNotifier notifier) {</span>
+<span class="pc bpc" id="L700" title="1 of 2 branches missed."> if
(boundary == null) {</span>
+<span class="nc" id="L701"> throw new
IllegalArgumentException("boundary may not be null");</span>
}
// We prepend CR/LF to the boundary to chop trailing CR/LF from
// body-data tokens.
-<span class="fc" id="L677"> this.boundaryLength = boundary.length +
BOUNDARY_PREFIX.length;</span>
-<span class="fc bfc" id="L678" title="All 2 branches covered."> if
(bufferSize < this.boundaryLength + 1) {</span>
-<span class="fc" id="L679"> throw new
IllegalArgumentException("The buffer size specified for the MultipartInput
is too small");</span>
- }
-
-<span class="fc" id="L682"> this.input = input;</span>
-<span class="fc" id="L683"> this.bufSize = Math.max(bufferSize,
boundaryLength * 2);</span>
-<span class="fc" id="L684"> this.buffer = new byte[this.bufSize];</span>
-<span class="fc" id="L685"> this.notifier = notifier;</span>
-
-<span class="fc" id="L687"> this.boundary = new
byte[this.boundaryLength];</span>
-<span class="fc" id="L688"> this.boundaryTable = new
int[this.boundaryLength + 1];</span>
-<span class="fc" id="L689"> this.keepRegion =
this.boundary.length;</span>
-
-<span class="fc" id="L691"> System.arraycopy(BOUNDARY_PREFIX, 0,
this.boundary, 0, BOUNDARY_PREFIX.length);</span>
-<span class="fc" id="L692"> System.arraycopy(boundary, 0,
this.boundary, BOUNDARY_PREFIX.length, boundary.length);</span>
-<span class="fc" id="L693"> computeBoundaryTable();</span>
-
-<span class="fc" id="L695"> head = 0;</span>
-<span class="fc" id="L696"> tail = 0;</span>
-<span class="fc" id="L697"> }</span>
+<span class="fc" id="L705"> this.boundaryLength = boundary.length +
BOUNDARY_PREFIX.length;</span>
+<span class="fc bfc" id="L706" title="All 2 branches covered."> if
(bufferSize < this.boundaryLength + 1) {</span>
+<span class="fc" id="L707"> throw new
IllegalArgumentException("The buffer size specified for the MultipartInput
is too small");</span>
+ }
+
+<span class="fc" id="L710"> this.input = input;</span>
+<span class="fc" id="L711"> this.bufSize = Math.max(bufferSize,
boundaryLength * 2);</span>
+<span class="fc" id="L712"> this.buffer = new byte[this.bufSize];</span>
+<span class="fc" id="L713"> this.notifier = notifier;</span>
+<span class="fc" id="L714"> this.partHeaderSizeMax =
partHeaderSizeMax;</span>
+
+<span class="fc" id="L716"> this.boundary = new
byte[this.boundaryLength];</span>
+<span class="fc" id="L717"> this.boundaryTable = new
int[this.boundaryLength + 1];</span>
+<span class="fc" id="L718"> this.keepRegion =
this.boundary.length;</span>
+
+<span class="fc" id="L720"> System.arraycopy(BOUNDARY_PREFIX, 0,
this.boundary, 0, BOUNDARY_PREFIX.length);</span>
+<span class="fc" id="L721"> System.arraycopy(boundary, 0,
this.boundary, BOUNDARY_PREFIX.length, boundary.length);</span>
+<span class="fc" id="L722"> computeBoundaryTable();</span>
+
+<span class="fc" id="L724"> head = 0;</span>
+<span class="fc" id="L725"> tail = 0;</span>
+<span class="fc" id="L726"> }</span>
/**
* Computes the table used for Knuth-Morris-Pratt search algorithm.
*/
private void computeBoundaryTable() {
-<span class="fc" id="L703"> var position = 2;</span>
-<span class="fc" id="L704"> var candidate = 0;</span>
+<span class="fc" id="L732"> var position = 2;</span>
+<span class="fc" id="L733"> var candidate = 0;</span>
-<span class="fc" id="L706"> boundaryTable[0] = -1;</span>
-<span class="fc" id="L707"> boundaryTable[1] = 0;</span>
+<span class="fc" id="L735"> boundaryTable[0] = -1;</span>
+<span class="fc" id="L736"> boundaryTable[1] = 0;</span>
-<span class="fc bfc" id="L709" title="All 2 branches covered."> while
(position <= boundaryLength) {</span>
-<span class="pc bpc" id="L710" title="1 of 2 branches missed."> if
(boundary[position - 1] == boundary[candidate]) {</span>
-<span class="nc" id="L711"> boundaryTable[position] = candidate
+ 1;</span>
-<span class="nc" id="L712"> candidate++;</span>
-<span class="nc" id="L713"> position++;</span>
-<span class="pc bpc" id="L714" title="1 of 2 branches missed."> }
else if (candidate > 0) {</span>
-<span class="nc" id="L715"> candidate =
boundaryTable[candidate];</span>
+<span class="fc bfc" id="L738" title="All 2 branches covered."> while
(position <= boundaryLength) {</span>
+<span class="fc bfc" id="L739" title="All 2 branches covered."> if
(boundary[position - 1] == boundary[candidate]) {</span>
+<span class="fc" id="L740"> boundaryTable[position] = candidate
+ 1;</span>
+<span class="fc" id="L741"> candidate++;</span>
+<span class="fc" id="L742"> position++;</span>
+<span class="fc bfc" id="L743" title="All 2 branches covered."> }
else if (candidate > 0) {</span>
+<span class="fc" id="L744"> candidate =
boundaryTable[candidate];</span>
} else {
-<span class="fc" id="L717"> boundaryTable[position] = 0;</span>
-<span class="fc" id="L718"> position++;</span>
+<span class="fc" id="L746"> boundaryTable[position] = 0;</span>
+<span class="fc" id="L747"> position++;</span>
}
}
-<span class="fc" id="L721"> }</span>
+<span class="fc" id="L750"> }</span>
/**
* Reads {@code body-data} from the current {@code encapsulation} and
discards it.
@@ -731,7 +760,7 @@ public final class MultipartInput {
* @throws IOException if an i/o error occurs.
*/
public long discardBodyData() throws MalformedStreamException, IOException
{
-<span class="nc" id="L734"> return
readBodyData(NullOutputStream.INSTANCE);</span>
+<span class="fc" id="L763"> return
readBodyData(NullOutputStream.INSTANCE);</span>
}
/**
@@ -742,13 +771,13 @@ public final class MultipartInput {
* @return The position of byte found, counting from beginning of the
{@code buffer}, or {@code -1} if not found.
*/
protected int findByte(final byte value, final int pos) {
-<span class="nc bnc" id="L745" title="All 2 branches missed."> for (var
i = pos; i < tail; i++) {</span>
-<span class="nc bnc" id="L746" title="All 2 branches missed."> if
(buffer[i] == value) {</span>
-<span class="nc" id="L747"> return i;</span>
+<span class="nc bnc" id="L774" title="All 2 branches missed."> for (var
i = pos; i < tail; i++) {</span>
+<span class="nc bnc" id="L775" title="All 2 branches missed."> if
(buffer[i] == value) {</span>
+<span class="nc" id="L776"> return i;</span>
}
}
-<span class="nc" id="L751"> return -1;</span>
+<span class="nc" id="L780"> return -1;</span>
}
/**
@@ -757,19 +786,19 @@ public final class MultipartInput {
* @return The position of the boundary found, counting from the beginning
of the {@code buffer}, or {@code -1} if not found.
*/
protected int findSeparator() {
-<span class="nc" id="L760"> var bufferPos = this.head;</span>
-<span class="nc" id="L761"> var tablePos = 0;</span>
-<span class="nc bnc" id="L762" title="All 2 branches missed."> while
(bufferPos < this.tail) {</span>
-<span class="nc bnc" id="L763" title="All 4 branches missed.">
while (tablePos >= 0 && buffer[bufferPos] != boundary[tablePos])
{</span>
-<span class="nc" id="L764"> tablePos =
boundaryTable[tablePos];</span>
- }
-<span class="nc" id="L766"> bufferPos++;</span>
-<span class="nc" id="L767"> tablePos++;</span>
-<span class="nc bnc" id="L768" title="All 2 branches missed."> if
(tablePos == boundaryLength) {</span>
-<span class="nc" id="L769"> return bufferPos -
boundaryLength;</span>
+<span class="fc" id="L789"> var bufferPos = this.head;</span>
+<span class="fc" id="L790"> var tablePos = 0;</span>
+<span class="fc bfc" id="L791" title="All 2 branches covered."> while
(bufferPos < this.tail) {</span>
+<span class="pc bpc" id="L792" title="2 of 4 branches missed.">
while (tablePos >= 0 && buffer[bufferPos] != boundary[tablePos])
{</span>
+<span class="nc" id="L793"> tablePos =
boundaryTable[tablePos];</span>
+ }
+<span class="fc" id="L795"> bufferPos++;</span>
+<span class="fc" id="L796"> tablePos++;</span>
+<span class="fc bfc" id="L797" title="All 2 branches covered."> if
(tablePos == boundaryLength) {</span>
+<span class="fc" id="L798"> return bufferPos -
boundaryLength;</span>
}
}
-<span class="nc" id="L772"> return -1;</span>
+<span class="fc" id="L801"> return -1;</span>
}
/**
@@ -779,7 +808,16 @@ public final class MultipartInput {
* @return The encoding used to read part headers.
*/
public Charset getHeaderCharset() {
-<span class="nc" id="L782"> return headerCharset;</span>
+<span class="nc" id="L811"> return headerCharset;</span>
+ }
+
+ /** Returns the per part size limit for headers.
+ *
+ * @return The maximum size of the headers in bytes.
+ * @since 2.0.0-M4
+ */
+ public int getPartHeaderSizeMax() {
+<span class="fc" id="L820"> return partHeaderSizeMax;</span>
}
/**
@@ -788,7 +826,7 @@ public final class MultipartInput {
* @return A new instance of {@link ItemInputStream}.
*/
public ItemInputStream newInputStream() {
-<span class="nc" id="L791"> return new ItemInputStream();</span>
+<span class="fc" id="L829"> return new ItemInputStream();</span>
}
/**
@@ -803,8 +841,8 @@ public final class MultipartInput {
* @throws IOException if an i/o error occurs.
*/
public long readBodyData(final OutputStream output) throws
MalformedStreamException, IOException {
-<span class="nc" id="L806"> try (var inputStream = newInputStream())
{</span>
-<span class="nc" id="L807"> return IOUtils.copyLarge(inputStream,
output);</span>
+<span class="fc" id="L844"> try (var inputStream = newInputStream())
{</span>
+<span class="fc" id="L845"> return IOUtils.copyLarge(inputStream,
output);</span>
}
}
@@ -816,35 +854,35 @@ public final class MultipartInput {
* @throws MalformedStreamException if the stream ends unexpectedly or
fails to follow required syntax.
*/
public boolean readBoundary() throws FileUploadSizeException,
MalformedStreamException {
-<span class="nc" id="L819"> final var marker = new byte[2];</span>
+<span class="fc" id="L857"> final var marker = new byte[2];</span>
final boolean nextChunk;
-<span class="nc" id="L821"> head += boundaryLength;</span>
+<span class="fc" id="L859"> head += boundaryLength;</span>
try {
-<span class="nc" id="L823"> marker[0] = readByte();</span>
-<span class="nc bnc" id="L824" title="All 2 branches missed."> if
(marker[0] == LF) {</span>
+<span class="fc" id="L861"> marker[0] = readByte();</span>
+<span class="pc bpc" id="L862" title="1 of 2 branches missed."> if
(marker[0] == LF) {</span>
// Work around IE5 Mac bug with input type=image.
// Because the boundary delimiter, not including the trailing
// CRLF, must not appear within any file (RFC 2046, section
// 5.1.1), we know the missing CR is due to a buggy browser
// rather than a file containing something similar to a
// boundary.
-<span class="nc" id="L831"> return true;</span>
+<span class="nc" id="L869"> return true;</span>
}
-<span class="nc" id="L834"> marker[1] = readByte();</span>
-<span class="nc bnc" id="L835" title="All 2 branches missed."> if
(arrayEquals(marker, STREAM_TERMINATOR, 2)) {</span>
-<span class="nc" id="L836"> nextChunk = false;</span>
-<span class="nc bnc" id="L837" title="All 2 branches missed."> }
else if (arrayEquals(marker, FIELD_SEPARATOR, 2)) {</span>
-<span class="nc" id="L838"> nextChunk = true;</span>
+<span class="fc" id="L872"> marker[1] = readByte();</span>
+<span class="pc bpc" id="L873" title="1 of 2 branches missed."> if
(arrayEquals(marker, STREAM_TERMINATOR, 2)) {</span>
+<span class="nc" id="L874"> nextChunk = false;</span>
+<span class="pc bpc" id="L875" title="1 of 2 branches missed."> }
else if (arrayEquals(marker, FIELD_SEPARATOR, 2)) {</span>
+<span class="fc" id="L876"> nextChunk = true;</span>
} else {
-<span class="nc" id="L840"> throw new
MalformedStreamException("Unexpected characters follow a
boundary");</span>
+<span class="nc" id="L878"> throw new
MalformedStreamException("Unexpected characters follow a
boundary");</span>
}
-<span class="nc" id="L842"> } catch (final FileUploadSizeException e)
{</span>
-<span class="nc" id="L843"> throw e;</span>
-<span class="nc" id="L844"> } catch (final IOException e) {</span>
-<span class="nc" id="L845"> throw new
MalformedStreamException("Stream ended unexpectedly", e);</span>
-<span class="nc" id="L846"> }</span>
-<span class="nc" id="L847"> return nextChunk;</span>
+<span class="nc" id="L880"> } catch (final FileUploadSizeException e)
{</span>
+<span class="nc" id="L881"> throw e;</span>
+<span class="nc" id="L882"> } catch (final IOException e) {</span>
+<span class="nc" id="L883"> throw new
MalformedStreamException("Stream ended unexpectedly", e);</span>
+<span class="fc" id="L884"> }</span>
+<span class="fc" id="L885"> return nextChunk;</span>
}
/**
@@ -855,19 +893,19 @@ public final class MultipartInput {
*/
public byte readByte() throws IOException {
// Buffer depleted ?
-<span class="nc bnc" id="L858" title="All 2 branches missed."> if (head
== tail) {</span>
-<span class="nc" id="L859"> head = 0;</span>
+<span class="pc bpc" id="L896" title="1 of 2 branches missed."> if
(head == tail) {</span>
+<span class="nc" id="L897"> head = 0;</span>
// Refill.
-<span class="nc" id="L861"> tail = input.read(buffer, head,
bufSize);</span>
-<span class="nc bnc" id="L862" title="All 2 branches missed."> if
(tail == -1) {</span>
+<span class="nc" id="L899"> tail = input.read(buffer, head,
bufSize);</span>
+<span class="nc bnc" id="L900" title="All 2 branches missed."> if
(tail == -1) {</span>
// No more data available.
-<span class="nc" id="L864"> throw new IOException("No more
data is available");</span>
+<span class="nc" id="L902"> throw new IOException("No more
data is available");</span>
}
-<span class="nc bnc" id="L866" title="All 2 branches missed."> if
(notifier != null) {</span>
-<span class="nc" id="L867"> notifier.noteBytesRead(tail);</span>
+<span class="nc bnc" id="L904" title="All 2 branches missed."> if
(notifier != null) {</span>
+<span class="nc" id="L905"> notifier.noteBytesRead(tail);</span>
}
}
-<span class="nc" id="L870"> return buffer[head++];</span>
+<span class="fc" id="L908"> return buffer[head++];</span>
}
/**
@@ -875,43 +913,46 @@ public final class MultipartInput {
* <p>
* Headers are returned verbatim to the input stream, including the
trailing {@code CRLF} marker. Parsing is left to the application.
* </p>
+ * <p>
+ * <strong>TODO</strong> allow limiting maximum header size to
protect against abuse.
+ * </p>
*
* @return The {@code header-part} of the current encapsulation.
* @throws FileUploadSizeException if the bytes read from the stream
exceeded the size limits.
* @throws MalformedStreamException if the stream ends unexpectedly.
*/
public String readHeaders() throws FileUploadSizeException,
MalformedStreamException {
-<span class="nc" id="L884"> var i = 0;</span>
+<span class="fc" id="L925"> var i = 0;</span>
byte b;
// to support multi-byte characters
-<span class="nc" id="L887"> final var baos = new
ByteArrayOutputStream();</span>
-<span class="nc" id="L888"> var size = 0;</span>
-<span class="nc bnc" id="L889" title="All 2 branches missed."> while (i
< HEADER_SEPARATOR.length) {</span>
+<span class="fc" id="L928"> final var baos = new
ByteArrayOutputStream();</span>
+<span class="fc" id="L929"> var size = 0;</span>
+<span class="pc bpc" id="L930" title="1 of 2 branches missed."> while
(i < HEADER_SEPARATOR.length) {</span>
try {
-<span class="nc" id="L891"> b = readByte();</span>
-<span class="nc" id="L892"> } catch (final FileUploadSizeException
e) {</span>
+<span class="fc" id="L932"> b = readByte();</span>
+<span class="nc" id="L933"> } catch (final FileUploadSizeException
e) {</span>
// wraps a FileUploadSizeException, re-throw as it will be
unwrapped later
-<span class="nc" id="L894"> throw e;</span>
-<span class="nc" id="L895"> } catch (final IOException e) {</span>
-<span class="nc" id="L896"> throw new
MalformedStreamException("Stream ended unexpectedly", e);</span>
-<span class="nc" id="L897"> }</span>
-<span class="nc bnc" id="L898" title="All 2 branches missed."> if
(++size > HEADER_PART_SIZE_MAX) {</span>
-<span class="nc" id="L899"> throw new
MalformedStreamException(</span>
-<span class="nc" id="L900"> String.format("Header
section has more than %s bytes (maybe it is not properly terminated)",
HEADER_PART_SIZE_MAX));</span>
+<span class="nc" id="L935"> throw e;</span>
+<span class="nc" id="L936"> } catch (final IOException e) {</span>
+<span class="nc" id="L937"> throw new
MalformedStreamException("Stream ended unexpectedly", e);</span>
+<span class="fc" id="L938"> }</span>
+<span class="fc" id="L939"> final int phsm =
getPartHeaderSizeMax();</span>
+<span class="pc bpc" id="L940" title="1 of 4 branches missed."> if
(phsm != -1 && ++size > phsm) {</span>
+<span class="fc" id="L941"> throw new
FileUploadSizeException(</span>
+<span class="fc" id="L942"> String.format("Header
section has more than %s bytes (maybe it is not properly terminated)",
Integer.valueOf(phsm)), phsm, size);</span>
}
-<span class="nc bnc" id="L902" title="All 2 branches missed."> if
(b == HEADER_SEPARATOR[i]) {</span>
-<span class="nc" id="L903"> i++;</span>
+<span class="fc bfc" id="L944" title="All 2 branches covered."> if
(b == HEADER_SEPARATOR[i]) {</span>
+<span class="fc" id="L945"> i++;</span>
} else {
-<span class="nc" id="L905"> i = 0;</span>
+<span class="fc" id="L947"> i = 0;</span>
}
-<span class="nc" id="L907"> baos.write(b);</span>
- }
-
+<span class="fc" id="L949"> baos.write(b);</span>
+<span class="fc" id="L950"> }</span>
try {
-<span class="nc" id="L911"> return
baos.toString(Charsets.toCharset(headerCharset,
Charset.defaultCharset()).name());</span>
-<span class="nc" id="L912"> } catch (final UnsupportedEncodingException
e) {</span>
+<span class="nc" id="L952"> return
baos.toString(Charsets.toCharset(headerCharset,
Charset.defaultCharset()).name());</span>
+<span class="nc" id="L953"> } catch (final UnsupportedEncodingException
e) {</span>
// not possible
-<span class="nc" id="L914"> throw new
IllegalStateException(e);</span>
+<span class="nc" id="L955"> throw new
IllegalStateException(e);</span>
}
}
@@ -931,12 +972,12 @@ public final class MultipartInput {
* @throws FileUploadBoundaryException if the {@code boundary} has a
different length than the one being currently parsed.
*/
public void setBoundary(final byte[] boundary) throws
FileUploadBoundaryException {
-<span class="nc bnc" id="L934" title="All 2 branches missed."> if
(boundary.length != boundaryLength - BOUNDARY_PREFIX.length) {</span>
-<span class="nc" id="L935"> throw new
FileUploadBoundaryException("The length of a boundary token cannot be
changed");</span>
+<span class="nc bnc" id="L975" title="All 2 branches missed."> if
(boundary.length != boundaryLength - BOUNDARY_PREFIX.length) {</span>
+<span class="nc" id="L976"> throw new
FileUploadBoundaryException("The length of a boundary token cannot be
changed");</span>
}
-<span class="nc" id="L937"> System.arraycopy(boundary, 0,
this.boundary, BOUNDARY_PREFIX.length, boundary.length);</span>
-<span class="nc" id="L938"> computeBoundaryTable();</span>
-<span class="nc" id="L939"> }</span>
+<span class="nc" id="L978"> System.arraycopy(boundary, 0,
this.boundary, BOUNDARY_PREFIX.length, boundary.length);</span>
+<span class="nc" id="L979"> computeBoundaryTable();</span>
+<span class="nc" id="L980"> }</span>
/**
* Sets the character encoding to be used when reading the headers of
individual parts. When not specified, or {@code null}, the platform default
encoding
@@ -945,8 +986,8 @@ public final class MultipartInput {
* @param headerCharset The encoding used to read part headers.
*/
public void setHeaderCharset(final Charset headerCharset) {
-<span class="nc" id="L948"> this.headerCharset = headerCharset;</span>
-<span class="nc" id="L949"> }</span>
+<span class="nc" id="L989"> this.headerCharset = headerCharset;</span>
+<span class="nc" id="L990"> }</span>
/**
* Finds the beginning of the first {@code encapsulation}.
@@ -956,25 +997,25 @@ public final class MultipartInput {
*/
public boolean skipPreamble() throws IOException {
// First delimiter may be not preceded with a CRLF.
-<span class="nc" id="L959"> System.arraycopy(boundary, 2, boundary, 0,
boundary.length - 2);</span>
-<span class="nc" id="L960"> boundaryLength = boundary.length - 2;</span>
-<span class="nc" id="L961"> computeBoundaryTable();</span>
+<span class="fc" id="L1000"> System.arraycopy(boundary, 2, boundary, 0,
boundary.length - 2);</span>
+<span class="fc" id="L1001"> boundaryLength = boundary.length -
2;</span>
+<span class="fc" id="L1002"> computeBoundaryTable();</span>
try {
// Discard all data up to the delimiter.
-<span class="nc" id="L964"> discardBodyData();</span>
+<span class="fc" id="L1005"> discardBodyData();</span>
// Read boundary - if succeeded, the stream contains an
// encapsulation.
-<span class="nc" id="L968"> return readBoundary();</span>
-<span class="nc" id="L969"> } catch (final MalformedStreamException e)
{</span>
-<span class="nc" id="L970"> return false;</span>
+<span class="fc" id="L1009"> return readBoundary();</span>
+<span class="nc" id="L1010"> } catch (final MalformedStreamException e)
{</span>
+<span class="nc" id="L1011"> return false;</span>
} finally {
// Restore delimiter.
-<span class="nc" id="L973"> System.arraycopy(boundary, 0, boundary,
2, boundary.length - 2);</span>
-<span class="nc" id="L974"> boundaryLength = boundary.length;</span>
-<span class="nc" id="L975"> boundary[0] = CR;</span>
-<span class="nc" id="L976"> boundary[1] = LF;</span>
-<span class="nc" id="L977"> computeBoundaryTable();</span>
+<span class="fc" id="L1014"> System.arraycopy(boundary, 0,
boundary, 2, boundary.length - 2);</span>
+<span class="fc" id="L1015"> boundaryLength =
boundary.length;</span>
+<span class="fc" id="L1016"> boundary[0] = CR;</span>
+<span class="fc" id="L1017"> boundary[1] = LF;</span>
+<span class="fc" id="L1018"> computeBoundaryTable();</span>
}
}