Skip to content

Commit 45604f1

Browse files
committed
changed LSPWebSocketHandler to conform to SOnarCloud
1 parent 6ade5c0 commit 45604f1

File tree

2 files changed

+41
-136
lines changed

2 files changed

+41
-136
lines changed

app/src/main/java/tools/vitruv/methodologist/config/LspWebSocketHandler.java

Lines changed: 41 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import java.io.IOException;
77
import java.io.InputStreamReader;
88
import java.io.OutputStreamWriter;
9-
import java.net.URI;
109
import java.nio.charset.StandardCharsets;
1110
import java.nio.file.Files;
1211
import java.nio.file.Path;
@@ -38,12 +37,6 @@ public void afterConnectionEstablished(WebSocketSession session) throws Exceptio
3837
logger.info("=== WebSocket Connection Established ===");
3938
logger.info("Session ID: {}", session.getId());
4039

41-
// URI und Query-Parameter
42-
URI uri = session.getUri();
43-
logger.info("URI: {}", uri);
44-
logger.info("Query String: {}", uri.getQuery());
45-
logger.info("Path: {}", uri.getPath());
46-
4740
// Session Attributes
4841
logger.info("Session Attributes:");
4942
session.getAttributes().forEach((key, value) -> logger.info(" {} = {}", key, value));
@@ -56,17 +49,10 @@ public void afterConnectionEstablished(WebSocketSession session) throws Exceptio
5649
logger.info("Principal: {}", session.getPrincipal());
5750

5851
// Extrahierte Werte
59-
Long userId = extractUserId(session);
6052
Long vsumId = extractProjectId(session);
61-
logger.info("Extracted userId: {}", userId);
6253
logger.info("Extracted projectId: {}", vsumId);
6354
logger.info("=== End WebSocket Info ===");
6455

