|
28 | 28 | import java.util.Objects;
|
29 | 29 | import java.util.concurrent.CompletableFuture;
|
30 | 30 | import java.util.concurrent.TimeoutException;
|
| 31 | +import java.util.concurrent.atomic.AtomicReference; |
31 | 32 | import java.util.function.Consumer;
|
32 | 33 | import java.util.function.Function;
|
33 | 34 | import java.util.function.Predicate;
|
|
45 | 46 | import org.eclipse.jetty.server.internal.HttpChannelState;
|
46 | 47 | import org.eclipse.jetty.util.Attributes;
|
47 | 48 | import org.eclipse.jetty.util.Callback;
|
| 49 | +import org.eclipse.jetty.util.ExceptionUtil; |
48 | 50 | import org.eclipse.jetty.util.Fields;
|
49 | 51 | import org.eclipse.jetty.util.HostPort;
|
50 | 52 | import org.eclipse.jetty.util.NanoTime;
|
|
54 | 56 | import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
55 | 57 | import org.eclipse.jetty.util.annotation.ManagedObject;
|
56 | 58 | import org.eclipse.jetty.util.thread.Invocable;
|
| 59 | +import org.slf4j.Logger; |
| 60 | +import org.slf4j.LoggerFactory; |
57 | 61 |
|
58 | 62 | /**
|
59 | 63 | * <p>The representation of an HTTP request, for any protocol version (HTTP/1.1, HTTP/2, HTTP/3).</p>
|
|
123 | 127 | */
|
124 | 128 | public interface Request extends Attributes, Content.Source
|
125 | 129 | {
|
| 130 | + Logger LOG = LoggerFactory.getLogger(Request.class); |
| 131 | + |
126 | 132 | String CACHE_ATTRIBUTE = Request.class.getCanonicalName() + ".CookieCache";
|
127 | 133 | String COOKIE_ATTRIBUTE = Request.class.getCanonicalName() + ".Cookies";
|
128 | 134 | List<Locale> DEFAULT_LOCALES = List.of(Locale.getDefault());
|
@@ -316,8 +322,73 @@ default void push(MetaData.Request resource)
|
316 | 322 |
|
317 | 323 | TunnelSupport getTunnelSupport();
|
318 | 324 |
|
| 325 | + /** |
| 326 | + * Add a {@link HttpStream.Wrapper} to the current {@link HttpStream}. |
| 327 | + * @param wrapper A function that wraps the passed stream. |
| 328 | + * @see #addCompletionListener(Request, Consumer) |
| 329 | + */ |
319 | 330 | void addHttpStreamWrapper(Function<HttpStream, HttpStream> wrapper);
|
320 | 331 |
|
| 332 | + /** |
| 333 | + * Adds a completion listener that is an optimized equivalent to overriding the |
| 334 | + * {@link HttpStream#succeeded()} and {@link HttpStream#failed(Throwable)} methods |
| 335 | + * of a {@link HttpStream.Wrapper} created by a call to {@link #addHttpStreamWrapper(Function)}. |
| 336 | + * In the case of a failure, the {@link Throwable} cause is passed to the listener, but unlike |
| 337 | + * {@link #addFailureListener(Consumer)} listeners, which are called when the failure occurs, completion |
| 338 | + * listeners are called only once the {@link HttpStream} is completed at the very end of processing. |
| 339 | + * |
| 340 | + * @param listener A {@link Consumer} of {@link Throwable} to call when the request handling is complete. The |
| 341 | + * listener is passed a null {@link Throwable} on success. |
| 342 | + * @see #addHttpStreamWrapper(Function) |
| 343 | + */ |
| 344 | + static void addCompletionListener(Request request, Consumer<Throwable> listener) |
| 345 | + { |
| 346 | + // Look for a ChannelRequest to use its optimized addCompletionLister |
| 347 | + HttpChannelState.ChannelRequest channelRequest = as(request, HttpChannelState.ChannelRequest.class); |
| 348 | + if (channelRequest != null) |
| 349 | + { |
| 350 | + channelRequest.addCompletionListener(listener); |
| 351 | + } |
| 352 | + else |
| 353 | + { |
| 354 | + // No ChannelRequest, so directly implement listener with a stream wrapper. |
| 355 | + AtomicReference<Consumer<Throwable>> onCompletion = new AtomicReference<>(listener); |
| 356 | + request.addHttpStreamWrapper(s -> new HttpStream.Wrapper(s) |
| 357 | + { |
| 358 | + @Override |
| 359 | + public void succeeded() |
| 360 | + { |
| 361 | + onCompletion(null); |
| 362 | + super.succeeded(); |
| 363 | + } |
| 364 | + |
| 365 | + @Override |
| 366 | + public void failed(Throwable x) |
| 367 | + { |
| 368 | + onCompletion(x); |
| 369 | + super.failed(x); |
| 370 | + } |
| 371 | + |
| 372 | + private void onCompletion(Throwable x) |
| 373 | + { |
| 374 | + Consumer<Throwable> l = onCompletion.getAndSet(null); |
| 375 | + if (l != null) |
| 376 | + { |
| 377 | + try |
| 378 | + { |
| 379 | + l.accept(x); |
| 380 | + } |
| 381 | + catch (Throwable t) |
| 382 | + { |
| 383 | + ExceptionUtil.addSuppressedIfNotAssociated(x, t); |
| 384 | + LOG.warn("{} threw", l, t); |
| 385 | + } |
| 386 | + } |
| 387 | + } |
| 388 | + }); |
| 389 | + } |
| 390 | + } |
| 391 | + |
321 | 392 | /**
|
322 | 393 | * <p>Get a {@link Session} associated with the request.
|
323 | 394 | * Sessions may not be supported by a given configuration, in which case
|
|
0 commit comments