spring-cloud-gateway: Gateway don't close websocket session when client close websocket.

Gateway won’t close websocket session of backend when client close it, and when the backend continue to send message to client, the gateway will show error:

2018-03-30 16:41:53.432 ERROR 9255 --- [ctor-http-nio-2] .a.w.r.e.DefaultErrorWebExceptionHandler : Failed to handle request [GET http://localhost:8080/ws-test]

java.io.IOException: Broken pipe
	at sun.nio.ch.FileDispatcherImpl.write0(Native Method) ~[na:1.8.0_151]
	at sun.nio.ch.SocketDispatcher.write(SocketDispatcher.java:47) ~[na:1.8.0_151]
	at sun.nio.ch.IOUtil.writeFromNativeBuffer(IOUtil.java:93) ~[na:1.8.0_151]
	at sun.nio.ch.IOUtil.write(IOUtil.java:51) ~[na:1.8.0_151]
	at sun.nio.ch.SocketChannelImpl.write(SocketChannelImpl.java:471) ~[na:1.8.0_151]
	at io.netty.channel.socket.nio.NioSocketChannel.doWrite(NioSocketChannel.java:403) ~[netty-transport-4.1.22.Final.jar:4.1.22.Final]
	at io.netty.channel.AbstractChannel$AbstractUnsafe.flush0(AbstractChannel.java:934) ~[netty-transport-4.1.22.Final.jar:4.1.22.Final]
	at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.flush0(AbstractNioChannel.java:360) ~[netty-transport-4.1.22.Final.jar:4.1.22.Final]
	at io.netty.channel.AbstractChannel$AbstractUnsafe.flush(AbstractChannel.java:901) ~[netty-transport-4.1.22.Final.jar:4.1.22.Final]
	at io.netty.channel.DefaultChannelPipeline$HeadContext.flush(DefaultChannelPipeline.java:1376) ~[netty-transport-4.1.22.Final.jar:4.1.22.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeFlush0(AbstractChannelHandlerContext.java:776) ~[netty-transport-4.1.22.Final.jar:4.1.22.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeFlush(AbstractChannelHandlerContext.java:768) ~[netty-transport-4.1.22.Final.jar:4.1.22.Final]
	at io.netty.channel.AbstractChannelHandlerContext.flush(AbstractChannelHandlerContext.java:749) ~[netty-transport-4.1.22.Final.jar:4.1.22.Final]
	at io.netty.channel.ChannelOutboundHandlerAdapter.flush(ChannelOutboundHandlerAdapter.java:115) ~[netty-transport-4.1.22.Final.jar:4.1.22.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeFlush0(AbstractChannelHandlerContext.java:776) ~[netty-transport-4.1.22.Final.jar:4.1.22.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeFlush(AbstractChannelHandlerContext.java:768) ~[netty-transport-4.1.22.Final.jar:4.1.22.Final]
	at io.netty.channel.AbstractChannelHandlerContext.flush(AbstractChannelHandlerContext.java:749) ~[netty-transport-4.1.22.Final.jar:4.1.22.Final]
	at reactor.ipc.netty.channel.ChannelOperationsHandler.lambda$scheduleFlush$0(ChannelOperationsHandler.java:333) ~[reactor-netty-0.7.5.RELEASE.jar:0.7.5.RELEASE]
	at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163) ~[netty-common-4.1.22.Final.jar:4.1.22.Final]
	at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:404) ~[netty-common-4.1.22.Final.jar:4.1.22.Final]
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:463) ~[netty-transport-4.1.22.Final.jar:4.1.22.Final]
	at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:886) ~[netty-common-4.1.22.Final.jar:4.1.22.Final]
	at java.lang.Thread.run(Thread.java:748) ~[na:1.8.0_151]

2018-03-30 16:41:53.482 ERROR 9255 --- [ctor-http-nio-2] o.s.w.s.adapter.HttpWebHandlerAdapter    : Failed to handle request [GET http://localhost:8080/ws-test]

java.lang.IllegalStateException: Status and headers already sent
	at reactor.ipc.netty.http.server.HttpServerOperations.status(HttpServerOperations.java:345) ~[reactor-netty-0.7.5.RELEASE.jar:0.7.5.RELEASE]
	at org.springframework.http.server.reactive.ReactorServerHttpResponse.applyStatusCode(ReactorServerHttpResponse.java:69) ~[spring-web-5.0.4.RELEASE.jar:5.0.4.RELEASE]
	at org.springframework.http.server.reactive.AbstractServerHttpResponse.lambda$doCommit$4(AbstractServerHttpResponse.java:213) ~[spring-web-5.0.4.RELEASE.jar:5.0.4.RELEASE]
	at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) ~[na:1.8.0_151]
	at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1380) ~[na:1.8.0_151]
	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481) ~[na:1.8.0_151]
	at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471) ~[na:1.8.0_151]
	at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708) ~[na:1.8.0_151]
	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:1.8.0_151]
	at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499) ~[na:1.8.0_151]
...

version Finchley.M9

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 27 (8 by maintainers)

Commits related to this issue

Most upvoted comments

I created a simple Spring Boot app reproducing the problem at https://github.com/dharezlak/spring-cloud-gateway-websocket.

This issue can be easily replicated with a simple Spring Boot app:

Maven deps:

<properties>
  <spring-cloud.version>Finchley.M6</spring-cloud.version>
</properties>

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
  </dependency>
</dependencies>

Web Socket handler:

@Component
public class MyWebSocketHandler implements WebSocketHandler {

	@Override
	public Mono<Void> handle(WebSocketSession session) {
		return session
				.receive()
				.doOnNext(System.out::println)
				.log()
				.doFinally(sig -> System.out.println("Web socket session finished: " + sig.name()))
				.then();
	}
}

Config:

@SpringBootApplication
public class WsdemoApplication {

	@Autowired
	private WebSocketHandler webSocketHandler;

	public static void main(String[] args) {
		SpringApplication.run(WsdemoApplication.class, args);
	}

	@Bean
	public HandlerMapping websocketmapping() {
		Map<String, WebSocketHandler> map = new HashMap<>();
	    map.put("/ws2", webSocketHandler);

	    SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping();
	    handlerMapping.setOrder(1);
	    handlerMapping.setUrlMap(map);
	    return handlerMapping;
	}

	@Bean
    public WebSocketHandlerAdapter handlerAdapter() {
        return new WebSocketHandlerAdapter();
    }

	@Bean
	public RouteLocator wsGateway(RouteLocatorBuilder builder) {
		return builder.routes()
				.route("ws", predicate -> predicate
						.path("/ws")
						.uri("ws://localhost:8080/ws2"))
				.build();
	}
}

In the browser you can test with:

var ws = new WebSocket("ws://localhost:8080/ws");
ws.send("hello");
ws.close();

and

var ws = new WebSocket("ws://localhost:8080/ws2");
ws.send("hello");
ws.close();

For the /ws endpoint (proxied) the connection is not closed immediately, but for /ws2 (not proxied) it is.

Hi @spencergibb , We are trying to use Spring Cloud Gateway as an edge-proxy to our Websocket endpoints, but seemingly the problem mentioned in this thread is still in place. If I just simply close the Websocket connection right after opened it, it closes without any issue. But if the client sends at least one message and tries to close after that, the connection seemingly will remain open, but neither the client nor the server will be able to send any more messages on that. I’ve tried even with 2.1.0.M1 version and still the same. Can we expect a fix on this in the near future?