From 4e735f1ca8ee12942c4125de2b0a70858e2c4779 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Fri, 5 Feb 2021 21:07:27 +0000 Subject: [PATCH] Allow HTTP GET and WebSocket to use the same path Closes gh-30 --- .../boot/WebFluxGraphQLAutoConfiguration.java | 21 ++++++++++----- .../boot/WebMvcGraphQLAutoConfiguration.java | 26 +++++++++++++------ .../src/main/resources/application.properties | 2 +- .../src/main/resources/static/index.html | 2 +- 4 files changed, 35 insertions(+), 16 deletions(-) diff --git a/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/WebFluxGraphQLAutoConfiguration.java b/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/WebFluxGraphQLAutoConfiguration.java index 23829ee..9e85dc0 100644 --- a/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/WebFluxGraphQLAutoConfiguration.java +++ b/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/WebFluxGraphQLAutoConfiguration.java @@ -19,6 +19,7 @@ import java.util.stream.Collectors; import graphql.GraphQL; +import reactor.core.publisher.Mono; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfigureAfter; @@ -41,6 +42,7 @@ import org.springframework.web.reactive.function.server.RouterFunctions; import org.springframework.web.reactive.function.server.ServerResponse; import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping; +import org.springframework.web.server.ServerWebExchange; import static org.springframework.web.reactive.function.server.RequestPredicates.accept; import static org.springframework.web.reactive.function.server.RequestPredicates.contentType; @@ -90,14 +92,21 @@ public GraphQLWebSocketHandler graphQLWebSocketHandler( public HandlerMapping graphQLWebSocketEndpoint( GraphQLWebSocketHandler handler, GraphQLProperties properties) { - String path = properties.getWebsocket().getPath(); - SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping(); - mapping.setUrlMap(Collections.singletonMap(path, handler)); - mapping.setOrder(-1); // Ahead of annotated controllers - return mapping; + WebSocketHandlerMapping handlerMapping = new WebSocketHandlerMapping(); + handlerMapping.setUrlMap(Collections.singletonMap(properties.getWebsocket().getPath(), handler)); + handlerMapping.setOrder(-2); // Ahead of HTTP endpoint ("routerFunctionMapping" bean) + return handlerMapping; } - } + private static class WebSocketHandlerMapping extends SimpleUrlHandlerMapping { + + @Override + public Mono getHandlerInternal(ServerWebExchange exchange) { + return ("WebSocket".equalsIgnoreCase(exchange.getRequest().getHeaders().getUpgrade()) ? + super.getHandlerInternal(exchange) : Mono.empty()); + } + } + } diff --git a/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/WebMvcGraphQLAutoConfiguration.java b/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/WebMvcGraphQLAutoConfiguration.java index 7d95734..cb6585c 100644 --- a/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/WebMvcGraphQLAutoConfiguration.java +++ b/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/WebMvcGraphQLAutoConfiguration.java @@ -19,6 +19,7 @@ import java.util.Map; import java.util.stream.Collectors; +import javax.servlet.http.HttpServletRequest; import javax.websocket.server.ServerContainer; import graphql.GraphQL; @@ -38,6 +39,7 @@ import org.springframework.graphql.WebInterceptor; import org.springframework.graphql.webmvc.GraphQLHttpHandler; import org.springframework.graphql.webmvc.GraphQLWebSocketHandler; +import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.web.servlet.HandlerMapping; @@ -103,16 +105,24 @@ public GraphQLWebSocketHandler graphQLWebSocketHandler( @Bean public HandlerMapping graphQLWebSocketEndpoint(GraphQLWebSocketHandler handler, GraphQLProperties properties) { - WebSocketHttpRequestHandler httpRequestHandler = - new WebSocketHttpRequestHandler(handler, new DefaultHandshakeHandler()); - - String path = properties.getWebsocket().getPath(); - SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping(); - mapping.setUrlMap(Collections.singletonMap(path, httpRequestHandler)); - mapping.setOrder(-1); // Ahead of annotated controllers - return mapping; + WebSocketHandlerMapping handlerMapping = new WebSocketHandlerMapping(); + handlerMapping.setUrlMap(Collections.singletonMap( + properties.getWebsocket().getPath(), + new WebSocketHttpRequestHandler(handler, new DefaultHandshakeHandler()))); + handlerMapping.setOrder(2); // Ahead of HTTP endpoint ("routerFunctionMapping" bean) + return handlerMapping; } } + + private static class WebSocketHandlerMapping extends SimpleUrlHandlerMapping { + + @Override + protected Object getHandlerInternal(HttpServletRequest request) throws Exception { + return ("WebSocket".equalsIgnoreCase(request.getHeader(HttpHeaders.UPGRADE)) ? + super.getHandlerInternal(request) : null); + } + } + } diff --git a/samples/webflux-websocket/src/main/resources/application.properties b/samples/webflux-websocket/src/main/resources/application.properties index 2f8df9c..648982f 100644 --- a/samples/webflux-websocket/src/main/resources/application.properties +++ b/samples/webflux-websocket/src/main/resources/application.properties @@ -1,4 +1,4 @@ -spring.graphql.websocket.path=/graphql/websocket +spring.graphql.websocket.path=/graphql management.endpoints.web.exposure.include=health,metrics,info logging.level.org.springframework.web=debug logging.level.org.springframework.http=debug diff --git a/samples/webflux-websocket/src/main/resources/static/index.html b/samples/webflux-websocket/src/main/resources/static/index.html index 4ad1ff6..4ed8ffb 100644 --- a/samples/webflux-websocket/src/main/resources/static/index.html +++ b/samples/webflux-websocket/src/main/resources/static/index.html @@ -8,7 +8,7 @@