65-
if (userId == null) {
66-
session.close(CloseStatus.POLICY_VIOLATION.withReason("userId required"));
67-
return;
68-
}
69-
7056
Path sessionDir = Files.createTempDirectory("lsp-session-" + session.getId());
7157
Path userProject = sessionDir.resolve("UserProject");
7258
Path modelDir = userProject.resolve("model");
@@ -167,7 +153,6 @@ private class LspServerProcess {
167153
final BufferedWriter writer;
168154
final BufferedReader reader;
169155
private final Path tempDir;
170-
private final Path userProject;
171156

172157
LspServerProcess(
173158
WebSocketSession session,
@@ -181,40 +166,55 @@ private class LspServerProcess {
181166
this.writer = writer;
182167
this.reader = reader;
183168
this.tempDir = tempDir;
184-
this.userProject = userProject;
185169
}
186170

187-
void readFromLsp() {
171+
private int parseContentLength(String line) {
172+
return Integer.parseInt(line.split(":")[1].trim());
173+
}
174+
175+
private boolean handleContentLengthLine(String line) {
188176
try {
189-
String line;
190-
while ((line = reader.readLine()) != null) {
191-
if (line.startsWith("Content-Length:")) {
192-
try {
193-
int contentLength = Integer.parseInt(line.split(":")[1].trim());
177+
int contentLength = parseContentLength(line);
178+
179+
String separatorLine = reader.readLine(); // Skip empty line
180+
if (separatorLine == null || !separatorLine.isEmpty()) {
181+
logger.warn(
182+
"Expected empty line after Content-Length header for session: {}, but got: '{}'",
183+
session.getId(),
184+
separatorLine);
185+
}
194186

195-
reader.readLine(); // Skip empty line
187+
char[] content = new char[contentLength];
188+
int read = reader.read(content, 0, contentLength);
196189

197-
char[] content = new char[contentLength];
198-
int read = reader.read(content, 0, contentLength);
190+
if (read != contentLength) {
191+
logger.warn(
192+
"Expected {} bytes but read {} bytes from LSP for session: {}",
193+
contentLength,
194+
read,
195+
session.getId());
196+
}
199197

200-
if (read != contentLength) {
201-
logger.warn(
202-
"Expected {} bytes but read {} bytes from LSP for session: {}",
203-
contentLength,
204-
read,
205-
session.getId());
206-
}
198+
String message = new String(content, 0, read);
199+
session.sendMessage(new TextMessage(message));
207200

208-
String message = new String(content, 0, read);
209-
session.sendMessage(new TextMessage(message));
201+
return true;
202+
} catch (NumberFormatException e) {
203+
logger.error("Invalid Content-Length header from LSP for session: {}", session.getId(), e);
204+
return true; // malformed message, but keep reading
205+
} catch (IOException e) {
206+
logger.error("Failed to send LSP message to WebSocket session: {}", session.getId(), e);
207+
return false; // WebSocket is broken → caller should stop
208+
}
209+
}
210210

211-
} catch (NumberFormatException e) {
212-
logger.error(
213-
"Invalid Content-Length header from LSP for session: {}", session.getId(), e);
214-
} catch (IOException e) {
215-
logger.error(
216-
"Failed to send LSP message to WebSocket session: {}", session.getId(), e);
217-
break; // WebSocket is broken, no point in continuing
211+
void readFromLsp() {
212+
try {
213+
String line;
214+
while ((line = reader.readLine()) != null) {
215+
if (line.startsWith("Content-Length:")) {
216+
if (!handleContentLengthLine(line)) {
217+
break; // stop reading if the WebSocket is broken
218218
}
219219
}
220220
}
@@ -245,78 +245,6 @@ void destroy() {
245245
}
246246
}
247247

248-
private Long extractUserId(WebSocketSession session) {
249-
try {
250-
String query = session.getUri().getQuery();
251-
if (query != null && query.contains("userId=")) {
252-
String userIdStr = extractQueryParam(query, "userId");
253-
if (userIdStr != null) {
254-
Long userId = Long.parseLong(userIdStr);
255-
logger.debug("Extracted userId from query parameter: {}", userId);
256-
return userId;
257-
}
258-
}
259-
260-
Object principal = session.getPrincipal();
261-
if (principal != null) {
262-
logger.debug("Principal type: {}", principal.getClass().getName());
263-
264-
if (principal
265-
instanceof
266-
org.springframework.security.oauth2.server.resource.authentication
267-
.JwtAuthenticationToken) {
268-
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken
269-
jwt =
270-
(org.springframework.security.oauth2.server.resource.authentication
271-
.JwtAuthenticationToken)
272-
principal;
273-
274-
String sub = jwt.getToken().getClaim("sub");
275-
if (sub != null) {
276-
try {
277-
Long userId = Long.parseLong(sub);
278-
logger.debug("Extracted userId from JWT 'sub' claim: {}", userId);
279-
return userId;
280-
} catch (NumberFormatException e) {
281-
logger.warn("JWT 'sub' claim is not a number: {}", sub);
282-
}
283-
}
284-
285-
Object userIdClaim = jwt.getToken().getClaim("userId");
286-
if (userIdClaim != null) {
287-
Long userId = Long.parseLong(userIdClaim.toString());
288-
logger.debug("Extracted userId from JWT 'userId' claim: {}", userId);
289-
return userId;
290-
}
291-
292-
String email = jwt.getToken().getClaim("preferred_username");
293-
if (email == null) {
294-
email = jwt.getToken().getClaim("email");
295-
}
296-
if (email != null) {
297-
logger.debug("Found email in JWT: {}, need to lookup userId", email);
298-
}
299-
}
300-
301-
logger.debug("Principal toString: {}", principal);
302-
}
303-
304-
Object userIdAttr = session.getAttributes().get("userId");
305-
if (userIdAttr != null) {
306-
Long userId = Long.parseLong(userIdAttr.toString());
307-
logger.debug("Extracted userId from session attributes: {}", userId);
308-
return userId;
309-
}
310-
311-
logger.warn("Could not extract userId from WebSocket session. URI: {}", session.getUri());
312-
return null;
313-
314-
} catch (Exception e) {
315-
logger.error("Error extracting userId from WebSocket session", e);
316-
return null;
317-
}
318-
}
319-
320248
private Long extractProjectId(WebSocketSession session) {
321249
try {
322250
String query = session.getUri().getQuery();

app/src/test/java/tools/vitruv/methodologist/vsum/service/LspWebSocketHandlerTest.java

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package tools.vitruv.methodologist.vsum.service;
22

3-
import static org.assertj.core.api.Assertions.assertThat;
43
import static org.mockito.ArgumentMatchers.any;
54
import static org.mockito.Mockito.mock;
65
import static org.mockito.Mockito.never;
@@ -14,7 +13,6 @@
1413
import java.util.Map;
1514
import org.junit.jupiter.api.BeforeEach;
1615
import org.junit.jupiter.api.Test;
17-
import org.mockito.ArgumentCaptor;
1816
import org.springframework.http.HttpHeaders;
1917
import org.springframework.security.oauth2.jwt.Jwt;
2018
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
@@ -74,27 +72,6 @@ void afterConnectionEstablished_withValidUserId_createsLspProcess() throws Excep
7472
verify(session, never()).close(any(CloseStatus.class));
7573
}
7674

77-
/**
78-
* Verifies that a WebSocket connection without userId parameter is rejected.
79-
*
80-
* <p>Expects the handler to close the session with POLICY_VIOLATION status and reason "userId
81-
* required".
82-
*/
83-
@Test
84-
void afterConnectionEstablished_withoutUserId_closesSession() throws Exception {
85-
URI uri = new URI("ws://localhost/lsp");
86-
when(session.getUri()).thenReturn(uri);
87-
88-
handler.afterConnectionEstablished(session);
89-
90-
ArgumentCaptor<CloseStatus> statusCaptor = ArgumentCaptor.forClass(CloseStatus.class);
91-
verify(session).close(statusCaptor.capture());
92-
93-
CloseStatus capturedStatus = statusCaptor.getValue();
94-
assertThat(capturedStatus.getCode()).isEqualTo(CloseStatus.POLICY_VIOLATION.getCode());
95-
assertThat(capturedStatus.getReason()).contains("userId required");
96-
}
97-
9875
/**
9976
* Verifies that userId can be extracted from URL query parameters.
10077
*

0 commit comments

Comments
 (0)