|
40 | 40 | import java.io.OutputStream; |
41 | 41 | import java.nio.charset.StandardCharsets; |
42 | 42 | import java.util.Iterator; |
43 | | -import java.util.Map; |
44 | 43 | import java.util.Objects; |
45 | 44 | import java.util.concurrent.Flow; |
46 | 45 | import java.util.concurrent.atomic.AtomicBoolean; |
47 | 46 |
|
48 | | -import static org.elasticsearch.ElasticsearchException.REST_EXCEPTION_SKIP_CAUSE; |
49 | | -import static org.elasticsearch.ElasticsearchException.REST_EXCEPTION_SKIP_STACK_TRACE; |
50 | | -import static org.elasticsearch.rest.RestController.ERROR_TRACE_DEFAULT; |
51 | | - |
52 | 47 | /** |
53 | 48 | * A version of {@link org.elasticsearch.rest.action.RestChunkedToXContentListener} that reads from a {@link Flow.Publisher} and encodes |
54 | 49 | * the response in Server-Sent Events. |
@@ -154,48 +149,23 @@ public void onFailure(Exception e) { |
154 | 149 | } |
155 | 150 | } |
156 | 151 |
|
157 | | - // taken indirectly from "new Response(channel, e)" |
158 | | - // except we need to emit the error as SSE |
159 | 152 | private ChunkedToXContent errorChunk(Throwable t) { |
160 | 153 | var status = ExceptionsHelper.status(t); |
161 | | - return params -> Iterators.concat(ChunkedToXContentHelper.startObject(), ChunkedToXContentHelper.chunk((b, p) -> { |
162 | | - // Render the exception with a simple message |
163 | | - if (channel.detailedErrorsEnabled() == false) { |
164 | | - String message = "No ElasticsearchException found"; |
165 | | - var inner = t; |
166 | | - for (int counter = 0; counter < 10 && inner != null; counter++) { |
167 | | - if (inner instanceof ElasticsearchException) { |
168 | | - message = inner.getClass().getSimpleName() + "[" + inner.getMessage() + "]"; |
169 | | - break; |
170 | | - } |
171 | | - inner = inner.getCause(); |
172 | | - } |
173 | | - return b.field("error", message); |
174 | | - } |
175 | | - |
176 | | - var errorParams = p; |
177 | | - if (errorParams.paramAsBoolean("error_trace", ERROR_TRACE_DEFAULT) && status != RestStatus.UNAUTHORIZED) { |
178 | | - errorParams = new ToXContent.DelegatingMapParams( |
179 | | - Map.of(REST_EXCEPTION_SKIP_STACK_TRACE, "false", REST_EXCEPTION_SKIP_CAUSE, "true"), |
180 | | - params |
181 | | - ); |
182 | | - } |
183 | 154 |
|
184 | | - // Render the exception with all details |
185 | | - final ElasticsearchException[] rootCauses = ElasticsearchException.guessRootCauses(t); |
186 | | - b.startObject("error"); |
187 | | - { |
188 | | - b.startArray("root_cause"); |
189 | | - for (ElasticsearchException rootCause : rootCauses) { |
190 | | - b.startObject(); |
191 | | - rootCause.toXContent(b, errorParams); |
192 | | - b.endObject(); |
193 | | - } |
194 | | - b.endArray(); |
195 | | - } |
196 | | - ElasticsearchException.generateThrowableXContent(b, errorParams, t); |
197 | | - return b.endObject(); |
198 | | - }), ChunkedToXContentHelper.field("status", status.getStatus()), ChunkedToXContentHelper.endObject()); |
| 155 | + Exception e; |
| 156 | + if (t instanceof Exception) { |
| 157 | + e = (Exception) t; |
| 158 | + } else { |
| 159 | + // if not exception, then error, and we should not let it escape. rethrow on another thread, and inform the user we're stopping. |
| 160 | + ExceptionsHelper.maybeDieOnAnotherThread(t); |
| 161 | + e = new RuntimeException("Fatal error while streaming response", t); |
| 162 | + } |
| 163 | + return params -> Iterators.concat( |
| 164 | + ChunkedToXContentHelper.startObject(), |
| 165 | + Iterators.single((b, p) -> ElasticsearchException.generateFailureXContent(b, p, e, channel.detailedErrorsEnabled())), |
| 166 | + Iterators.single((b, p) -> b.field("status", status.getStatus())), |
| 167 | + ChunkedToXContentHelper.endObject() |
| 168 | + ); |
199 | 169 | } |
200 | 170 |
|
201 | 171 | private void requestNextChunk(ActionListener<ChunkedRestResponseBodyPart> listener) { |
|
0 commit comments