
com.linecorp.armeria.common.RequestContext Maven / Gradle / Ivy
Show all versions of armeria-shaded Show documentation
/*
* Copyright 2016 LINE Corporation
*
* LINE Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package com.linecorp.armeria.common;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.util.Iterator;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import javax.annotation.Nullable;
import javax.net.ssl.SSLSession;
import org.slf4j.LoggerFactory;
import com.linecorp.armeria.client.ClientRequestContext;
import com.linecorp.armeria.common.logging.RequestLog;
import com.linecorp.armeria.common.logging.RequestLogBuilder;
import com.linecorp.armeria.common.util.Exceptions;
import com.linecorp.armeria.common.util.SafeCloseable;
import com.linecorp.armeria.server.ServiceRequestContext;
import io.micrometer.core.instrument.MeterRegistry;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.EventLoop;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.util.Attribute;
import io.netty.util.AttributeMap;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.ReferenceCounted;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
import io.netty.util.concurrent.GenericFutureListener;
import io.netty.util.concurrent.Promise;
/**
* Provides information about a {@link Request}, its {@link Response} and related utilities.
* A server-side {@link Request} has a {@link ServiceRequestContext} and
* a client-side {@link Request} has a {@link ClientRequestContext}.
*/
public interface RequestContext extends AttributeMap {
/**
* Creates a new derived {@link RequestContext} which only the {@link RequestLog}
* is different from the deriving context. Note that the references of {@link Attribute}s
* in the {@link #attrs()} are copied as well.
*/
RequestContext newDerivedContext();
/**
* Creates a new derived {@link RequestContext} with the specified {@link Request} which the
* {@link RequestLog} is different from the deriving context.
* Note that the references of {@link Attribute}s in the {@link #attrs()} are copied as well.
*/
RequestContext newDerivedContext(Request request);
/**
* Returns the context of the {@link Request} that is being handled in the current thread.
*
* @throws IllegalStateException if the context is unavailable in the current thread
*/
static T current() {
final T ctx = RequestContextThreadLocal.get();
if (ctx == null) {
throw new IllegalStateException(RequestContext.class.getSimpleName() + " unavailable");
}
return ctx;
}
/**
* Maps the context of the {@link Request} that is being handled in the current thread.
*
* @param mapper the {@link Function} that maps the {@link RequestContext}
* @param defaultValueSupplier the {@link Supplier} that provides the value when the context is unavailable
* in the current thread. If {@code null}, the {@code null} will be returned
* when the context is unavailable in the current thread.
*/
@Nullable
static T mapCurrent(
Function super RequestContext, T> mapper, @Nullable Supplier defaultValueSupplier) {
final RequestContext ctx = RequestContextThreadLocal.get();
if (ctx != null) {
return mapper.apply(ctx);
}
if (defaultValueSupplier != null) {
return defaultValueSupplier.get();
}
return null;
}
/**
* Returns the {@link SessionProtocol} of the current {@link Request}.
*/
SessionProtocol sessionProtocol();
/**
* Returns the remote address of this request, or {@code null} if the connection is not established yet.
*/
@Nullable
A remoteAddress();
/**
* Returns the local address of this request, or {@code null} if the connection is not established yet.
*/
@Nullable
A localAddress();
/**
* The {@link SSLSession} for this request if the connection is made over TLS, or {@code null} if
* the connection is not established yet or the connection is not a TLS connection.
*/
@Nullable
SSLSession sslSession();
/**
* Returns the HTTP method of the current {@link Request}.
*/
HttpMethod method();
/**
* Returns the absolute path part of the current {@link Request} URI, excluding the query part,
* as defined in RFC3986.
*/
String path();
/**
* Returns the absolute path part of the current {@link Request} URI, excluding the query part,
* decoded in UTF-8.
*/
String decodedPath();
/**
* Returns the query part of the current {@link Request} URI, without the leading {@code '?'},
* as defined in RFC3986.
*/
@Nullable
String query();
/**
* Returns the {@link Request} associated with this context.
*/
T request();
/**
* Returns the {@link RequestLog} that contains the information about the current {@link Request}.
*/
RequestLog log();
/**
* Returns the {@link RequestLogBuilder} that collects the information about the current {@link Request}.
*/
RequestLogBuilder logBuilder();
/**
* Returns the {@link MeterRegistry} that collects various stats.
*/
MeterRegistry meterRegistry();
/**
* Returns all {@link Attribute}s set in this context.
*/
Iterator> attrs();
/**
* Returns the {@link Executor} that is handling the current {@link Request}.
*/
default Executor executor() {
// The implementation is the same as eventLoop but we expose as an Executor as well given
// how much easier it is to write tests for an Executor (i.e.,
// when(ctx.executor()).thenReturn(MoreExecutors.directExecutor()));
return eventLoop();
}
/**
* Returns the {@link EventLoop} that is handling the current {@link Request}.
*/
EventLoop eventLoop();
/**
* Returns the {@link ByteBufAllocator} for this {@link RequestContext}. Any buffers created by this
* {@link ByteBufAllocator} must be
* reference-counted. If you don't know
* what this means, you should probably use {@code byte[]} or {@link ByteBuffer} directly instead
* of calling this method.
*/
default ByteBufAllocator alloc() {
throw new UnsupportedOperationException("No ByteBufAllocator available for this RequestContext.");
}
/**
* Returns an {@link Executor} that will make sure this {@link RequestContext} is set as the current
* context before executing any callback. This should almost always be used for executing asynchronous
* callbacks in service code to make sure features that require the {@link RequestContext} work properly.
* Most asynchronous libraries like {@link CompletableFuture} provide methods that accept an
* {@link Executor} to run callbacks on.
*/
default Executor contextAwareExecutor() {
// The implementation is the same as contextAwareEventLoop but we expose as an Executor as well given
// how common it is to use only as an Executor and it becomes much easier to write tests for an
// Executor (i.e., when(ctx.contextAwareExecutor()).thenReturn(MoreExecutors.directExecutor()));
return contextAwareEventLoop();
}
/**
* Returns an {@link EventLoop} that will make sure this {@link RequestContext} is set as the current
* context before executing any callback.
*/
default EventLoop contextAwareEventLoop() {
return new RequestContextAwareEventLoop(this, eventLoop());
}
/**
* Pushes the specified context to the thread-local stack. To pop the context from the stack, call
* {@link SafeCloseable#close()}, which can be done using a {@code try-with-resources} block.
*
* @deprecated Use {@link #push()}.
*/
@Deprecated
static SafeCloseable push(RequestContext ctx) {
return ctx.push(true);
}
/**
* Pushes the specified context to the thread-local stack. To pop the context from the stack, call
* {@link SafeCloseable#close()}, which can be done using a {@code try-with-resources} block.
*
* @deprecated Use {@link #push(boolean)}.
*/
@Deprecated
static SafeCloseable push(RequestContext ctx, boolean runCallbacks) {
return ctx.push(runCallbacks);
}
/**
* Pushes the specified context to the thread-local stack. To pop the context from the stack, call
* {@link SafeCloseable#close()}, which can be done using a {@code try-with-resources} block:
* {@code
* try (SafeCloseable ignored = ctx.push()) {
* ...
* }
* }
*
* The callbacks added by {@link #onEnter(Consumer)} and {@link #onExit(Consumer)} will be invoked
* when the context is pushed to and removed from the thread-local stack respectively.
*
*
NOTE: In case of re-entrance, the callbacks will never run.
*/
default SafeCloseable push() {
return push(true);
}
/**
* Pushes the specified context to the thread-local stack. To pop the context from the stack, call
* {@link SafeCloseable#close()}, which can be done using a {@code try-with-resources} block:
*
{@code
* try (PushHandle ignored = ctx.push(true)) {
* ...
* }
* }
*
* NOTE: This method is only useful when it is undesirable to invoke the callbacks, such as replacing
* the current context with another. Prefer {@link #push()} otherwise.
*
* @param runCallbacks if {@code true}, the callbacks added by {@link #onEnter(Consumer)} and
* {@link #onExit(Consumer)} will be invoked when the context is pushed to and
* removed from the thread-local stack respectively.
* If {@code false}, no callbacks will be executed.
* NOTE: In case of re-entrance, the callbacks will never run.
*/
default SafeCloseable push(boolean runCallbacks) {
final RequestContext oldCtx = RequestContextThreadLocal.getAndSet(this);
if (oldCtx == this) {
// Reentrance
return () -> { /* no-op */ };
}
if (runCallbacks) {
if (oldCtx != null) {
oldCtx.invokeOnChildCallbacks(this);
invokeOnEnterCallbacks();
return () -> {
invokeOnExitCallbacks();
RequestContextThreadLocal.set(oldCtx);
};
} else {
invokeOnEnterCallbacks();
return () -> {
invokeOnExitCallbacks();
RequestContextThreadLocal.remove();
};
}
} else {
if (oldCtx != null) {
return () -> RequestContextThreadLocal.set(oldCtx);
} else {
return RequestContextThreadLocal::remove;
}
}
}
/**
* Pushes this context to the thread-local stack if there is no current context. If there is and it is not
* same with this context (i.e. not reentrance), this method will throw an {@link IllegalStateException}.
* To pop the context from the stack, call {@link SafeCloseable#close()},
* which can be done using a {@code try-with-resources} block.
*/
default SafeCloseable pushIfAbsent() {
final RequestContext currentRequestContext = RequestContextThreadLocal.get();
if (currentRequestContext != null && currentRequestContext != this) {
throw new IllegalStateException(
"Trying to call object wrapped with context " + this + ", but context is currently " +
"set to " + currentRequestContext + ". This means the callback was called from " +
"unexpected thread or forgetting to close previous context.");
}
return push();
}
/**
* Returns an {@link Executor} that will execute callbacks in the given {@code executor}, making sure to
* propagate the current {@link RequestContext} into the callback execution. It is generally preferred to
* use {@link #contextAwareEventLoop()} to ensure the callback stays on the same thread as well.
*/
default Executor makeContextAware(Executor executor) {
return runnable -> executor.execute(makeContextAware(runnable));
}
/**
* Returns an {@link ExecutorService} that will execute callbacks in the given {@code executor}, making
* sure to propagate the current {@link RequestContext} into the callback execution.
*/
default ExecutorService makeContextAware(ExecutorService executor) {
return new RequestContextAwareExecutorService(this, executor);
}
/**
* Returns a {@link Callable} that makes sure the current {@link RequestContext} is set and then invokes
* the input {@code callable}.
*/
Callable makeContextAware(Callable callable);
/**
* Returns a {@link Runnable} that makes sure the current {@link RequestContext} is set and then invokes
* the input {@code runnable}.
*/
Runnable makeContextAware(Runnable runnable);
/**
* Returns a {@link Function} that makes sure the current {@link RequestContext} is set and then invokes
* the input {@code function}.
*/
Function makeContextAware(Function function);
/**
* Returns a {@link BiFunction} that makes sure the current {@link RequestContext} is set and then invokes
* the input {@code function}.
*/
BiFunction makeContextAware(BiFunction function);
/**
* Returns a {@link Consumer} that makes sure the current {@link RequestContext} is set and then invokes
* the input {@code action}.
*/
Consumer makeContextAware(Consumer action);
/**
* Returns a {@link BiConsumer} that makes sure the current {@link RequestContext} is set and then invokes
* the input {@code action}.
*/
BiConsumer makeContextAware(BiConsumer action);
/**
* Returns a {@link FutureListener} that makes sure the current {@link RequestContext} is set and then
* invokes the input {@code listener}.
*
* @deprecated Use {@link CompletableFuture} instead.
*/
@Deprecated
FutureListener makeContextAware(FutureListener listener);
/**
* Returns a {@link ChannelFutureListener} that makes sure the current {@link RequestContext} is set and
* then invokes the input {@code listener}.
*
* @deprecated Use {@link CompletableFuture} instead.
*/
@Deprecated
ChannelFutureListener makeContextAware(ChannelFutureListener listener);
/**
* Returns a {@link GenericFutureListener} that makes sure the current {@link RequestContext} is set and
* then invokes the input {@code listener}. Unlike other versions of {@code makeContextAware}, this one will
* invoke the listener with the future's result even if the context has already been timed out.
* @deprecated Use {@link CompletableFuture} instead.
*/
@Deprecated
> GenericFutureListener makeContextAware(GenericFutureListener listener);
/**
* Returns a {@link CompletionStage} that makes sure the current {@link CompletionStage} is set and
* then invokes the input {@code stage}.
*/
CompletionStage makeContextAware(CompletionStage stage);
/**
* Returns a {@link CompletableFuture} that makes sure the current {@link CompletableFuture} is set and
* then invokes the input {@code future}.
*/
default CompletableFuture makeContextAware(CompletableFuture future) {
return makeContextAware((CompletionStage) future).toCompletableFuture();
}
/**
* Returns whether this {@link RequestContext} has been timed-out (e.g., when the corresponding request
* passes a deadline).
*/
boolean isTimedOut();
/**
* Registers {@code callback} to be run when re-entering this {@link RequestContext}, usually when using
* the {@link #makeContextAware} family of methods. Any thread-local state associated with this context
* should be restored by this callback.
*
* @param callback a {@link Consumer} whose argument is this context
*/
void onEnter(Consumer super RequestContext> callback);
/**
* Registers {@code callback} to be run when re-entering this {@link RequestContext}, usually when using
* the {@link #makeContextAware} family of methods. Any thread-local state associated with this context
* should be restored by this callback.
*
* @deprecated Use {@link #onEnter(Consumer)} instead.
*/
@Deprecated
default void onEnter(Runnable callback) {
onEnter(ctx -> callback.run());
}
/**
* Registers {@code callback} to be run when re-exiting this {@link RequestContext}, usually when using
* the {@link #makeContextAware} family of methods. Any thread-local state associated with this context
* should be reset by this callback.
*
* @param callback a {@link Consumer} whose argument is this context
*/
void onExit(Consumer super RequestContext> callback);
/**
* Registers {@code callback} to be run when re-exiting this {@link RequestContext}, usually when using
* the {@link #makeContextAware} family of methods. Any thread-local state associated with this context
* should be reset by this callback.
*
* @deprecated Use {@link #onExit(Consumer)} instead.
*/
@Deprecated
default void onExit(Runnable callback) {
onExit(ctx -> callback.run());
}
/**
* Registers {@code callback} to be run when this context is replaced by a child context.
* You could use this method to inherit an attribute of this context to the child contexts or
* register a callback to the child contexts that may be created later:
* {@code
* ctx.onChild((curCtx, newCtx) -> {
* assert ctx == curCtx && curCtx != newCtx;
* // Inherit the value of the 'MY_ATTR' attribute to the child context.
* newCtx.attr(MY_ATTR).set(curCtx.attr(MY_ATTR).get());
* // Add a callback to the child context.
* newCtx.onExit(() -> { ... });
* });
* }
*
* @param callback a {@link BiConsumer} whose first argument is this context and
* whose second argument is the new context that replaces this context
*/
void onChild(BiConsumer super RequestContext, ? super RequestContext> callback);
/**
* Invokes all {@link #onEnter(Consumer)} callbacks. It is discouraged to use this method directly.
* Use {@link #makeContextAware(Runnable)} or {@link #push(boolean)} instead so that the callbacks are
* invoked automatically.
*/
void invokeOnEnterCallbacks();
/**
* Invokes all {@link #onExit(Consumer)} callbacks. It is discouraged to use this method directly.
* Use {@link #makeContextAware(Runnable)} or {@link #push(boolean)} instead so that the callbacks are
* invoked automatically.
*/
void invokeOnExitCallbacks();
/**
* Invokes all {@link #onChild(BiConsumer)} callbacks. It is discouraged to use this method directly.
* Use {@link #makeContextAware(Runnable)} or {@link #push(boolean)} instead so that the callbacks are
* invoked automatically.
*/
void invokeOnChildCallbacks(RequestContext newCtx);
/**
* Resolves the specified {@code promise} with the specified {@code result} so that the {@code promise} is
* marked as 'done'. If {@code promise} is done already, this method does the following:
*
* - Log a warning about the failure, and
* - Release {@code result} if it is {@linkplain ReferenceCounted a reference-counted object},
* such as {@link ByteBuf} and {@link FullHttpResponse}.
*
* Note that a {@link Promise} can be done already even if you did not call this method in the following
* cases:
*
* - Invocation timeout - The invocation associated with the {@link Promise} has been timed out.
* - User error - A service implementation called any of the following methods more than once:
*
* - {@link #resolvePromise(Promise, Object)}
* - {@link #rejectPromise(Promise, Throwable)}
* - {@link Promise#setSuccess(Object)}
* - {@link Promise#setFailure(Throwable)}
* - {@link Promise#cancel(boolean)}
*
*
*
*
* @deprecated Use {@link CompletableFuture} instead.
*/
@Deprecated
default void resolvePromise(Promise> promise, Object result) {
@SuppressWarnings("unchecked")
final Promise