diff options
Diffstat (limited to 'subprojects')
6 files changed, 99 insertions, 82 deletions
diff --git a/subprojects/frontend/vite.config.ts b/subprojects/frontend/vite.config.ts index 7cea4642..1059ed2f 100644 --- a/subprojects/frontend/vite.config.ts +++ b/subprojects/frontend/vite.config.ts | |||
@@ -161,6 +161,13 @@ export default defineConfig({ | |||
161 | host: listenHost, | 161 | host: listenHost, |
162 | port: listenPort, | 162 | port: listenPort, |
163 | strictPort: true, | 163 | strictPort: true, |
164 | headers: { | ||
165 | // Enable strict origin isolation, see e.g., | ||
166 | // https://github.com/vitejs/vite/issues/3909#issuecomment-1065893956 | ||
167 | 'Cross-Origin-Opener-Policy': 'same-origin', | ||
168 | 'Cross-Origin-Embedder-Policy': 'require-corp', | ||
169 | 'Cross-Origin-Resource-Policy': 'cross-origin', | ||
170 | }, | ||
164 | proxy: { | 171 | proxy: { |
165 | '/xtext-service': { | 172 | '/xtext-service': { |
166 | target: `${apiSecure ? 'https' : 'http'}://${apiHost}:${apiPort}`, | 173 | target: `${apiSecure ? 'https' : 'http'}://${apiHost}:${apiPort}`, |
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/CacheControlFilter.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/CacheControlFilter.java index 8ac8a21e..fbce62c1 100644 --- a/subprojects/language-web/src/main/java/tools/refinery/language/web/CacheControlFilter.java +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/CacheControlFilter.java | |||
@@ -1,21 +1,15 @@ | |||
1 | package tools.refinery.language.web; | 1 | package tools.refinery.language.web; |
2 | 2 | ||
3 | import jakarta.servlet.*; | ||
4 | import jakarta.servlet.http.HttpServletRequest; | ||
5 | import jakarta.servlet.http.HttpServletResponse; | ||
6 | import org.eclipse.jetty.http.HttpHeader; | ||
7 | |||
3 | import java.io.IOException; | 8 | import java.io.IOException; |
4 | import java.time.Duration; | 9 | import java.time.Duration; |
5 | import java.util.Set; | 10 | import java.util.Set; |
6 | import java.util.regex.Pattern; | 11 | import java.util.regex.Pattern; |
7 | 12 | ||
8 | import org.eclipse.jetty.http.HttpHeader; | ||
9 | |||
10 | import jakarta.servlet.Filter; | ||
11 | import jakarta.servlet.FilterChain; | ||
12 | import jakarta.servlet.FilterConfig; | ||
13 | import jakarta.servlet.ServletException; | ||
14 | import jakarta.servlet.ServletRequest; | ||
15 | import jakarta.servlet.ServletResponse; | ||
16 | import jakarta.servlet.http.HttpServletRequest; | ||
17 | import jakarta.servlet.http.HttpServletResponse; | ||
18 | |||
19 | public class CacheControlFilter implements Filter { | 13 | public class CacheControlFilter implements Filter { |
20 | private static final Pattern CACHE_URI_PATTERN = Pattern.compile(".*\\.(css|gif|js|map|png|svg|woff2?)"); | 14 | private static final Pattern CACHE_URI_PATTERN = Pattern.compile(".*\\.(css|gif|js|map|png|svg|woff2?)"); |
21 | 15 | ||
@@ -29,11 +23,6 @@ public class CacheControlFilter implements Filter { | |||
29 | private static final String CACHE_CONTROL_NO_CACHE_VALUE = "no-cache, no-store, max-age: 0, must-revalidate"; | 23 | private static final String CACHE_CONTROL_NO_CACHE_VALUE = "no-cache, no-store, max-age: 0, must-revalidate"; |
30 | 24 | ||
31 | @Override | 25 | @Override |
32 | public void init(FilterConfig filterConfig) throws ServletException { | ||
33 | // Nothing to initialize. | ||
34 | } | ||
35 | |||
36 | @Override | ||
37 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) | 26 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) |
38 | throws IOException, ServletException { | 27 | throws IOException, ServletException { |
39 | if (request instanceof HttpServletRequest httpRequest && response instanceof HttpServletResponse httpResponse) { | 28 | if (request instanceof HttpServletRequest httpRequest && response instanceof HttpServletResponse httpResponse) { |
@@ -49,9 +38,4 @@ public class CacheControlFilter implements Filter { | |||
49 | } | 38 | } |
50 | chain.doFilter(request, response); | 39 | chain.doFilter(request, response); |
51 | } | 40 | } |
52 | |||
53 | @Override | ||
54 | public void destroy() { | ||
55 | // Nothing to dispose. | ||
56 | } | ||
57 | } | 41 | } |
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/SecurityHeadersFilter.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/SecurityHeadersFilter.java new file mode 100644 index 00000000..40dd7ee5 --- /dev/null +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/SecurityHeadersFilter.java | |||
@@ -0,0 +1,32 @@ | |||
1 | package tools.refinery.language.web; | ||
2 | |||
3 | import jakarta.servlet.*; | ||
4 | import jakarta.servlet.http.HttpServletResponse; | ||
5 | |||
6 | import java.io.IOException; | ||
7 | |||
8 | public class SecurityHeadersFilter implements Filter { | ||
9 | @Override | ||
10 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, | ||
11 | ServletException { | ||
12 | if (response instanceof HttpServletResponse httpResponse) { | ||
13 | httpResponse.setHeader("Content-Security-Policy", "default-src 'self'; " + | ||
14 | // CodeMirror needs inline styles, see e.g., | ||
15 | // https://discuss.codemirror.net/t/inline-styles-and-content-security-policy/1311/2 | ||
16 | "style-src 'self' 'unsafe-inline'; " + | ||
17 | // Use 'data:' for displaying inline SVG backgrounds. | ||
18 | "img-src 'self' data:; " + | ||
19 | "object-src 'none'; " + | ||
20 | "base-uri 'none';"); | ||
21 | httpResponse.setHeader("X-Content-Type-Options", "nosniff"); | ||
22 | httpResponse.setHeader("X-Frame-Options", "DENY"); | ||
23 | httpResponse.setHeader("Referrer-Policy", "strict-origin"); | ||
24 | // Enable cross-origin isolation, https://web.dev/cross-origin-isolation-guide/ | ||
25 | httpResponse.setHeader("Cross-Origin-Opener-Policy", "same-origin"); | ||
26 | httpResponse.setHeader("Cross-Origin-Embedder-Policy", "require-corp"); | ||
27 | // We do not expose any sensitive data over HTTP, so <code>cross-origin</code> is safe here. | ||
28 | httpResponse.setHeader("Cross-Origin-Resource-Policy", "cross-origin"); | ||
29 | } | ||
30 | chain.doFilter(request, response); | ||
31 | } | ||
32 | } | ||
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/ServerLauncher.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/ServerLauncher.java index ffd903d0..ffa61321 100644 --- a/subprojects/language-web/src/main/java/tools/refinery/language/web/ServerLauncher.java +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/ServerLauncher.java | |||
@@ -3,15 +3,8 @@ | |||
3 | */ | 3 | */ |
4 | package tools.refinery.language.web; | 4 | package tools.refinery.language.web; |
5 | 5 | ||
6 | import java.io.File; | 6 | import jakarta.servlet.DispatcherType; |
7 | import java.io.IOException; | 7 | import jakarta.servlet.SessionTrackingMode; |
8 | import java.net.InetSocketAddress; | ||
9 | import java.net.URI; | ||
10 | import java.net.URISyntaxException; | ||
11 | import java.util.EnumSet; | ||
12 | import java.util.Optional; | ||
13 | import java.util.Set; | ||
14 | |||
15 | import org.eclipse.jetty.server.Server; | 8 | import org.eclipse.jetty.server.Server; |
16 | import org.eclipse.jetty.server.session.SessionHandler; | 9 | import org.eclipse.jetty.server.session.SessionHandler; |
17 | import org.eclipse.jetty.servlet.DefaultServlet; | 10 | import org.eclipse.jetty.servlet.DefaultServlet; |
@@ -21,11 +14,16 @@ import org.eclipse.jetty.util.resource.Resource; | |||
21 | import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer; | 14 | import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer; |
22 | import org.slf4j.Logger; | 15 | import org.slf4j.Logger; |
23 | import org.slf4j.LoggerFactory; | 16 | import org.slf4j.LoggerFactory; |
24 | |||
25 | import jakarta.servlet.DispatcherType; | ||
26 | import jakarta.servlet.SessionTrackingMode; | ||
27 | import tools.refinery.language.web.xtext.servlet.XtextWebSocketServlet; | 17 | import tools.refinery.language.web.xtext.servlet.XtextWebSocketServlet; |
28 | 18 | ||
19 | import java.io.File; | ||
20 | import java.io.IOException; | ||
21 | import java.net.InetSocketAddress; | ||
22 | import java.net.URI; | ||
23 | import java.net.URISyntaxException; | ||
24 | import java.util.EnumSet; | ||
25 | import java.util.Set; | ||
26 | |||
29 | public class ServerLauncher { | 27 | public class ServerLauncher { |
30 | public static final String DEFAULT_LISTEN_ADDRESS = "localhost"; | 28 | public static final String DEFAULT_LISTEN_ADDRESS = "localhost"; |
31 | 29 | ||
@@ -36,24 +34,25 @@ public class ServerLauncher { | |||
36 | public static final int HTTP_DEFAULT_PORT = 80; | 34 | public static final int HTTP_DEFAULT_PORT = 80; |
37 | 35 | ||
38 | public static final int HTTPS_DEFAULT_PORT = 443; | 36 | public static final int HTTPS_DEFAULT_PORT = 443; |
39 | 37 | ||
40 | public static final String ALLOWED_ORIGINS_SEPARATOR = ";"; | 38 | public static final String ALLOWED_ORIGINS_SEPARATOR = ","; |
41 | 39 | ||
42 | private static final Logger LOG = LoggerFactory.getLogger(ServerLauncher.class); | 40 | private static final Logger LOG = LoggerFactory.getLogger(ServerLauncher.class); |
43 | 41 | ||
44 | private final Server server; | 42 | private final Server server; |
45 | 43 | ||
46 | public ServerLauncher(InetSocketAddress bindAddress, Resource baseResource, Optional<String[]> allowedOrigins) { | 44 | public ServerLauncher(InetSocketAddress bindAddress, Resource baseResource, String[] allowedOrigins) { |
47 | server = new Server(bindAddress); | 45 | server = new Server(bindAddress); |
48 | var handler = new ServletContextHandler(); | 46 | var handler = new ServletContextHandler(); |
49 | addSessionHandler(handler); | 47 | addSessionHandler(handler); |
50 | addProblemServlet(handler, allowedOrigins); | 48 | addProblemServlet(handler, allowedOrigins); |
51 | if (baseResource != null) { | 49 | if (baseResource != null) { |
52 | handler.setBaseResource(baseResource); | 50 | handler.setBaseResource(baseResource); |
53 | handler.setWelcomeFiles(new String[] { "index.html" }); | 51 | handler.setWelcomeFiles(new String[]{"index.html"}); |
54 | addDefaultServlet(handler); | 52 | addDefaultServlet(handler); |
55 | } | 53 | } |
56 | handler.addFilter(CacheControlFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST)); | 54 | handler.addFilter(CacheControlFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST)); |
55 | handler.addFilter(SecurityHeadersFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST)); | ||
57 | server.setHandler(handler); | 56 | server.setHandler(handler); |
58 | } | 57 | } |
59 | 58 | ||
@@ -63,13 +62,13 @@ public class ServerLauncher { | |||
63 | handler.setSessionHandler(sessionHandler); | 62 | handler.setSessionHandler(sessionHandler); |
64 | } | 63 | } |
65 | 64 | ||
66 | private void addProblemServlet(ServletContextHandler handler, Optional<String[]> allowedOrigins) { | 65 | private void addProblemServlet(ServletContextHandler handler, String[] allowedOrigins) { |
67 | var problemServletHolder = new ServletHolder(ProblemWebSocketServlet.class); | 66 | var problemServletHolder = new ServletHolder(ProblemWebSocketServlet.class); |
68 | if (allowedOrigins.isEmpty()) { | 67 | if (allowedOrigins == null) { |
69 | LOG.warn("All WebSocket origins are allowed! This setting should not be used in production!"); | 68 | LOG.warn("All WebSocket origins are allowed! This setting should not be used in production!"); |
70 | } else { | 69 | } else { |
71 | var allowedOriginsString = String.join(XtextWebSocketServlet.ALLOWED_ORIGINS_SEPARATOR, | 70 | var allowedOriginsString = String.join(XtextWebSocketServlet.ALLOWED_ORIGINS_SEPARATOR, |
72 | allowedOrigins.get()); | 71 | allowedOrigins); |
73 | problemServletHolder.setInitParameter(XtextWebSocketServlet.ALLOWED_ORIGINS_INIT_PARAM, | 72 | problemServletHolder.setInitParameter(XtextWebSocketServlet.ALLOWED_ORIGINS_INIT_PARAM, |
74 | allowedOriginsString); | 73 | allowedOriginsString); |
75 | } | 74 | } |
@@ -141,8 +140,8 @@ public class ServerLauncher { | |||
141 | return Resource.newResource(webRootUri); | 140 | return Resource.newResource(webRootUri); |
142 | } | 141 | } |
143 | // Look for unpacked production artifacts (convenience for running from IDE). | 142 | // Look for unpacked production artifacts (convenience for running from IDE). |
144 | var unpackedResourcePathComponents = new String[] { System.getProperty("user.dir"), "build", "webpack", | 143 | var unpackedResourcePathComponents = new String[]{System.getProperty("user.dir"), "build", "webpack", |
145 | "production" }; | 144 | "production"}; |
146 | var unpackedResourceDir = new File(String.join(File.separator, unpackedResourcePathComponents)); | 145 | var unpackedResourceDir = new File(String.join(File.separator, unpackedResourcePathComponents)); |
147 | if (unpackedResourceDir.isDirectory()) { | 146 | if (unpackedResourceDir.isDirectory()) { |
148 | return Resource.newResource(unpackedResourceDir); | 147 | return Resource.newResource(unpackedResourceDir); |
@@ -164,29 +163,31 @@ public class ServerLauncher { | |||
164 | if (portStr != null) { | 163 | if (portStr != null) { |
165 | return Integer.parseInt(portStr); | 164 | return Integer.parseInt(portStr); |
166 | } | 165 | } |
167 | return DEFAULT_LISTEN_PORT; | 166 | return DEFAULT_PUBLIC_PORT; |
168 | } | 167 | } |
169 | 168 | ||
170 | private static Optional<String[]> getAllowedOrigins() { | 169 | private static String[] getAllowedOrigins() { |
171 | var allowedOrigins = System.getenv("ALLOWED_ORIGINS"); | 170 | var allowedOrigins = System.getenv("ALLOWED_ORIGINS"); |
172 | if (allowedOrigins != null) { | 171 | if (allowedOrigins != null) { |
173 | return Optional.of(allowedOrigins.split(ALLOWED_ORIGINS_SEPARATOR)); | 172 | return allowedOrigins.split(ALLOWED_ORIGINS_SEPARATOR); |
174 | } | 173 | } |
175 | return getAllowedOriginsFromPublicHostAndPort(); | 174 | return getAllowedOriginsFromPublicHostAndPort(); |
176 | } | 175 | } |
177 | 176 | ||
178 | private static Optional<String[]> getAllowedOriginsFromPublicHostAndPort() { | 177 | // This method returns <code>null</code> to indicate that all origins are allowed. |
178 | @SuppressWarnings("squid:S1168") | ||
179 | private static String[] getAllowedOriginsFromPublicHostAndPort() { | ||
179 | var publicHost = getPublicHost(); | 180 | var publicHost = getPublicHost(); |
180 | if (publicHost == null) { | 181 | if (publicHost == null) { |
181 | return Optional.empty(); | 182 | return null; |
182 | } | 183 | } |
183 | int publicPort = getPublicPort(); | 184 | int publicPort = getPublicPort(); |
184 | var scheme = publicPort == HTTPS_DEFAULT_PORT ? "https" : "http"; | 185 | var scheme = publicPort == HTTPS_DEFAULT_PORT ? "https" : "http"; |
185 | var urlWithPort = String.format("%s://%s:%d", scheme, publicHost, publicPort); | 186 | var urlWithPort = String.format("%s://%s:%d", scheme, publicHost, publicPort); |
186 | if (publicPort == HTTPS_DEFAULT_PORT || publicPort == HTTP_DEFAULT_PORT) { | 187 | if (publicPort == HTTPS_DEFAULT_PORT || publicPort == HTTP_DEFAULT_PORT) { |
187 | var urlWithoutPort = String.format("%s://%s", scheme, publicHost); | 188 | var urlWithoutPort = String.format("%s://%s", scheme, publicHost); |
188 | return Optional.of(new String[] { urlWithPort, urlWithoutPort }); | 189 | return new String[]{urlWithPort, urlWithoutPort}; |
189 | } | 190 | } |
190 | return Optional.of(new String[] { urlWithPort }); | 191 | return new String[]{urlWithPort}; |
191 | } | 192 | } |
192 | } | 193 | } |
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocketServlet.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocketServlet.java index 942ca380..a2ad2943 100644 --- a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocketServlet.java +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocketServlet.java | |||
@@ -1,28 +1,25 @@ | |||
1 | package tools.refinery.language.web.xtext.servlet; | 1 | package tools.refinery.language.web.xtext.servlet; |
2 | 2 | ||
3 | import java.io.IOException; | 3 | import jakarta.servlet.ServletConfig; |
4 | import java.time.Duration; | 4 | import jakarta.servlet.ServletException; |
5 | import java.util.Set; | 5 | import org.eclipse.jetty.websocket.server.*; |
6 | |||
7 | import org.eclipse.jetty.websocket.server.JettyServerUpgradeRequest; | ||
8 | import org.eclipse.jetty.websocket.server.JettyServerUpgradeResponse; | ||
9 | import org.eclipse.jetty.websocket.server.JettyWebSocketCreator; | ||
10 | import org.eclipse.jetty.websocket.server.JettyWebSocketServlet; | ||
11 | import org.eclipse.jetty.websocket.server.JettyWebSocketServletFactory; | ||
12 | import org.eclipse.xtext.resource.IResourceServiceProvider; | 6 | import org.eclipse.xtext.resource.IResourceServiceProvider; |
13 | import org.slf4j.Logger; | 7 | import org.slf4j.Logger; |
14 | import org.slf4j.LoggerFactory; | 8 | import org.slf4j.LoggerFactory; |
15 | 9 | ||
16 | import jakarta.servlet.ServletConfig; | 10 | import java.io.IOException; |
17 | import jakarta.servlet.ServletException; | 11 | import java.io.Serial; |
12 | import java.time.Duration; | ||
13 | import java.util.Set; | ||
18 | 14 | ||
19 | public abstract class XtextWebSocketServlet extends JettyWebSocketServlet implements JettyWebSocketCreator { | 15 | public abstract class XtextWebSocketServlet extends JettyWebSocketServlet implements JettyWebSocketCreator { |
20 | 16 | @Serial | |
21 | private static final long serialVersionUID = -3772740838165122685L; | 17 | private static final long serialVersionUID = -3772740838165122685L; |
22 | 18 | ||
23 | public static final String ALLOWED_ORIGINS_SEPARATOR = ";"; | 19 | public static final String ALLOWED_ORIGINS_SEPARATOR = ","; |
24 | 20 | ||
25 | public static final String ALLOWED_ORIGINS_INIT_PARAM = "tools.refinery.language.web.xtext.XtextWebSocketServlet.allowedOrigin"; | 21 | public static final String ALLOWED_ORIGINS_INIT_PARAM = |
22 | "tools.refinery.language.web.xtext.XtextWebSocketServlet.allowedOrigin"; | ||
26 | 23 | ||
27 | public static final String XTEXT_SUBPROTOCOL_V1 = "tools.refinery.language.web.xtext.v1"; | 24 | public static final String XTEXT_SUBPROTOCOL_V1 = "tools.refinery.language.web.xtext.v1"; |
28 | 25 | ||
@@ -33,7 +30,7 @@ public abstract class XtextWebSocketServlet extends JettyWebSocketServlet implem | |||
33 | 30 | ||
34 | private static final Duration IDLE_TIMEOUT = Duration.ofSeconds(30); | 31 | private static final Duration IDLE_TIMEOUT = Duration.ofSeconds(30); |
35 | 32 | ||
36 | private transient Logger log = LoggerFactory.getLogger(getClass()); | 33 | private final transient Logger log = LoggerFactory.getLogger(getClass()); |
37 | 34 | ||
38 | private transient Set<String> allowedOrigins = null; | 35 | private transient Set<String> allowedOrigins = null; |
39 | 36 | ||
diff --git a/subprojects/language-web/src/test/java/tools/refinery/language/web/ProblemWebSocketServletIntegrationTest.java b/subprojects/language-web/src/test/java/tools/refinery/language/web/ProblemWebSocketServletIntegrationTest.java index a26ce040..e6b4b940 100644 --- a/subprojects/language-web/src/test/java/tools/refinery/language/web/ProblemWebSocketServletIntegrationTest.java +++ b/subprojects/language-web/src/test/java/tools/refinery/language/web/ProblemWebSocketServletIntegrationTest.java | |||
@@ -1,18 +1,5 @@ | |||
1 | package tools.refinery.language.web; | 1 | package tools.refinery.language.web; |
2 | 2 | ||
3 | import static org.hamcrest.MatcherAssert.assertThat; | ||
4 | import static org.hamcrest.Matchers.equalTo; | ||
5 | import static org.hamcrest.Matchers.hasSize; | ||
6 | import static org.hamcrest.Matchers.instanceOf; | ||
7 | import static org.hamcrest.Matchers.startsWith; | ||
8 | import static org.junit.jupiter.api.Assertions.assertThrows; | ||
9 | |||
10 | import java.io.IOException; | ||
11 | import java.net.InetSocketAddress; | ||
12 | import java.net.URI; | ||
13 | import java.util.concurrent.CompletableFuture; | ||
14 | import java.util.concurrent.CompletionException; | ||
15 | |||
16 | import org.eclipse.jetty.http.HttpHeader; | 3 | import org.eclipse.jetty.http.HttpHeader; |
17 | import org.eclipse.jetty.http.HttpStatus; | 4 | import org.eclipse.jetty.http.HttpStatus; |
18 | import org.eclipse.jetty.server.Server; | 5 | import org.eclipse.jetty.server.Server; |
@@ -32,15 +19,24 @@ import org.junit.jupiter.api.BeforeEach; | |||
32 | import org.junit.jupiter.api.Test; | 19 | import org.junit.jupiter.api.Test; |
33 | import org.junit.jupiter.params.ParameterizedTest; | 20 | import org.junit.jupiter.params.ParameterizedTest; |
34 | import org.junit.jupiter.params.provider.ValueSource; | 21 | import org.junit.jupiter.params.provider.ValueSource; |
35 | |||
36 | import tools.refinery.language.web.tests.WebSocketIntegrationTestClient; | 22 | import tools.refinery.language.web.tests.WebSocketIntegrationTestClient; |
37 | import tools.refinery.language.web.xtext.servlet.XtextStatusCode; | 23 | import tools.refinery.language.web.xtext.servlet.XtextStatusCode; |
38 | import tools.refinery.language.web.xtext.servlet.XtextWebSocketServlet; | 24 | import tools.refinery.language.web.xtext.servlet.XtextWebSocketServlet; |
39 | 25 | ||
26 | import java.io.IOException; | ||
27 | import java.net.InetSocketAddress; | ||
28 | import java.net.URI; | ||
29 | import java.util.concurrent.CompletableFuture; | ||
30 | import java.util.concurrent.CompletionException; | ||
31 | |||
32 | import static org.hamcrest.MatcherAssert.assertThat; | ||
33 | import static org.hamcrest.Matchers.*; | ||
34 | import static org.junit.jupiter.api.Assertions.assertThrows; | ||
35 | |||
40 | class ProblemWebSocketServletIntegrationTest { | 36 | class ProblemWebSocketServletIntegrationTest { |
41 | private static int SERVER_PORT = 28080; | 37 | private static final int SERVER_PORT = 28080; |
42 | 38 | ||
43 | private static String SERVLET_URI = "/xtext-service"; | 39 | private static final String SERVLET_URI = "/xtext-service"; |
44 | 40 | ||
45 | private GlobalStateMemento stateBeforeInjectorCreation; | 41 | private GlobalStateMemento stateBeforeInjectorCreation; |
46 | 42 | ||
@@ -151,7 +147,7 @@ class ProblemWebSocketServletIntegrationTest { | |||
151 | @ParameterizedTest(name = "Origin: {0}") | 147 | @ParameterizedTest(name = "Origin: {0}") |
152 | @ValueSource(strings = { "https://refinery.example", "https://refinery.example:443", "HTTPS://REFINERY.EXAMPLE" }) | 148 | @ValueSource(strings = { "https://refinery.example", "https://refinery.example:443", "HTTPS://REFINERY.EXAMPLE" }) |
153 | void validOriginTest(String origin) { | 149 | void validOriginTest(String origin) { |
154 | startServer("https://refinery.example;https://refinery.example:443"); | 150 | startServer("https://refinery.example,https://refinery.example:443"); |
155 | var clientSocket = new CloseImmediatelyTestClient(); | 151 | var clientSocket = new CloseImmediatelyTestClient(); |
156 | connect(clientSocket, origin, XtextWebSocketServlet.XTEXT_SUBPROTOCOL_V1); | 152 | connect(clientSocket, origin, XtextWebSocketServlet.XTEXT_SUBPROTOCOL_V1); |
157 | clientSocket.waitForTestResult(); | 153 | clientSocket.waitForTestResult(); |
@@ -160,7 +156,7 @@ class ProblemWebSocketServletIntegrationTest { | |||
160 | 156 | ||
161 | @Test | 157 | @Test |
162 | void invalidOriginTest() { | 158 | void invalidOriginTest() { |
163 | startServer("https://refinery.example;https://refinery.example:443"); | 159 | startServer("https://refinery.example,https://refinery.example:443"); |
164 | var clientSocket = new CloseImmediatelyTestClient(); | 160 | var clientSocket = new CloseImmediatelyTestClient(); |
165 | var exception = assertThrows(CompletionException.class, | 161 | var exception = assertThrows(CompletionException.class, |
166 | () -> connect(clientSocket, "https://invalid.example", XtextWebSocketServlet.XTEXT_SUBPROTOCOL_V1)); | 162 | () -> connect(clientSocket, "https://invalid.example", XtextWebSocketServlet.XTEXT_SUBPROTOCOL_V1)); |