diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/grpc/RemoteAddressInterceptor.java b/service/src/main/java/org/whispersystems/textsecuregcm/grpc/RemoteAddressInterceptor.java new file mode 100644 index 000000000..0fe21bdb5 --- /dev/null +++ b/service/src/main/java/org/whispersystems/textsecuregcm/grpc/RemoteAddressInterceptor.java @@ -0,0 +1,33 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.textsecuregcm.grpc; + +import io.grpc.Context; +import io.grpc.Contexts; +import io.grpc.Grpc; +import io.grpc.Metadata; +import io.grpc.ServerCall; +import io.grpc.ServerCallHandler; +import io.grpc.ServerInterceptor; +import java.net.SocketAddress; + +public class RemoteAddressInterceptor implements ServerInterceptor { + + @Override + public ServerCall.Listener interceptCall(final ServerCall serverCall, + final Metadata headers, + final ServerCallHandler next) { + + // Note: the specific implementation for getting a remote client address may change depending on the client + // connection strategy. The important thing is that the remote address wind up in the context for the current + // call so it can be retrieved by `RemoteAddressUtil`. + final SocketAddress remoteAddress = serverCall.getAttributes().get(Grpc.TRANSPORT_ATTR_REMOTE_ADDR); + + return Contexts.interceptCall( + Context.current().withValue(RemoteAddressUtil.REMOTE_ADDRESS_CONTEXT_KEY, remoteAddress), + serverCall, headers, next); + } +} diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/grpc/RemoteAddressUtil.java b/service/src/main/java/org/whispersystems/textsecuregcm/grpc/RemoteAddressUtil.java new file mode 100644 index 000000000..946a6a3ba --- /dev/null +++ b/service/src/main/java/org/whispersystems/textsecuregcm/grpc/RemoteAddressUtil.java @@ -0,0 +1,23 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.textsecuregcm.grpc; + +import io.grpc.Context; +import java.net.SocketAddress; + +public class RemoteAddressUtil { + + static final Context.Key REMOTE_ADDRESS_CONTEXT_KEY = Context.key("remote-address"); + + /** + * Returns the socket address of the remote client in the current gRPC request context. + * + * @return the socket address of the remote client + */ + public static SocketAddress getRemoteAddress() { + return REMOTE_ADDRESS_CONTEXT_KEY.get(); + } +} diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/grpc/MockRemoteAddressInterceptor.java b/service/src/test/java/org/whispersystems/textsecuregcm/grpc/MockRemoteAddressInterceptor.java new file mode 100644 index 000000000..11e316b42 --- /dev/null +++ b/service/src/test/java/org/whispersystems/textsecuregcm/grpc/MockRemoteAddressInterceptor.java @@ -0,0 +1,37 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.textsecuregcm.grpc; + +import io.grpc.Context; +import io.grpc.Contexts; +import io.grpc.Metadata; +import io.grpc.ServerCall; +import io.grpc.ServerCallHandler; +import io.grpc.ServerInterceptor; +import java.net.SocketAddress; +import javax.annotation.Nullable; + +public class MockRemoteAddressInterceptor implements ServerInterceptor { + + @Nullable + private SocketAddress remoteAddress; + + public void setRemoteAddress(@Nullable final SocketAddress remoteAddress) { + this.remoteAddress = remoteAddress; + } + + @Override + public ServerCall.Listener interceptCall(final ServerCall serverCall, + final Metadata headers, + final ServerCallHandler next) { + + return remoteAddress != null + ? next.startCall(serverCall, headers) + : Contexts.interceptCall( + Context.current().withValue(RemoteAddressUtil.REMOTE_ADDRESS_CONTEXT_KEY, remoteAddress), + serverCall, headers, next); + } +}