Skip to content

Commit e840204

Browse files
authored
Merge branch 'main' into data-stream-mappings-effective-mappings
2 parents a96ad86 + be63963 commit e840204

File tree

4 files changed

+80
-12
lines changed

4 files changed

+80
-12
lines changed

server/src/main/java/org/elasticsearch/ElasticsearchException.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ public class ElasticsearchException extends RuntimeException implements ToXConte
118118
private static final String ROOT_CAUSE = "root_cause";
119119

120120
static final String TIMED_OUT_HEADER = "X-Timed-Out";
121+
static final String EXCEPTION_TYPE_HEADER = "X-Elasticsearch-Exception";
121122

122123
private static final Map<Integer, CheckedFunction<StreamInput, ? extends ElasticsearchException, IOException>> ID_TO_SUPPLIER;
123124
private static final Map<Class<? extends ElasticsearchException>, ElasticsearchExceptionHandle> CLASS_TO_ELASTICSEARCH_EXCEPTION_HANDLE;
@@ -131,7 +132,6 @@ public class ElasticsearchException extends RuntimeException implements ToXConte
131132
@SuppressWarnings("this-escape")
132133
public ElasticsearchException(Throwable cause) {
133134
super(cause);
134-
maybePutTimeoutHeader();
135135
}
136136

137137
/**
@@ -146,7 +146,6 @@ public ElasticsearchException(Throwable cause) {
146146
@SuppressWarnings("this-escape")
147147
public ElasticsearchException(String msg, Object... args) {
148148
super(LoggerMessageFormat.format(msg, args));
149-
maybePutTimeoutHeader();
150149
}
151150

152151
/**
@@ -163,7 +162,6 @@ public ElasticsearchException(String msg, Object... args) {
163162
@SuppressWarnings("this-escape")
164163
public ElasticsearchException(String msg, Throwable cause, Object... args) {
165164
super(LoggerMessageFormat.format(msg, args), cause);
166-
maybePutTimeoutHeader();
167165
}
168166

169167
@SuppressWarnings("this-escape")
@@ -174,11 +172,19 @@ public ElasticsearchException(StreamInput in) throws IOException {
174172
metadata.putAll(in.readMapOfLists(StreamInput::readString));
175173
}
176174

177-
private void maybePutTimeoutHeader() {
175+
private void maybeAddErrorHeaders() {
178176
if (isTimeout()) {
179177
// see https://www.rfc-editor.org/rfc/rfc8941.html#section-4.1.9 for booleans in structured headers
180178
bodyHeaders.put(TIMED_OUT_HEADER, List.of("?1"));
181179
}
180+
if (httpHeaders.containsKey(EXCEPTION_TYPE_HEADER) == false) {
181+
// TODO: cache unwrapping the cause? we do this in several places...
182+
Throwable cause = unwrapCause();
183+
RestStatus status = ExceptionsHelper.status(cause);
184+
if (status.getStatus() >= 500) {
185+
httpHeaders.put(EXCEPTION_TYPE_HEADER, List.of(cause.getClass().getSimpleName()));
186+
}
187+
}
182188
}
183189

184190
/**
@@ -244,6 +250,7 @@ public void addBodyHeader(String key, String... value) {
244250
* Returns a set of all body header keys on this exception
245251
*/
246252
public Set<String> getBodyHeaderKeys() {
253+
maybeAddErrorHeaders();
247254
return bodyHeaders.keySet();
248255
}
249256

