Improve size limiting string message (#122427)

SizeLimitingStringWriter is used to limit the output size of mustache
scripts. When the size is limited, the resulting exception lacks detail
needed to identify the problem. In particular, the actual size that
would result is not given. Additionally, the excerpt lacks any of the
new string being added. This commit tweaks the exception to include both
of these details.
This commit is contained in:
Ryan Ernst 2025-02-13 11:28:22 -08:00 committed by GitHub
parent f8aa047994
commit 482a49fce1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 48 additions and 14 deletions

View file

@ -0,0 +1,5 @@
pr: 122427
summary: Improve size limiting string message
area: Infra/Core
type: enhancement
issues: []

View file

@ -423,7 +423,7 @@ public class MustacheScriptEngineTests extends ESTestCase {
ex.getCause().getCause(), ex.getCause().getCause(),
allOf( allOf(
instanceOf(SizeLimitingStringWriter.SizeLimitExceededException.class), instanceOf(SizeLimitingStringWriter.SizeLimitExceededException.class),
transformedMatch(Throwable::getMessage, endsWith("has exceeded the size limit [1024]")) transformedMatch(Throwable::getMessage, endsWith("has size [1030] which exceeds the size limit [1024]"))
) )
); );
} }

View file

@ -30,18 +30,29 @@ public class SizeLimitingStringWriter extends StringWriter {
this.sizeLimit = sizeLimit; this.sizeLimit = sizeLimit;
} }
private void checkSizeLimit(int additionalChars) { private int limitSize(int additionalChars) {
int bufLen = getBuffer().length(); int neededSize = getBuffer().length() + additionalChars;
if (bufLen + additionalChars > sizeLimit) { if (neededSize > sizeLimit) {
throw new SizeLimitExceededException( return additionalChars - (neededSize - sizeLimit);
Strings.format("String [%s...] has exceeded the size limit [%s]", getBuffer().substring(0, Math.min(bufLen, 20)), sizeLimit)
);
} }
return additionalChars;
}
private void throwSizeLimitExceeded(int limitedChars, int requestedChars) {
assert limitedChars < requestedChars;
int bufLen = getBuffer().length();
int foundSize = bufLen - limitedChars + requestedChars; // reconstitute original
String selection = getBuffer().substring(0, Math.min(bufLen, 20));
throw new SizeLimitExceededException(
Strings.format("String [%s...] has size [%d] which exceeds the size limit [%d]", selection, foundSize, sizeLimit)
);
} }
@Override @Override
public void write(int c) { public void write(int c) {
checkSizeLimit(1); if (limitSize(1) != 1) {
throwSizeLimitExceeded(0, 1);
}
super.write(c); super.write(c);
} }
@ -49,20 +60,29 @@ public class SizeLimitingStringWriter extends StringWriter {
@Override @Override
public void write(char[] cbuf, int off, int len) { public void write(char[] cbuf, int off, int len) {
checkSizeLimit(len); int limitedLen = limitSize(len);
super.write(cbuf, off, len); if (limitedLen > 0) {
super.write(cbuf, off, limitedLen);
}
if (limitedLen != len) {
throwSizeLimitExceeded(limitedLen, len);
}
} }
@Override @Override
public void write(String str) { public void write(String str) {
checkSizeLimit(str.length()); this.write(str, 0, str.length());
super.write(str);
} }
@Override @Override
public void write(String str, int off, int len) { public void write(String str, int off, int len) {
checkSizeLimit(len); int limitedLen = limitSize(len);
super.write(str, off, len); if (limitedLen > 0) {
super.write(str, off, limitedLen);
}
if (limitedLen != len) {
throwSizeLimitExceeded(limitedLen, len);
}
} }
// append(...) delegates to write(...) methods // append(...) delegates to write(...) methods

View file

@ -11,6 +11,8 @@ package org.elasticsearch.common.text;
import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.ESTestCase;
import static org.hamcrest.Matchers.equalTo;
public class SizeLimitingStringWriterTests extends ESTestCase { public class SizeLimitingStringWriterTests extends ESTestCase {
public void testSizeIsLimited() { public void testSizeIsLimited() {
SizeLimitingStringWriter writer = new SizeLimitingStringWriter(10); SizeLimitingStringWriter writer = new SizeLimitingStringWriter(10);
@ -26,4 +28,11 @@ public class SizeLimitingStringWriterTests extends ESTestCase {
expectThrows(SizeLimitingStringWriter.SizeLimitExceededException.class, () -> writer.append("a")); expectThrows(SizeLimitingStringWriter.SizeLimitExceededException.class, () -> writer.append("a"));
expectThrows(SizeLimitingStringWriter.SizeLimitExceededException.class, () -> writer.append("a", 0, 1)); expectThrows(SizeLimitingStringWriter.SizeLimitExceededException.class, () -> writer.append("a", 0, 1));
} }
public void testLimitMessage() {
SizeLimitingStringWriter writer = new SizeLimitingStringWriter(3);
var e = expectThrows(SizeLimitingStringWriter.SizeLimitExceededException.class, () -> writer.write("abcdefgh"));
assertThat(e.getMessage(), equalTo("String [abc...] has size [8] which exceeds the size limit [3]"));
}
} }