Spring cloud gateway에서 허가되지 않은 url character 사용하기
레거시 시스템에 Spring cloud gateway를 도입하면서 하나의 장벽에 부딪히게 되었습니다.
오랫동안 유지되어온 시스템에서 client들에게 완벽한 하위호환을 보장해주어야하는 상황에서 일부 client들이 url에 포함해서는 안되는 특수문자들을 인코딩을 하지 않고, 날 것으로 그대로 보내오고 있었습니다.
http://localhost:8080/test/case/key/var1|var2
[2dd57409, L:/127.0.0.1:8080 - R:/127.0.0.1:59831] Decoding failed: REQUEST(decodeResult: failure(java.net.URISyntaxException: Illegal character in path at index 40: http://localhost:8080/test/case/key/var1|var2), version: HTTP/1.1)
사용되어지는 특수문자들도 심지어 여러가지..
|, ^, {, } 등등.. 모두 url 인코딩이 필요한 character들인데, 인코딩 없이 request가 들어오고 있는 상황에서 문제는 기본적으로 Spring cloud gateway 내부에서 requet url을 이용하여 JAVA URI object를 생성하면서 URISyntaxException 을 발생 시킨다는 것 입니다.
실제 저희가 작성 중인 besinsess 로직으로 오기도 전에 framework 에서 자체적으로 400 을 리턴해버리고 있기 때문에 해당 framework 코드 동작 전에 무언가 조치가 필요한 상황이었습니다.
다행히도 netty 레벨에서 동작에 network state에 전환되는 중간에 custom 한 code를 개입할 수 있고, 이때 request 에 손을 대는 것이 가능했고 아래와 같이 이슈를 해결하였습니다.
@Slf4j
@Component
public class NettyWebServerCustomizer
implements WebServerFactoryCustomizer<NettyReactiveWebServerFactory> {
@Override
public void customize(NettyReactiveWebServerFactory factory) {
factory.addServerCustomizers(serverCustomizers -> serverCustomizers
.childObserve(new CustomEncoderObserver())
);
}
}
public class CustomEncoderObserver implements ConnectionObserver {
@Override
public void onStateChange(@NonNull Connection connection, @NonNull State newState) {
if (newState == ConnectionObserver.State.CONNECTED) {
connection.channel().pipeline()
.addAfter(
NettyPipeline.HttpCodec, "", new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(
@NonNull ChannelHandlerContext channelHandlerContext,
@NonNull Object message) {
if (message instanceof HttpRequest) {
HttpRequest request = (HttpRequest) message;
if (/* request.uri()를 받아와서 확인하고 싶은 포인트를 검증 */) {
request.setUri(/* uri를 변경한 것을 다시 SET */);
}
}
channelHandlerContext.fireChannelRead(message);
}
@Override
public boolean isSharable() {
return true;
}
});
}
}
}
Connection을 모니터링 한 후, connected 는 모든 연결에 최초 1회만 일어나기 때문에 이때 개입하여 특정 인코딩이 되지 않은 특수문자들에 대해서 수동으로 인코딩된 값으로 replace 하는 형태로 문제를 해결할 수 있었습니다.