diff options
author | 2021-10-11 21:05:43 +0200 | |
---|---|---|
committer | 2021-10-31 19:26:10 +0100 | |
commit | cd7067ef419780f7dcd4195da3d3dc869bb4544e (patch) | |
tree | bf235e97e523e478052050dc879f1f8afbdb3e8a /language-web/src/main/java/tools | |
parent | feat(web): add websocket server (diff) | |
download | refinery-cd7067ef419780f7dcd4195da3d3dc869bb4544e.tar.gz refinery-cd7067ef419780f7dcd4195da3d3dc869bb4544e.tar.zst refinery-cd7067ef419780f7dcd4195da3d3dc869bb4544e.zip |
feat(web): better websocket logging
Diffstat (limited to 'language-web/src/main/java/tools')
3 files changed, 54 insertions, 28 deletions
diff --git a/language-web/src/main/java/tools/refinery/language/web/ServerLauncher.java b/language-web/src/main/java/tools/refinery/language/web/ServerLauncher.java index 0942b680..f6311070 100644 --- a/language-web/src/main/java/tools/refinery/language/web/ServerLauncher.java +++ b/language-web/src/main/java/tools/refinery/language/web/ServerLauncher.java | |||
@@ -35,6 +35,8 @@ public class ServerLauncher { | |||
35 | public static final int HTTP_DEFAULT_PORT = 80; | 35 | public static final int HTTP_DEFAULT_PORT = 80; |
36 | 36 | ||
37 | public static final int HTTPS_DEFAULT_PORT = 443; | 37 | public static final int HTTPS_DEFAULT_PORT = 443; |
38 | |||
39 | public static final String ALLOWED_ORIGINS_SEPARATOR = ";"; | ||
38 | 40 | ||
39 | private static final Logger LOG = LoggerFactory.getLogger(ServerLauncher.class); | 41 | private static final Logger LOG = LoggerFactory.getLogger(ServerLauncher.class); |
40 | 42 | ||
@@ -166,7 +168,7 @@ public class ServerLauncher { | |||
166 | private static Optional<String[]> getAllowedOrigins() { | 168 | private static Optional<String[]> getAllowedOrigins() { |
167 | var allowedOrigins = System.getenv("ALLOWED_ORIGINS"); | 169 | var allowedOrigins = System.getenv("ALLOWED_ORIGINS"); |
168 | if (allowedOrigins != null) { | 170 | if (allowedOrigins != null) { |
169 | return Optional.of(allowedOrigins.split(XtextWebSocketServlet.ALLOWED_ORIGINS_SEPARATOR)); | 171 | return Optional.of(allowedOrigins.split(ALLOWED_ORIGINS_SEPARATOR)); |
170 | } | 172 | } |
171 | return getAllowedOriginsFromPublicHostAndPort(); | 173 | return getAllowedOriginsFromPublicHostAndPort(); |
172 | } | 174 | } |
diff --git a/language-web/src/main/java/tools/refinery/language/web/xtext/XtextWebSocket.java b/language-web/src/main/java/tools/refinery/language/web/xtext/XtextWebSocket.java index c76ef12f..4ad98b6e 100644 --- a/language-web/src/main/java/tools/refinery/language/web/xtext/XtextWebSocket.java +++ b/language-web/src/main/java/tools/refinery/language/web/xtext/XtextWebSocket.java | |||
@@ -7,10 +7,14 @@ import org.eclipse.emf.common.util.URI; | |||
7 | import org.eclipse.jetty.websocket.api.Session; | 7 | import org.eclipse.jetty.websocket.api.Session; |
8 | import org.eclipse.jetty.websocket.api.StatusCode; | 8 | import org.eclipse.jetty.websocket.api.StatusCode; |
9 | import org.eclipse.jetty.websocket.api.WriteCallback; | 9 | import org.eclipse.jetty.websocket.api.WriteCallback; |
10 | import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; | ||
11 | import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; | ||
12 | import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError; | ||
10 | import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; | 13 | import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; |
11 | import org.eclipse.jetty.websocket.api.annotations.WebSocket; | 14 | import org.eclipse.jetty.websocket.api.annotations.WebSocket; |
12 | import org.eclipse.xtext.resource.IResourceServiceProvider; | 15 | import org.eclipse.xtext.resource.IResourceServiceProvider; |
13 | import org.eclipse.xtext.web.server.IServiceContext; | 16 | import org.eclipse.xtext.web.server.IServiceContext; |
17 | import org.eclipse.xtext.web.server.IServiceResult; | ||
14 | import org.eclipse.xtext.web.server.ISession; | 18 | import org.eclipse.xtext.web.server.ISession; |
15 | import org.eclipse.xtext.web.server.IUnwrappableServiceResult; | 19 | import org.eclipse.xtext.web.server.IUnwrappableServiceResult; |
16 | import org.eclipse.xtext.web.server.InvalidRequestException; | 20 | import org.eclipse.xtext.web.server.InvalidRequestException; |
@@ -26,7 +30,7 @@ import com.google.gson.JsonParseException; | |||
26 | import com.google.inject.Injector; | 30 | import com.google.inject.Injector; |
27 | 31 | ||
28 | @WebSocket | 32 | @WebSocket |
29 | public class XtextWebSocket implements WriteCallback { | 33 | public class XtextWebSocket { |
30 | private static final Logger LOG = LoggerFactory.getLogger(XtextWebSocket.class); | 34 | private static final Logger LOG = LoggerFactory.getLogger(XtextWebSocket.class); |
31 | 35 | ||
32 | private final Gson gson = new Gson(); | 36 | private final Gson gson = new Gson(); |
@@ -40,61 +44,82 @@ public class XtextWebSocket implements WriteCallback { | |||
40 | this.resourceServiceProviderRegistry = resourceServiceProviderRegistry; | 44 | this.resourceServiceProviderRegistry = resourceServiceProviderRegistry; |
41 | } | 45 | } |
42 | 46 | ||
47 | @OnWebSocketConnect | ||
48 | public void onConnect(Session webSocketSession) { | ||
49 | LOG.debug("New websocket connection from {}", webSocketSession.getRemoteAddress()); | ||
50 | } | ||
51 | |||
52 | @OnWebSocketClose | ||
53 | public void onClose(Session webSocketSession, int statusCode, String reason) { | ||
54 | if (statusCode == StatusCode.NORMAL) { | ||
55 | LOG.debug("{} closed connection normally: {}", webSocketSession.getRemoteAddress(), reason); | ||
56 | } else { | ||
57 | LOG.warn("{} closed connection with status code {}: {}", webSocketSession.getRemoteAddress(), statusCode, | ||
58 | reason); | ||
59 | } | ||
60 | } | ||
61 | |||
62 | @OnWebSocketError | ||
63 | public void onError(Session webSocketSession, Throwable error) { | ||
64 | LOG.error("Internal websocket error in connection from" + webSocketSession.getRemoteAddress(), error); | ||
65 | } | ||
66 | |||
43 | @OnWebSocketMessage | 67 | @OnWebSocketMessage |
44 | public void onMessage(Session webSocketSession, Reader reader) { | 68 | public void onMessage(Session webSocketSession, Reader reader) { |
45 | XtextWebSocketRequest request; | 69 | XtextWebSocketRequest request; |
46 | try { | 70 | try { |
47 | request = gson.fromJson(reader, XtextWebSocketRequest.class); | 71 | request = gson.fromJson(reader, XtextWebSocketRequest.class); |
48 | } catch (JsonIOException e) { | 72 | } catch (JsonIOException e) { |
49 | LOG.error("Cannot read from websocket " + webSocketSession.getRemoteAddress(), e); | 73 | LOG.error("Cannot read from websocket from" + webSocketSession.getRemoteAddress(), e); |
50 | if (webSocketSession.isOpen()) { | 74 | if (webSocketSession.isOpen()) { |
51 | webSocketSession.close(StatusCode.SERVER_ERROR, "Cannot read payload"); | 75 | webSocketSession.close(StatusCode.SERVER_ERROR, "Cannot read payload"); |
52 | } | 76 | } |
53 | return; | 77 | return; |
54 | } catch (JsonParseException e) { | 78 | } catch (JsonParseException e) { |
55 | LOG.warn("Malformed websocket request from " + webSocketSession.getRemoteAddress(), e); | 79 | LOG.warn("Malformed websocket request from" + webSocketSession.getRemoteAddress(), e); |
56 | webSocketSession.close(XtextStatusCode.INVALID_JSON, "Invalid JSON payload"); | 80 | webSocketSession.close(XtextStatusCode.INVALID_JSON, "Invalid JSON payload"); |
57 | return; | 81 | return; |
58 | } | 82 | } |
59 | var serviceContext = new SimpleServiceContext(session, request.getRequestData()); | 83 | var serviceContext = new SimpleServiceContext(session, request.getRequestData()); |
60 | var response = handleMessage(serviceContext); | 84 | var response = handleMessage(webSocketSession, serviceContext); |
61 | response.setId(request.getId()); | 85 | response.setId(request.getId()); |
62 | var responseString = gson.toJson(response); | 86 | var responseString = gson.toJson(response); |
63 | try { | 87 | try { |
64 | webSocketSession.getRemote().sendPartialString(responseString, true, this); | 88 | webSocketSession.getRemote().sendPartialString(responseString, true, new WriteCallback() { |
89 | @Override | ||
90 | public void writeFailed(Throwable x) { | ||
91 | LOG.warn("Cannot complete async write to websocket " + webSocketSession.getRemoteAddress(), x); | ||
92 | } | ||
93 | }); | ||
65 | } catch (IOException e) { | 94 | } catch (IOException e) { |
66 | LOG.warn("Cannot initiaite async write to websocket to " + webSocketSession.getRemoteAddress(), e); | 95 | LOG.warn("Cannot initiaite async write to websocket " + webSocketSession.getRemoteAddress(), e); |
67 | if (webSocketSession.isOpen()) { | 96 | if (webSocketSession.isOpen()) { |
68 | webSocketSession.close(StatusCode.SERVER_ERROR, "Cannot write payload"); | 97 | webSocketSession.close(StatusCode.SERVER_ERROR, "Cannot write payload"); |
69 | } | 98 | } |
70 | } | 99 | } |
71 | } | 100 | } |
72 | 101 | ||
73 | @Override | 102 | protected XtextWebSocketResponse handleMessage(Session webSocketSession, IServiceContext serviceContext) { |
74 | public void writeFailed(Throwable x) { | 103 | IServiceResult serviceResult; |
75 | LOG.warn("Cannot complete async write to websocket", x); | ||
76 | } | ||
77 | |||
78 | protected XtextWebSocketResponse handleMessage(IServiceContext serviceContext) { | ||
79 | try { | 104 | try { |
80 | var injector = getInjector(serviceContext); | 105 | var injector = getInjector(serviceContext); |
81 | var serviceDispatcher = injector.getInstance(XtextServiceDispatcher.class); | 106 | var serviceDispatcher = injector.getInstance(XtextServiceDispatcher.class); |
82 | var service = serviceDispatcher.getService(serviceContext); | 107 | var service = serviceDispatcher.getService(serviceContext); |
83 | var serviceResult = service.getService().apply(); | 108 | serviceResult = service.getService().apply(); |
84 | var response = new XtextWebSocketOkResponse(); | ||
85 | if (serviceResult instanceof IUnwrappableServiceResult unwrappableServiceResult | ||
86 | && unwrappableServiceResult.getContent() != null) { | ||
87 | response.setResponseData(unwrappableServiceResult.getContent()); | ||
88 | } else { | ||
89 | response.setResponseData(serviceResult); | ||
90 | } | ||
91 | return response; | ||
92 | } catch (InvalidRequestException e) { | 109 | } catch (InvalidRequestException e) { |
93 | LOG.warn("Invalid request", e); | 110 | LOG.warn("Invalid request from websocket " + webSocketSession.getRemoteAddress(), e); |
94 | var error = new XtextWebSocketErrorResponse(); | 111 | var error = new XtextWebSocketErrorResponse(); |
95 | error.setErrorMessage(e.getMessage()); | 112 | error.setErrorMessage(e.getMessage()); |
96 | return error; | 113 | return error; |
97 | } | 114 | } |
115 | var response = new XtextWebSocketOkResponse(); | ||
116 | if (serviceResult instanceof IUnwrappableServiceResult unwrappableServiceResult | ||
117 | && unwrappableServiceResult.getContent() != null) { | ||
118 | response.setResponseData(unwrappableServiceResult.getContent()); | ||
119 | } else { | ||
120 | response.setResponseData(serviceResult); | ||
121 | } | ||
122 | return response; | ||
98 | } | 123 | } |
99 | 124 | ||
100 | /** | 125 | /** |
diff --git a/language-web/src/main/java/tools/refinery/language/web/xtext/XtextWebSocketServlet.java b/language-web/src/main/java/tools/refinery/language/web/xtext/XtextWebSocketServlet.java index 0de6c358..2db11325 100644 --- a/language-web/src/main/java/tools/refinery/language/web/xtext/XtextWebSocketServlet.java +++ b/language-web/src/main/java/tools/refinery/language/web/xtext/XtextWebSocketServlet.java | |||
@@ -38,11 +38,11 @@ public abstract class XtextWebSocketServlet extends JettyWebSocketServlet implem | |||
38 | @Override | 38 | @Override |
39 | public void init(ServletConfig config) throws ServletException { | 39 | public void init(ServletConfig config) throws ServletException { |
40 | var allowedOriginsStr = config.getInitParameter(ALLOWED_ORIGINS_INIT_PARAM); | 40 | var allowedOriginsStr = config.getInitParameter(ALLOWED_ORIGINS_INIT_PARAM); |
41 | if (allowedOriginsStr != null) { | 41 | if (allowedOriginsStr == null) { |
42 | log.warn("All WebSocket origins are allowed! This setting should not be used in production!"); | ||
43 | } else { | ||
42 | allowedOrigins = Set.of(allowedOriginsStr.split(ALLOWED_ORIGINS_SEPARATOR)); | 44 | allowedOrigins = Set.of(allowedOriginsStr.split(ALLOWED_ORIGINS_SEPARATOR)); |
43 | log.info("Allowed origins: {}", allowedOrigins); | 45 | log.info("Allowed origins: {}", allowedOrigins); |
44 | } else { | ||
45 | log.warn("All WebSocket origins are allowed! This setting should not be used in production!"); | ||
46 | } | 46 | } |
47 | super.init(config); | 47 | super.init(config); |
48 | } | 48 | } |
@@ -58,7 +58,7 @@ public abstract class XtextWebSocketServlet extends JettyWebSocketServlet implem | |||
58 | public Object createWebSocket(JettyServerUpgradeRequest req, JettyServerUpgradeResponse resp) { | 58 | public Object createWebSocket(JettyServerUpgradeRequest req, JettyServerUpgradeResponse resp) { |
59 | if (allowedOrigins != null) { | 59 | if (allowedOrigins != null) { |
60 | var origin = req.getOrigin(); | 60 | var origin = req.getOrigin(); |
61 | if (origin != null && !allowedOrigins.contains(origin.toLowerCase())) { | 61 | if (origin == null || !allowedOrigins.contains(origin.toLowerCase())) { |
62 | log.error("Connection from {} from forbidden origin {}", req.getRemoteSocketAddress(), origin); | 62 | log.error("Connection from {} from forbidden origin {}", req.getRemoteSocketAddress(), origin); |
63 | try { | 63 | try { |
64 | resp.sendForbidden("Origin not allowed"); | 64 | resp.sendForbidden("Origin not allowed"); |
@@ -68,7 +68,6 @@ public abstract class XtextWebSocketServlet extends JettyWebSocketServlet implem | |||
68 | return null; | 68 | return null; |
69 | } | 69 | } |
70 | } | 70 | } |
71 | log.debug("New connection from {}", req.getRemoteSocketAddress()); | ||
72 | var session = new SimpleSession(); | 71 | var session = new SimpleSession(); |
73 | return new XtextWebSocket(session, IResourceServiceProvider.Registry.INSTANCE); | 72 | return new XtextWebSocket(session, IResourceServiceProvider.Registry.INSTANCE); |
74 | } | 73 | } |