@Override public Mono<Void> upgrade(ServerWebExchange exchange, WebSocketHandler handler, @Nullable String subProtocol, Supplier<HandshakeInfo> handshakeInfoFactory) { ServerHttpResponse response = exchange.getResponse(); HttpServerResponse reactorResponse = ((AbstractServerHttpResponse) response).getNativeResponse(); HandshakeInfo handshakeInfo = handshakeInfoFactory.get(); NettyDataBufferFactory bufferFactory = (NettyDataBufferFactory) response.bufferFactory(); return reactorResponse.sendWebsocket(subProtocol, this.maxFramePayloadLength, (in, out) -> { ReactorNettyWebSocketSession session = new ReactorNettyWebSocketSession( in, out, handshakeInfo, bufferFactory, this.maxFramePayloadLength); return handler.handle(session); }); }
@Override public Mono<CloseableChannel> start(ConnectionAcceptor acceptor) { Objects.requireNonNull(acceptor, "acceptor must not be null"); return server .handle( (request, response) -> { transportHeaders.get().forEach(response::addHeader); return response.sendWebsocket(WebsocketRouteTransport.newHandler(acceptor)); }) .bind() .map(CloseableChannel::new); } }
/** * Upgrade connection to Websocket. Mono and Callback are invoked on handshake * success, otherwise the returned {@link Mono} fails. * * @param websocketHandler the in/out handler for ws transport * @return a {@link Mono} completing when upgrade is confirmed */ default Mono<Void> sendWebsocket(BiFunction<? super WebsocketInbound, ? super WebsocketOutbound, ? extends Publisher<Void>> websocketHandler) { return sendWebsocket(null, websocketHandler); }
/** * Upgrade connection to Websocket. Mono and Callback are invoked on handshake * success, otherwise the returned {@link Mono} fails. * * @param websocketHandler the in/out handler for ws transport * @return a {@link Mono} completing when upgrade is confirmed */ default Mono<Void> sendWebsocket(BiFunction<? super WebsocketInbound, ? super WebsocketOutbound, ? extends Publisher<Void>> websocketHandler) { return sendWebsocket(null, websocketHandler); }
/** * Upgrade connection to Websocket with optional subprotocol(s). Mono and Callback * are invoked on handshake success, otherwise the returned {@link Mono} fails. * * @param protocols optional sub-protocol * @param websocketHandler the in/out handler for ws transport * * @return a {@link Mono} completing when upgrade is confirmed */ default Mono<Void> sendWebsocket(@Nullable String protocols, BiFunction<? super WebsocketInbound, ? super WebsocketOutbound, ? extends Publisher<Void>> websocketHandler) { return sendWebsocket(protocols, 65536, websocketHandler); }
/** * Upgrade connection to Websocket with optional subprotocol(s). Mono and Callback * are invoked on handshake success, otherwise the returned {@link Mono} fails. * * @param protocols optional sub-protocol * @param websocketHandler the in/out handler for ws transport * * @return a {@link Mono} completing when upgrade is confirmed */ default Mono<Void> sendWebsocket(@Nullable String protocols, BiFunction<? super WebsocketInbound, ? super WebsocketOutbound, ? extends Publisher<Void>> websocketHandler) { return sendWebsocket(protocols, 65536, websocketHandler); }
@Override public Mono<CloseableChannel> start(ConnectionAcceptor acceptor) { Objects.requireNonNull(acceptor, "acceptor must not be null"); return server .handle( (request, response) -> { transportHeaders.get().forEach(response::addHeader); return response.sendWebsocket(WebsocketRouteTransport.newHandler(acceptor)); }) .bind() .map(CloseableChannel::new); } }
@Override public Publisher<Void> apply(HttpServerRequest httpRequest, HttpServerResponse httpResponse) { return httpResponse.sendWebsocket( (WebsocketInbound inbound, WebsocketOutbound outbound) -> onConnect(new WebsocketSession(messageCodec, httpRequest, inbound, outbound))); }
@Test public void anySubprotocolSelectsFirstClientProvided() { httpServer = HttpServer.create() .port(0) .handle((in, out) -> out.sendWebsocket("proto2,*", (i, o) -> o.sendString( Mono.just("SERVER:" + o.selectedSubprotocol())))) .wiretap(true) .bindNow(); String res = Objects.requireNonNull( HttpClient.create() .port(httpServer.address().getPort()) .headers(h -> h.add("Authorization", auth)) .websocket("proto1, proto2") .uri("/test") .handle((in, out) -> in.receive() .asString() .map(srv -> "CLIENT:" + in.selectedSubprotocol() + "-" + srv)) .log() .collectList() .block(Duration.ofSeconds(30))).get(0); Assert.assertThat(res, is("CLIENT:proto1-SERVER:proto1")); }
@Test public void noSubprotocolSelected() { httpServer = HttpServer.create() .port(0) .handle((in, out) -> out.sendWebsocket((i, o) -> o.sendString( Mono.just("SERVER:" + o.selectedSubprotocol())))) .wiretap(true) .bindNow(); String res = Objects.requireNonNull( HttpClient.create() .port(httpServer.address().getPort()) .headers(h -> h.add("Authorization", auth)) .websocket() .uri("/test") .handle((in, out) -> in.receive() .asString() .map(srv -> "CLIENT:" + in.selectedSubprotocol() + "-" + srv)) .log() .collectList() .block(Duration.ofSeconds(30))).get(0); Assert.assertThat(res, is("CLIENT:null-SERVER:null")); }
@Test public void simpleTest() { httpServer = HttpServer.create() .port(0) .handle((in, out) -> out.sendWebsocket((i, o) -> o.sendString(Mono.just("test")))) .wiretap(true) .bindNow(); String res = Objects.requireNonNull( HttpClient.create() .port(httpServer.address().getPort()) .wiretap(true) .headers(h -> h.add("Authorization", auth)) .websocket() .uri("/test") .handle((i, o) -> i.receive().asString()) .log("client") .collectList() .block()).get(0); Assert.assertThat(res, is("test")); }
@Test public void simpleSubprotocolServerSupported() { httpServer = HttpServer.create() .port(0) .handle((in, out) -> out.sendWebsocket("SUBPROTOCOL", (i, o) -> o.sendString(Mono.just("test")))) .wiretap(true) .bindNow(); String res = Objects.requireNonNull( HttpClient.create() .port(httpServer.address() .getPort()) .wiretap(true) .headers(h -> h.add("Authorization", auth)) .websocket("SUBPROTOCOL,OTHER") .uri("/test") .handle((i, o) -> i.receive().asString()) .log() .collectList() .block(Duration.ofSeconds(30))).get(0); Assert.assertThat(res, is("test")); }
@Test public void testMaxFramePayloadLengthFailed() { httpServer = HttpServer.create() .port(0) .handle((in, out) -> out.sendWebsocket((i, o) -> o.sendString(Mono.just("12345678901")))) .wiretap(true) .bindNow(); Mono<Void> response = HttpClient.create() .port(httpServer.address().getPort()) .websocket(10) .handle((in, out) -> in.receive() .asString() .map(srv -> srv)) .log() .then(); StepVerifier.create(response) .expectError(CorruptedFrameException.class) .verify(Duration.ofSeconds(30)); }
@Test public void testMaxFramePayloadLengthSuccess() { httpServer = HttpServer.create() .port(0) .handle((in, out) -> out.sendWebsocket((i, o) -> o.sendString(Mono.just("12345678901")))) .wiretap(true) .bindNow(); Mono<Void> response = HttpClient.create() .port(httpServer.address().getPort()) .websocket(11) .handle((in, out) -> in.receive() .asString() .map(srv -> srv)) .log() .then(); StepVerifier.create(response) .expectComplete() .verify(Duration.ofSeconds(30)); }
@Test public void simpleSubprotocolServerNoSubprotocol() { httpServer = HttpServer.create() .port(0) .handle((in, out) -> out.sendWebsocket((i, o) -> o.sendString( Mono.just("test")))) .wiretap(true) .bindNow(); StepVerifier.create( HttpClient.create() .port(httpServer.address().getPort()) .headers(h -> h.add("Authorization", auth)) .websocket("SUBPROTOCOL,OTHER") .uri("/test") .handle((i, o) -> i.receive().asString()) ) .verifyErrorMessage("Invalid subprotocol. Actual: null. Expected one of: SUBPROTOCOL,OTHER"); }
@Test public void simpleSubprotocolServerNotSupported() { httpServer = HttpServer.create() .port(0) .handle((in, out) -> out.sendWebsocket( "protoA,protoB", (i, o) -> { return o.sendString(Mono.just("test")); })) .wiretap(true) .bindNow(); StepVerifier.create( HttpClient.create() .port(httpServer.address().getPort()) .headers(h -> h.add("Authorization", auth)) .websocket("SUBPROTOCOL,OTHER") .uri("/test") .handle((i, o) -> i.receive().asString()) ) //the SERVER returned null which means that it couldn't select a protocol .verifyErrorMessage("Invalid subprotocol. Actual: null. Expected one of: SUBPROTOCOL,OTHER"); }
@Test public void testIssue460() { DisposableServer server = HttpServer.create() .port(0) .host("::1") .wiretap(true) .handle((req, res) -> res.sendWebsocket((in, out) -> Mono.never())) .bindNow(); HttpClient httpClient = HttpClient.create() .addressSupplier(server::address) .wiretap(true) .headers(h -> h.add(HttpHeaderNames.HOST, "[::1")); StepVerifier.create(httpClient.websocket() .connect()) .expectError() .verify(Duration.ofSeconds(30)); server.disposeNow(); }
@Test public void testCloseWebSocketFrameSentByClient() { httpServer = HttpServer.create() .port(0) .handle((req, res) -> res.sendWebsocket((in, out) -> out.sendString(Mono.just("echo")) .sendObject(new CloseWebSocketFrame()))) .wiretap(true) .bindNow(); Mono<Void> response = HttpClient.create() .port(httpServer.address().getPort()) .websocket() .uri("/") .handle((in, out) -> out.sendObject(in.receiveFrames() .doOnNext(WebSocketFrame::retain) .then())) .next(); StepVerifier.create(response) .expectComplete() .verify(Duration.ofSeconds(30)); }
@Test public void serverWebSocketFailed() { httpServer = HttpServer.create() .port(0) .handle((in, out) -> { if (!in.requestHeaders().contains("Authorization")) { return out.status(401); } else { return out.sendWebsocket((i, o) -> o.sendString(Mono.just("test"))); } }) .wiretap(true) .bindNow(); Mono<String> res = HttpClient.create() .port(httpServer.address() .getPort()) .websocket() .uri("/test") .handle((in, out) -> in.receive().aggregate().asString()) .next(); StepVerifier.create(res) .expectError(WebSocketHandshakeException.class) .verify(Duration.ofSeconds(30)); }
private void doTestIssue444(BiFunction<WebsocketInbound, WebsocketOutbound, Publisher<Void>> fn) { httpServer = HttpServer.create() .host("localhost") .port(0) .handle((req, res) -> res.sendWebsocket(null, fn)) .wiretap(true) .bindNow(); StepVerifier.create( HttpClient.create() .addressSupplier(() -> httpServer.address()) .wiretap(true) .websocket() .uri("/") .handle((i, o) -> i.receiveFrames() .then())) .expectComplete() .verify(Duration.ofSeconds(30)); }