Enable WebSocket endpoint with config property

Prior to this commit, the WebSocket endpoint (MVC or WebFlux) would be
enabled by default. In the case of MVC, the presence of
`spring-websocket` and the required dependencies was an additional hint
that could enable/disable this support depending on the application.

This commit changes the websocket path configuration so that it doesn't
hold a default value anymore. Configuring this property will trigger the
WebSocket support.

Right now developers cannot configure it to be the same as the
`spring.graphql.path`, but this will be solved in #30.

This commit also revisits the configuration properties namespace for
clarity, moving the websocket bits under their own section.

Closes gh-31
This commit is contained in:
Brian Clozel
2021-02-02 15:27:00 +01:00
parent 5696b8cead
commit 36121f338f
5 changed files with 82 additions and 48 deletions

View File

@@ -128,6 +128,23 @@ management.metrics.graphql.autotime.enabled=true
You can contribute `RuntimeWiringCustomizer` beans to the context in order to configure the runtime wiring of your GraphQL application.
### WebSocket support
This project also supports WebSocket as a transport for GraphQL requests - you can use it to build [`Subscription` queries](http://spec.graphql.org/draft/#sec-Subscription).
This use case is powered by Reactor `Flux`, check out the `samples/webflux-websocket` sample application for more.
To enable this support, you need to configure the `spring.graphql.websocket.path` property in your application
and have the required dependencies on classpath. In the case of a Servlet application, adding the `spring-boot-starter-websocket` should be enough.
WebSocket support comes with dedicated properties:
````properties
# Path of the GraphQL WebSocket subscription endpoint.
spring.graphql.websocket.path=/graphql/websocket
# Time within which the initial {@code CONNECTION_INIT} type message must be received.
spring.graphql.websocket.connection-init-timeout=60s
````
### Extension points
You can contribute [`WebInterceptor` beans](https://github.com/spring-projects-experimental/spring-graphql/blob/master/spring-graphql-web/src/main/java/org/springframework/graphql/WebInterceptor.java)

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2020-2020 the original author or authors.
* Copyright 2020-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,47 +32,54 @@ public class GraphQLProperties {
*/
private String path = "/graphql";
/**
* Path of the GraphQL WebSocket subscription endpoint.
*/
private String webSocketPath = path + "/websocket";
/**
* For the GraphQL over WebSocket endpoint, this is time within which the
* initial {@code CONNECTION_INIT} type message must be received.
*/
private Duration connectionInitTimeoutDuration = Duration.ofSeconds(60);
private WebSocket websocket = new WebSocket();
public String getPath() {
return path;
return this.path;
}
public void setPath(String path) {
this.path = path;
}
public String getWebSocketPath() {
return webSocketPath;
}
public void setWebSocketPath(String webSocketPath) {
this.webSocketPath = webSocketPath;
}
public String getSchemaLocation() {
return schemaLocation;
return this.schemaLocation;
}
public void setSchemaLocation(String schemaLocation) {
this.schemaLocation = schemaLocation;
}
public Duration getConnectionInitTimeoutDuration() {
return this.connectionInitTimeoutDuration;
public WebSocket getWebsocket() {
return this.websocket;
}
public void setConnectionInitTimeoutDuration(Duration connectionInitTimeoutDuration) {
this.connectionInitTimeoutDuration = connectionInitTimeoutDuration;
static class WebSocket {
/**
* Path of the GraphQL WebSocket subscription endpoint.
*/
private String path;
/**
* Time within which the initial {@code CONNECTION_INIT} type message must be received.
*/
private Duration connectionInitTimeout = Duration.ofSeconds(60);
public String getPath() {
return this.path;
}
public void setPath(String path) {
this.path = path;
}
public Duration getConnectionInitTimeout() {
return this.connectionInitTimeout;
}
public void setConnectionInitTimeout(Duration connectionInitTimeout) {
this.connectionInitTimeout = connectionInitTimeout;
}
}
}

View File

@@ -25,6 +25,7 @@ import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -57,18 +58,6 @@ public class WebFluxGraphQLAutoConfiguration {
return new GraphQLHttpHandler(graphQLBuilder.build(), interceptors.orderedStream().collect(Collectors.toList()));
}
@Bean
@ConditionalOnMissingBean
public GraphQLWebSocketHandler graphQLWebSocketHandler(
GraphQL.Builder graphQLBuilder, GraphQLProperties properties, ServerCodecConfigurer configurer,
ObjectProvider<WebInterceptor> interceptors) {
return new GraphQLWebSocketHandler(
graphQLBuilder.build(), interceptors.orderedStream().collect(Collectors.toList()),
configurer, properties.getConnectionInitTimeoutDuration()
);
}
@Bean
public RouterFunction<ServerResponse> graphQLEndpoint(
GraphQLHttpHandler handler, GraphQLProperties properties, ResourceLoader resourceLoader) {
@@ -82,15 +71,33 @@ public class WebFluxGraphQLAutoConfiguration {
.build();
}
@Bean
public HandlerMapping graphQLWebSocketEndpoint(
GraphQLWebSocketHandler handler, GraphQLProperties properties) {
@ConditionalOnProperty(prefix = "spring.graphql.websocket", name = "path")
static class WebSocketConfiguration {
@Bean
@ConditionalOnMissingBean
public GraphQLWebSocketHandler graphQLWebSocketHandler(
GraphQL.Builder graphQLBuilder, GraphQLProperties properties, ServerCodecConfigurer configurer,
ObjectProvider<WebInterceptor> interceptors) {
return new GraphQLWebSocketHandler(
graphQLBuilder.build(), interceptors.orderedStream().collect(Collectors.toList()),
configurer, properties.getWebsocket().getConnectionInitTimeout()
);
}
@Bean
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;
}
String path = properties.getWebSocketPath();
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setUrlMap(Collections.singletonMap(path, handler));
mapping.setOrder(-1); // Ahead of annotated controllers
return mapping;
}
}

View File

@@ -28,6 +28,7 @@ import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.context.annotation.Bean;
@@ -80,6 +81,7 @@ public class WebMvcGraphQLAutoConfiguration {
@ConditionalOnClass({ServerContainer.class, WebSocketHandler.class})
@ConditionalOnProperty(prefix = "spring.graphql.websocket", name = "path")
static class WebSocketConfiguration {
@Bean
@@ -95,7 +97,7 @@ public class WebMvcGraphQLAutoConfiguration {
return new GraphQLWebSocketHandler(
graphQLBuilder.build(), interceptors.orderedStream().collect(Collectors.toList()),
converter, properties.getConnectionInitTimeoutDuration()
converter, properties.getWebsocket().getConnectionInitTimeout()
);
}
@@ -104,7 +106,7 @@ public class WebMvcGraphQLAutoConfiguration {
WebSocketHttpRequestHandler httpRequestHandler =
new WebSocketHttpRequestHandler(handler, new DefaultHandshakeHandler());
String path = properties.getWebSocketPath();
String path = properties.getWebsocket().getPath();
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setUrlMap(Collections.singletonMap(path, httpRequestHandler));
mapping.setOrder(-1); // Ahead of annotated controllers

View File

@@ -1,3 +1,4 @@
spring.graphql.websocket.path=/graphql/websocket
management.endpoints.web.exposure.include=health,metrics,info
logging.level.org.springframework.web=debug
logging.level.org.springframework.http=debug