From 6f21bc2e6ca7224243f8f1ea1a28eb72eba3dd9a Mon Sep 17 00:00:00 2001
From: Alex Panchenko <440271+panchenko@users.noreply.github.com>
Date: Tue, 16 Sep 2025 05:47:18 +0200
Subject: [PATCH] servlet: configurable methodNameResolver (#12333)
Introduces configuring a method name resolver in `ServletServerBuilder` for customizing the servlet context root path for request paths.
---
.../io/grpc/servlet/JettyTransportTest.java | 1 +
.../java/io/grpc/servlet/ServletAdapter.java | 8 +++++++-
.../io/grpc/servlet/ServletServerBuilder.java | 19 ++++++++++++++++++-
.../io/grpc/servlet/TomcatTransportTest.java | 4 +++-
.../grpc/servlet/UndertowTransportTest.java | 4 +++-
5 files changed, 32 insertions(+), 4 deletions(-)
diff --git a/servlet/src/jettyTest/java/io/grpc/servlet/JettyTransportTest.java b/servlet/src/jettyTest/java/io/grpc/servlet/JettyTransportTest.java
index e9cb391ea0..c896c7a23e 100644
--- a/servlet/src/jettyTest/java/io/grpc/servlet/JettyTransportTest.java
+++ b/servlet/src/jettyTest/java/io/grpc/servlet/JettyTransportTest.java
@@ -69,6 +69,7 @@ public class JettyTransportTest extends AbstractTransportTest {
listener.transportCreated(new ServletServerBuilder.ServerTransportImpl(scheduler));
ServletAdapter adapter =
new ServletAdapter(serverTransportListener, streamTracerFactories,
+ ServletAdapter.DEFAULT_METHOD_NAME_RESOLVER,
Integer.MAX_VALUE);
GrpcServlet grpcServlet = new GrpcServlet(adapter);
diff --git a/servlet/src/main/java/io/grpc/servlet/ServletAdapter.java b/servlet/src/main/java/io/grpc/servlet/ServletAdapter.java
index 4bfe894977..e84b9341fd 100644
--- a/servlet/src/main/java/io/grpc/servlet/ServletAdapter.java
+++ b/servlet/src/main/java/io/grpc/servlet/ServletAdapter.java
@@ -45,6 +45,7 @@ import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
import java.util.logging.Logger;
import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
@@ -72,18 +73,23 @@ import javax.servlet.http.HttpServletResponse;
public final class ServletAdapter {
static final Logger logger = Logger.getLogger(ServletAdapter.class.getName());
+ static final Function DEFAULT_METHOD_NAME_RESOLVER =
+ req -> req.getRequestURI().substring(1); // remove the leading "/"
private final ServerTransportListener transportListener;
private final List extends ServerStreamTracer.Factory> streamTracerFactories;
+ private final Function methodNameResolver;
private final int maxInboundMessageSize;
private final Attributes attributes;
ServletAdapter(
ServerTransportListener transportListener,
List extends ServerStreamTracer.Factory> streamTracerFactories,
+ Function methodNameResolver,
int maxInboundMessageSize) {
this.transportListener = transportListener;
this.streamTracerFactories = streamTracerFactories;
+ this.methodNameResolver = methodNameResolver;
this.maxInboundMessageSize = maxInboundMessageSize;
attributes = transportListener.transportReady(Attributes.EMPTY);
}
@@ -119,7 +125,7 @@ public final class ServletAdapter {
AsyncContext asyncCtx = req.startAsync(req, resp);
- String method = req.getRequestURI().substring(1); // remove the leading "/"
+ String method = methodNameResolver.apply(req);
Metadata headers = getHeaders(req);
if (logger.isLoggable(FINEST)) {
diff --git a/servlet/src/main/java/io/grpc/servlet/ServletServerBuilder.java b/servlet/src/main/java/io/grpc/servlet/ServletServerBuilder.java
index 72c4383d27..aee25de01a 100644
--- a/servlet/src/main/java/io/grpc/servlet/ServletServerBuilder.java
+++ b/servlet/src/main/java/io/grpc/servlet/ServletServerBuilder.java
@@ -49,8 +49,10 @@ import java.net.SocketAddress;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
+import java.util.function.Function;
import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe;
+import javax.servlet.http.HttpServletRequest;
/**
* Builder to build a gRPC server that can run as a servlet. This is for advanced custom settings.
@@ -64,6 +66,8 @@ import javax.annotation.concurrent.NotThreadSafe;
@NotThreadSafe
public final class ServletServerBuilder extends ForwardingServerBuilder {
List extends ServerStreamTracer.Factory> streamTracerFactories;
+ private Function methodNameResolver =
+ ServletAdapter.DEFAULT_METHOD_NAME_RESOLVER;
int maxInboundMessageSize = DEFAULT_MAX_MESSAGE_SIZE;
private final ServerImplBuilder serverImplBuilder;
@@ -98,7 +102,8 @@ public final class ServletServerBuilder extends ForwardingServerBuilderThe default strategy is using {@link HttpServletRequest#getRequestURI()} without the leading
+ * slash.
+ */
+ public ServletServerBuilder methodNameResolver(
+ Function methodResolver) {
+ this.methodNameResolver = checkNotNull(methodResolver);
+ return this;
+ }
+
@Override
public ServletServerBuilder maxInboundMessageSize(int bytes) {
checkArgument(bytes >= 0, "bytes must be >= 0");
diff --git a/servlet/src/tomcatTest/java/io/grpc/servlet/TomcatTransportTest.java b/servlet/src/tomcatTest/java/io/grpc/servlet/TomcatTransportTest.java
index 262036883a..2171c6eb2d 100644
--- a/servlet/src/tomcatTest/java/io/grpc/servlet/TomcatTransportTest.java
+++ b/servlet/src/tomcatTest/java/io/grpc/servlet/TomcatTransportTest.java
@@ -81,7 +81,9 @@ public class TomcatTransportTest extends AbstractTransportTest {
ServerTransportListener serverTransportListener =
listener.transportCreated(new ServerTransportImpl(scheduler));
ServletAdapter adapter =
- new ServletAdapter(serverTransportListener, streamTracerFactories, Integer.MAX_VALUE);
+ new ServletAdapter(serverTransportListener, streamTracerFactories,
+ ServletAdapter.DEFAULT_METHOD_NAME_RESOLVER,
+ Integer.MAX_VALUE);
GrpcServlet grpcServlet = new GrpcServlet(adapter);
tomcatServer = new Tomcat();
diff --git a/servlet/src/undertowTest/java/io/grpc/servlet/UndertowTransportTest.java b/servlet/src/undertowTest/java/io/grpc/servlet/UndertowTransportTest.java
index e14c11985d..ef897c87d7 100644
--- a/servlet/src/undertowTest/java/io/grpc/servlet/UndertowTransportTest.java
+++ b/servlet/src/undertowTest/java/io/grpc/servlet/UndertowTransportTest.java
@@ -100,7 +100,9 @@ public class UndertowTransportTest extends AbstractTransportTest {
ServerTransportListener serverTransportListener =
listener.transportCreated(new ServerTransportImpl(scheduler));
ServletAdapter adapter =
- new ServletAdapter(serverTransportListener, streamTracerFactories, Integer.MAX_VALUE);
+ new ServletAdapter(serverTransportListener, streamTracerFactories,
+ ServletAdapter.DEFAULT_METHOD_NAME_RESOLVER,
+ Integer.MAX_VALUE);
GrpcServlet grpcServlet = new GrpcServlet(adapter);
InstanceFactory extends Servlet> instanceFactory =
() -> new ImmediateInstanceHandle<>(grpcServlet);