@@ -252,10 +259,12 @@ public Set<String> getBodyHeaderKeys() {
252259
* given key exists.
253260
*/
254261
public List<String> getBodyHeader(String key) {
262+
maybeAddErrorHeaders();
255263
return bodyHeaders.get(key);
256264
}
257265

258266
protected Map<String, List<String>> getBodyHeaders() {
267+
maybeAddErrorHeaders();
259268
return bodyHeaders;
260269
}
261270

@@ -279,6 +288,7 @@ public void addHttpHeader(String key, String... value) {
279288
* Returns a set of all body header keys on this exception
280289
*/
281290
public Set<String> getHttpHeaderKeys() {
291+
maybeAddErrorHeaders();
282292
return httpHeaders.keySet();
283293
}
284294

@@ -287,10 +297,12 @@ public Set<String> getHttpHeaderKeys() {
287297
* given key exists.
288298
*/
289299
public List<String> getHttpHeader(String key) {
300+
maybeAddErrorHeaders();
290301
return httpHeaders.get(key);
291302
}
292303

293304
protected Map<String, List<String>> getHttpHeaders() {
305+
maybeAddErrorHeaders();
294306
return httpHeaders;
295307
}
296308

server/src/test/java/org/elasticsearch/ElasticsearchExceptionTests.java

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1553,19 +1553,24 @@ private void testExceptionLoop(Exception rootException) throws IOException {
15531553
assertArrayEquals(ser.getStackTrace(), rootException.getStackTrace());
15541554
}
15551555

1556-
static class ExceptionSubclass extends ElasticsearchException {
1556+
static class TimeoutSubclass extends ElasticsearchException {
1557+
TimeoutSubclass(String message) {
1558+
super(message);
1559+
}
1560+
15571561
@Override
15581562
public boolean isTimeout() {
15591563
return true;
15601564
}
15611565

1562-
ExceptionSubclass(String message) {
1563-
super(message);
1566+
@Override
1567+
public RestStatus status() {
1568+
return RestStatus.BAD_REQUEST;
15641569
}
15651570
}
15661571

1567-
public void testTimeout() throws IOException {
1568-
var e = new ExceptionSubclass("some timeout");
1572+
public void testTimeoutHeader() throws IOException {
1573+
var e = new TimeoutSubclass("some timeout");
15691574
assertThat(e.getBodyHeaderKeys(), hasItem(ElasticsearchException.TIMED_OUT_HEADER));
15701575

15711576
XContentBuilder builder = XContentFactory.jsonBuilder();
@@ -1574,7 +1579,7 @@ public void testTimeout() throws IOException {
15741579
builder.endObject();
15751580
String expected = """
15761581
{
1577-
"type": "exception_subclass",
1582+
"type": "timeout_subclass",
15781583
"reason": "some timeout",
15791584
"timed_out": true,
15801585
"header": {
@@ -1584,6 +1589,44 @@ public void testTimeout() throws IOException {
15841589
assertEquals(XContentHelper.stripWhitespace(expected), Strings.toString(builder));
15851590
}
15861591

1592+
static class Exception5xx extends ElasticsearchException {
1593+
Exception5xx(String message) {
1594+
super(message);
1595+
}
1596+
1597+
@Override
1598+
public RestStatus status() {
1599+
return RestStatus.INTERNAL_SERVER_ERROR;
1600+
}
1601+
}
1602+
1603+
static class Exception4xx extends ElasticsearchException {
1604+
Exception4xx(String message) {
1605+
super(message);
1606+
}
1607+
1608+
@Override
1609+
public RestStatus status() {
1610+
return RestStatus.BAD_REQUEST;
1611+
}
1612+
}
1613+
1614+
public void testExceptionTypeHeader() throws IOException {
1615+
var e = new Exception5xx("some exception");
1616+
assertThat(e.getHttpHeaderKeys(), hasItem(ElasticsearchException.EXCEPTION_TYPE_HEADER));
1617+
1618+
XContentBuilder builder = XContentFactory.jsonBuilder();
1619+
builder.startObject();
1620+
e.toXContent(builder, ToXContent.EMPTY_PARAMS);
1621+
builder.endObject();
1622+
String expected = """
1623+
{
1624+
"type": "exception5xx",
1625+
"reason": "some exception"
1626+
}""";
1627+
assertEquals(XContentHelper.stripWhitespace(expected), Strings.toString(builder));
1628+
}
1629+
15871630
public void testHttpHeaders() throws IOException {
15881631
var e = new ElasticsearchException("some exception");
15891632
e.addHttpHeader("My-Header", "value");
@@ -1592,13 +1635,19 @@ public void testHttpHeaders() throws IOException {
15921635
assertThat(e.getHttpHeaders(), hasEntry("My-Header", List.of("value")));
15931636

15941637
// ensure http headers are not written to response body
1638+
}
1639+
1640+
public void testNoExceptionTypeHeaderOn4xx() throws IOException {
1641+
var e = new Exception4xx("some exception");
1642+
assertThat(e.getHttpHeaderKeys(), not(hasItem(ElasticsearchException.EXCEPTION_TYPE_HEADER)));
1643+
15951644
XContentBuilder builder = XContentFactory.jsonBuilder();
15961645
builder.startObject();
15971646
e.toXContent(builder, ToXContent.EMPTY_PARAMS);
15981647
builder.endObject();
15991648
String expected = """
16001649
{
1601-
"type": "exception",
1650+
"type": "exception4xx",
16021651
"reason": "some exception"
16031652
}""";
16041653
assertEquals(XContentHelper.stripWhitespace(expected), Strings.toString(builder));

server/src/test/java/org/elasticsearch/ExceptionSerializationTests.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,9 @@ public void testExceptionRegistration() throws IOException, URISyntaxException {
148148
CancellableThreadsTests.CustomException.class,
149149
RestResponseTests.WithHeadersException.class,
150150
AbstractClientHeadersTestCase.InternalException.class,
151-
ElasticsearchExceptionTests.ExceptionSubclass.class
151+
ElasticsearchExceptionTests.TimeoutSubclass.class,
152+
ElasticsearchExceptionTests.Exception4xx.class,
153+
ElasticsearchExceptionTests.Exception5xx.class
152154
);
153155
FileVisitor<Path> visitor = new FileVisitor<Path>() {
154156
private Path pkgPrefix = PathUtils.get(path).getParent();

server/src/test/java/org/elasticsearch/rest/RestResponseTests.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,11 @@ public static class WithHeadersException extends ElasticsearchException {
539539
this.addHttpHeader("My-Header", "v1");
540540
this.addMetadata("es.test", "value1", "value2");
541541
}
542+
543+
@Override
544+
public RestStatus status() {
545+
return RestStatus.BAD_REQUEST;
546+
}
542547
}
543548

544549
private static class SimpleExceptionRestChannel extends AbstractRestChannel {

0 commit comments

Comments
 (0)