servlet: configurable methodNameResolver (#12333)

Introduces configuring a method name resolver in `ServletServerBuilder` for customizing the servlet context root path for request paths.
This commit is contained in:
Alex Panchenko 2025-09-16 05:47:18 +02:00 committed by GitHub
parent 6fae71b740
commit 6f21bc2e6c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 32 additions and 4 deletions

View File

@ -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);

View File

@ -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<HttpServletRequest, String> 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<HttpServletRequest, String> methodNameResolver;
private final int maxInboundMessageSize;
private final Attributes attributes;
ServletAdapter(
ServerTransportListener transportListener,
List<? extends ServerStreamTracer.Factory> streamTracerFactories,
Function<HttpServletRequest, String> 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)) {

View File

@ -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<ServletServerBuilder> {
List<? extends ServerStreamTracer.Factory> streamTracerFactories;
private Function<HttpServletRequest, String> 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 ForwardingServerBuilder<ServletS
* Creates a {@link ServletAdapter}.
*/
public ServletAdapter buildServletAdapter() {
return new ServletAdapter(buildAndStart(), streamTracerFactories, maxInboundMessageSize);
return new ServletAdapter(buildAndStart(), streamTracerFactories, methodNameResolver,
maxInboundMessageSize);
}
/**
@ -176,6 +181,18 @@ public final class ServletServerBuilder extends ForwardingServerBuilder<ServletS
throw new UnsupportedOperationException("TLS should be configured by the servlet container");
}
/**
* Specifies how to determine gRPC method name from servlet request.
*
* <p>The default strategy is using {@link HttpServletRequest#getRequestURI()} without the leading
* slash.</p>
*/
public ServletServerBuilder methodNameResolver(
Function<HttpServletRequest, String> methodResolver) {
this.methodNameResolver = checkNotNull(methodResolver);
return this;
}
@Override
public ServletServerBuilder maxInboundMessageSize(int bytes) {
checkArgument(bytes >= 0, "bytes must be >= 0");

View File

@ -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();

View File

@ -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);