Include raw "Accept-Language" header value in RequestAttributes

This commit is contained in:
Ameya Lokare
2026-03-09 13:46:46 -07:00
parent 11df65b8d8
commit a320504fdc
6 changed files with 45 additions and 27 deletions
@@ -5,12 +5,37 @@
package org.whispersystems.textsecuregcm.grpc;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.InetAddress;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import javax.annotation.Nullable;
public record RequestAttributes(InetAddress remoteAddress,
@Nullable String userAgent,
@Nullable String acceptLanguageRaw,
List<Locale.LanguageRange> acceptLanguage) {
private static final Logger LOGGER = LoggerFactory.getLogger(RequestAttributes.class);
public RequestAttributes(InetAddress remoteAddress,
@Nullable String userAgent,
@Nullable String acceptLanguageRaw) {
this(remoteAddress, userAgent, acceptLanguageRaw, parseAcceptLanguage(acceptLanguageRaw, userAgent));
}
private static List<Locale.LanguageRange> parseAcceptLanguage(final String acceptLanguageRaw, final String userAgent) {
List<Locale.LanguageRange> acceptLanguages = Collections.emptyList();
if (StringUtils.isNotBlank(acceptLanguageRaw)) {
try {
acceptLanguages = Locale.LanguageRange.parse(acceptLanguageRaw);
} catch (final IllegalArgumentException e) {
LOGGER.debug("Invalid Accept-Language header from User-Agent {}: {}", userAgent, acceptLanguageRaw, e);
}
}
return acceptLanguages;
}
}
@@ -70,17 +70,8 @@ public class RequestAttributesInterceptor implements ServerInterceptor {
return ServerInterceptorUtil.closeWithStatus(call, Status.UNAVAILABLE);
}
@Nullable List<Locale.LanguageRange> acceptLanguages = Collections.emptyList();
if (StringUtils.isNotBlank(acceptLanguageHeader)) {
try {
acceptLanguages = Locale.LanguageRange.parse(acceptLanguageHeader);
} catch (final IllegalArgumentException e) {
log.debug("Invalid Accept-Language header from User-Agent {}: {}", userAgentHeader, acceptLanguageHeader, e);
}
}
final RequestAttributes requestAttributes =
new RequestAttributes(remoteAddress.get(), userAgentHeader, acceptLanguages);
new RequestAttributes(remoteAddress.get(), userAgentHeader, acceptLanguageHeader);
return Contexts.interceptCall(
Context.current().withValue(RequestAttributesUtil.REQUEST_ATTRIBUTES_CONTEXT_KEY, requestAttributes),
call, headers, next);
@@ -1,6 +1,7 @@
package org.whispersystems.textsecuregcm.grpc;
import io.grpc.Context;
import javax.annotation.Nullable;
import java.net.InetAddress;
import java.util.Arrays;
import java.util.List;
@@ -22,6 +23,15 @@ public class RequestAttributesUtil {
return REQUEST_ATTRIBUTES_CONTEXT_KEY.get().acceptLanguage();
}
/**
* Returns the raw "Accept-Language" header string from the remote client in the current gRPC request context
*
* @return the raw "Accept-Language" header listed by the remote client; may be null if not specified
*/
public static @Nullable String getAcceptLanguageRaw() {
return REQUEST_ATTRIBUTES_CONTEXT_KEY.get().acceptLanguageRaw();
}
/**
* Returns a list of distinct locales supported by the JVM and accepted by the remote client in the current gRPC
* context. May be empty if the client did not supply a list of acceptable languages, if the list of acceptable
@@ -96,7 +96,7 @@ public class ProfileAnonymousGrpcServiceTest extends SimpleBaseGrpcTest<ProfileA
protected ProfileAnonymousGrpcService createServiceBeforeEachTest() {
getMockRequestAttributesInterceptor().setRequestAttributes(new RequestAttributes(InetAddresses.forString("127.0.0.1"),
"Signal-Android/1.2.3",
Locale.LanguageRange.parse("en-us")));
"en-us"));
return new ProfileAnonymousGrpcService(
accountsManager,
@@ -173,7 +173,7 @@ public class ProfileGrpcServiceTest extends SimpleBaseGrpcTest<ProfileGrpcServic
getMockRequestAttributesInterceptor().setRequestAttributes(new RequestAttributes(InetAddresses.forString("127.0.0.1"),
"Signal-Android/1.2.3",
Locale.LanguageRange.parse("en-us")));
"en-us"));
when(rateLimiters.getProfileLimiter()).thenReturn(rateLimiter);
@@ -22,22 +22,22 @@ class RequestAttributesUtilTest {
@Test
void getAcceptableLanguages() throws Exception {
assertEquals(Collections.emptyList(),
callWithRequestAttributes(buildRequestAttributes(Collections.emptyList()),
callWithRequestAttributes(buildRequestAttributes(null, null),
RequestAttributesUtil::getAcceptableLanguages));
assertEquals(Locale.LanguageRange.parse("en,ja"),
callWithRequestAttributes(buildRequestAttributes(Locale.LanguageRange.parse("en,ja")),
callWithRequestAttributes(buildRequestAttributes(null, "en,ja"),
RequestAttributesUtil::getAcceptableLanguages));
}
@Test
void getAvailableAcceptedLocales() throws Exception {
assertEquals(Collections.emptyList(),
callWithRequestAttributes(buildRequestAttributes(Collections.emptyList()),
callWithRequestAttributes(buildRequestAttributes(null, null),
RequestAttributesUtil::getAvailableAcceptedLocales));
final List<Locale> availableAcceptedLocales =
callWithRequestAttributes(buildRequestAttributes(Locale.LanguageRange.parse("en,ja")),
callWithRequestAttributes(buildRequestAttributes(null, "en,ja"),
RequestAttributesUtil::getAvailableAcceptedLocales);
assertFalse(availableAcceptedLocales.isEmpty());
@@ -56,11 +56,11 @@ class RequestAttributesUtilTest {
@Test
void getUserAgent() throws Exception {
assertEquals(Optional.empty(),
callWithRequestAttributes(buildRequestAttributes((String) null),
callWithRequestAttributes(buildRequestAttributes(null, null),
RequestAttributesUtil::getUserAgent));
assertEquals(Optional.of("Signal-Desktop/1.2.3 Linux"),
callWithRequestAttributes(buildRequestAttributes("Signal-Desktop/1.2.3 Linux"),
callWithRequestAttributes(buildRequestAttributes("Signal-Desktop/1.2.3 Linux", null),
RequestAttributesUtil::getUserAgent));
}
@@ -70,16 +70,8 @@ class RequestAttributesUtilTest {
.call(callable);
}
private static RequestAttributes buildRequestAttributes(final String userAgent) {
return buildRequestAttributes(userAgent, Collections.emptyList());
}
private static RequestAttributes buildRequestAttributes(final List<Locale.LanguageRange> acceptLanguage) {
return buildRequestAttributes(null, acceptLanguage);
}
private static RequestAttributes buildRequestAttributes(@Nullable final String userAgent,
final List<Locale.LanguageRange> acceptLanguage) {
@Nullable final String acceptLanguage) {
return new RequestAttributes(REMOTE_ADDRESS, userAgent, acceptLanguage);
}