Skip to content

Commit 91e716c

Browse files
l-trottaraynigon
andauthored
Improve Misleading Error Message in ContentTooLongException (#1239) (#1242)
Co-authored-by: Simon Schneider <10846939+raynigon@users.noreply.github.com>
1 parent 31831c7 commit 91e716c

2 files changed

Lines changed: 220 additions & 2 deletions

File tree

rest5-client/src/main/java/co/elastic/clients/transport/rest5_client/low_level/BufferedByteConsumer.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,10 @@ protected int capacityIncrement() {
6565

6666
@Override
6767
protected void data(final ByteBuffer src, final boolean endOfStream) throws ContentTooLongException {
68-
if (buffer.length() + src.limit() > limit) {
68+
long contentLength = buffer.length() + src.limit();
69+
if (contentLength > limit) {
6970
throw new ContentTooLongException(
70-
"entity content is too long [" + src.limit() + "] for the configured buffer limit [" + limit + "]"
71+
"entity content is too long [" + contentLength + "] for the configured buffer limit [" + limit + "]"
7172
);
7273
}
7374
buffer.append(src);
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package co.elastic.clients.transport.rest5_client.low_level;
21+
22+
import org.apache.hc.core5.concurrent.FutureCallback;
23+
import org.apache.hc.core5.http.ContentTooLongException;
24+
import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
25+
import org.junit.jupiter.api.Test;
26+
27+
import java.nio.ByteBuffer;
28+
import java.nio.charset.StandardCharsets;
29+
30+
import static org.junit.jupiter.api.Assertions.assertThrows;
31+
import static org.junit.jupiter.api.Assertions.assertEquals;
32+
import static org.junit.jupiter.api.Assertions.assertTrue;
33+
import static org.junit.jupiter.api.Assertions.assertFalse;
34+
import static org.junit.jupiter.api.Assertions.fail;
35+
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
36+
import static org.junit.jupiter.api.Assertions.assertNotNull;
37+
38+
class BufferedByteConsumerTest extends RestClientTestCase {
39+
40+
@Test
41+
void testConstructor() {
42+
assertThrows(IllegalArgumentException.class, () -> new BufferedByteConsumer(0));
43+
assertThrows(IllegalArgumentException.class, () -> new BufferedByteConsumer(-1));
44+
}
45+
46+
@Test
47+
void testCapacityIncrement() {
48+
int bufferLimit = 1024;
49+
BufferedByteConsumer consumer = new BufferedByteConsumer(bufferLimit);
50+
assertEquals(bufferLimit, consumer.capacityIncrement());
51+
}
52+
53+
@Test
54+
void testContentTooLongException() {
55+
int bufferLimit = 10;
56+
BufferedByteConsumer consumer = new BufferedByteConsumer(bufferLimit);
57+
58+
// Start the stream
59+
consumer.streamStart(null, null);
60+
61+
// Try to add data that exceeds the limit
62+
byte[] data = "This is a long string that exceeds the buffer limit".getBytes(StandardCharsets.UTF_8);
63+
ByteBuffer buffer = ByteBuffer.wrap(data);
64+
65+
ContentTooLongException exception = assertThrows(ContentTooLongException.class, () -> {
66+
consumer.data(buffer, false);
67+
});
68+
assertEquals("entity content is too long [51] for the configured buffer limit [10]", exception.getMessage());
69+
}
70+
71+
@Test
72+
void testContentTooLongExceptionWithTwoCalls() {
73+
int bufferLimit = 100;
74+
BufferedByteConsumer consumer = new BufferedByteConsumer(bufferLimit);
75+
76+
// Start the stream
77+
consumer.streamStart(null, null);
78+
79+
// Try to add data that exceeds the limit
80+
byte[] data = "This is a long string that exceeds the buffer limit".getBytes(StandardCharsets.UTF_8);
81+
ByteBuffer buffer = ByteBuffer.wrap(data);
82+
83+
ContentTooLongException exception = assertThrows(ContentTooLongException.class, () -> {
84+
consumer.data(buffer, false);
85+
consumer.data(buffer, false); // This should trigger the exception on the second call
86+
});
87+
assertEquals("entity content is too long [102] for the configured buffer limit [100]", exception.getMessage());
88+
}
89+
90+
@Test
91+
void testContentTooLongExceptionWithCallback() throws Exception {
92+
int bufferLimit = 10;
93+
BufferedByteConsumer consumer = new BufferedByteConsumer(bufferLimit);
94+
95+
TestFutureCallback callback = new TestFutureCallback();
96+
consumer.streamStart(null, callback);
97+
98+
// Try to add data that exceeds the limit
99+
byte[] data = "This is a long string that exceeds the buffer limit".getBytes(StandardCharsets.UTF_8);
100+
ByteBuffer buffer = ByteBuffer.wrap(data);
101+
102+
try {
103+
consumer.data(buffer, false);
104+
fail("Expected ContentTooLongException");
105+
} catch (ContentTooLongException e) {
106+
// Verify the exception message contains the expected values
107+
assertTrue(e.getMessage().contains("entity content is too long"));
108+
assertTrue(e.getMessage().contains(String.valueOf(bufferLimit)));
109+
}
110+
}
111+
112+
@Test
113+
void testSuccessfulDataConsumption() throws Exception {
114+
int bufferLimit = 100;
115+
BufferedByteConsumer consumer = new BufferedByteConsumer(bufferLimit);
116+
117+
TestFutureCallback callback = new TestFutureCallback();
118+
consumer.streamStart(null, callback);
119+
120+
// Add data within the limit
121+
byte[] data = "Hello, World!".getBytes(StandardCharsets.UTF_8);
122+
ByteBuffer buffer = ByteBuffer.wrap(data);
123+
124+
consumer.data(buffer, true);
125+
consumer.completed();
126+
127+
// Verify the callback was called successfully
128+
assertTrue(callback.isCompleted());
129+
assertFalse(callback.isFailed());
130+
assertNotNull(consumer.getContent());
131+
}
132+
133+
@Test
134+
void testContentAtExactLimit() throws Exception {
135+
int bufferLimit = 13;
136+
BufferedByteConsumer consumer = new BufferedByteConsumer(bufferLimit);
137+
138+
consumer.streamStart(null, null);
139+
140+
// Add data exactly at the limit
141+
byte[] data = "Hello, World!".getBytes(StandardCharsets.UTF_8);
142+
ByteBuffer buffer = ByteBuffer.wrap(data);
143+
144+
// Should not throw exception
145+
assertDoesNotThrow(() -> consumer.data(buffer, true));
146+
}
147+
148+
@Test
149+
void testContentJustOverLimit() throws Exception {
150+
int bufferLimit = 12;
151+
BufferedByteConsumer consumer = new BufferedByteConsumer(bufferLimit);
152+
153+
consumer.streamStart(null, null);
154+
155+
// Add data just over the limit
156+
byte[] data = "Hello, World!".getBytes(StandardCharsets.UTF_8);
157+
ByteBuffer buffer = ByteBuffer.wrap(data);
158+
159+
assertThrows(ContentTooLongException.class, () -> consumer.data(buffer, true));
160+
}
161+
162+
@Test
163+
void testFailedCallback() {
164+
int bufferLimit = 100;
165+
BufferedByteConsumer consumer = new BufferedByteConsumer(bufferLimit);
166+
167+
TestFutureCallback callback = new TestFutureCallback();
168+
consumer.streamStart(null, callback);
169+
170+
Exception testException = new RuntimeException("Test exception");
171+
consumer.failed(testException);
172+
173+
assertTrue(callback.isFailed());
174+
assertEquals(testException, callback.getException());
175+
}
176+
177+
private static class TestFutureCallback implements FutureCallback<ByteArrayEntity> {
178+
private boolean completed = false;
179+
private boolean failed = false;
180+
private ByteArrayEntity result;
181+
private Exception exception;
182+
183+
@Override
184+
public void completed(ByteArrayEntity result) {
185+
this.completed = true;
186+
this.result = result;
187+
}
188+
189+
@Override
190+
public void failed(Exception ex) {
191+
this.failed = true;
192+
this.exception = ex;
193+
}
194+
195+
@Override
196+
public void cancelled() {
197+
// Not used in tests
198+
}
199+
200+
public boolean isCompleted() {
201+
return completed;
202+
}
203+
204+
public boolean isFailed() {
205+
return failed;
206+
}
207+
208+
public ByteArrayEntity getResult() {
209+
return result;
210+
}
211+
212+
public Exception getException() {
213+
return exception;
214+
}
215+
}
216+
217+
}

0 commit comments

Comments
 (0)