Skip to content

Commit

Permalink
Exception handler interceptor is ordered correctly
Browse files Browse the repository at this point in the history
The exception handler interceptor has to be called first so that
it can handle exceptions thrown by other interceptors. Tests up
to now didn't assert that (they focused on exceptions thrown
in services).
  • Loading branch information
dsyer committed Jan 11, 2025
1 parent 6dc5d85 commit 1559843
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,27 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledOnOs;
import org.junit.jupiter.api.condition.OS;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.grpc.autoconfigure.server.GrpcServerProperties;
import org.springframework.grpc.client.ChannelBuilderOptions;
import org.springframework.grpc.client.GrpcChannelFactory;
import org.springframework.grpc.sample.proto.HelloReply;
import org.springframework.grpc.sample.proto.HelloRequest;
import org.springframework.grpc.sample.proto.SimpleGrpc;
import org.springframework.grpc.server.GlobalServerInterceptor;
import org.springframework.grpc.server.GrpcServerFactory;
import org.springframework.grpc.test.AutoConfigureInProcessTransport;
import org.springframework.grpc.test.LocalGrpcPort;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ActiveProfiles;

import io.grpc.ForwardingServerCallListener;
import io.grpc.ManagedChannel;
import io.grpc.ServerCall.Listener;
import io.grpc.ServerInterceptor;
import io.grpc.Status.Code;
import io.grpc.StatusRuntimeException;
import io.grpc.netty.NettyChannelBuilder;
Expand Down Expand Up @@ -86,6 +91,64 @@ void defaultErrorResponseIsUnknown(@Autowired GrpcChannelFactory channels) {

}

@Nested
@SpringBootTest
@AutoConfigureInProcessTransport
class ServerWithExceptionInInterceptor {

@Test
void specificErrorResponse(@Autowired GrpcChannelFactory channels) {
SimpleGrpc.SimpleBlockingStub client = SimpleGrpc.newBlockingStub(channels.createChannel("0.0.0.0:0"));
assertThat(assertThrows(StatusRuntimeException.class,
() -> client.sayHello(HelloRequest.newBuilder().setName("foo").build()))
.getStatus()
.getCode()).isEqualTo(Code.INVALID_ARGUMENT);
}

@TestConfiguration
static class TestConfig {

@Bean
@GlobalServerInterceptor
public ServerInterceptor exceptionInterceptor() {
return new CustomInterceptor();
}

static class CustomInterceptor implements ServerInterceptor {

@Override
public <ReqT, RespT> io.grpc.ServerCall.Listener<ReqT> interceptCall(
io.grpc.ServerCall<ReqT, RespT> call, io.grpc.Metadata headers,
io.grpc.ServerCallHandler<ReqT, RespT> next) {
return new CustomListener<>(next.startCall(call, headers));
}

}

static class CustomListener<ReqT> extends ForwardingServerCallListener<ReqT> {

private Listener<ReqT> delegate;

CustomListener(io.grpc.ServerCall.Listener<ReqT> delegate) {
this.delegate = delegate;
}

@Override
public void onReady() {
throw new IllegalArgumentException("test");
}

@Override
protected Listener<ReqT> delegate() {
return this.delegate;
}

}

}

}

@Nested
@SpringBootTest("spring.grpc.server.exception-handler.enabled=false")
@AutoConfigureInProcessTransport
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
* @see ServerInterceptor
* @see GrpcExceptionHandler
*/
@Order(Ordered.LOWEST_PRECEDENCE)
@Order(Ordered.HIGHEST_PRECEDENCE)
public class GrpcExceptionHandlerInterceptor implements ServerInterceptor {

private final GrpcExceptionHandler exceptionHandler;
Expand Down Expand Up @@ -79,6 +79,16 @@ static class ExceptionHandlerListener<ReqT, RespT> extends SimpleForwardingServe
this.exceptionHandler = exceptionHandler;
}

@Override
public void onReady() {
try {
super.onReady();
}
catch (Throwable t) {
this.call.close(this.exceptionHandler.handleException(t), new Metadata());
}
}

@Override
public void onMessage(ReqT message) {
try {
Expand Down

0 comments on commit 1559843

Please sign in to comment.