Skip to content

Commit caf0054

Browse files
authored
Reading multibyte characters leads to corrupt FileStore and shows the wrong diagnostic. Using the built-in method String#readNBytes helps to prevent it. (georgewfraser#221)
1 parent 17ddd3c commit caf0054

File tree

3 files changed

+32
-28
lines changed

3 files changed

+32
-28
lines changed

src/main/java/org/javacs/debug/proto/DebugAdapter.java

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -52,21 +52,11 @@ private static char read(InputStream client) {
5252

5353
private static String readLength(InputStream client, int byteLength) {
5454
// Eat whitespace
55-
// Have observed problems with extra \r\n sequences from VSCode
56-
var next = read(client);
57-
while (Character.isWhitespace(next)) {
58-
next = read(client);
59-
}
60-
// Append next
61-
var result = new StringBuilder();
62-
var i = 0;
63-
while (true) {
64-
result.append(next);
65-
i++;
66-
if (i == byteLength) break;
67-
next = read(client);
55+
try {
56+
return new String(client.readNBytes(byteLength), StandardCharsets.UTF_8).stripLeading();
57+
} catch (IOException e) {
58+
throw new RuntimeException("An error occurred during the reading of client data", e);
6859
}
69-
return result.toString();
7060
}
7161

7262
static String nextToken(InputStream client) {

src/main/java/org/javacs/lsp/LSP.java

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -58,21 +58,11 @@ private static char read(InputStream client) {
5858

5959
private static String readLength(InputStream client, int byteLength) {
6060
// Eat whitespace
61-
// Have observed problems with extra \r\n sequences from VSCode
62-
var next = read(client);
63-
while (Character.isWhitespace(next)) {
64-
next = read(client);
65-
}
66-
// Append next
67-
var result = new StringBuilder();
68-
var i = 0;
69-
while (true) {
70-
result.append(next);
71-
i++;
72-
if (i == byteLength) break;
73-
next = read(client);
61+
try {
62+
return new String(client.readNBytes(byteLength), StandardCharsets.UTF_8).stripLeading();
63+
} catch (IOException e) {
64+
throw new RuntimeException("An error occurred during the reading of client data", e);
7465
}
75-
return result.toString();
7666
}
7767

7868
static String nextToken(InputStream client) {

src/test/java/org/javacs/lsp/LspTest.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import static org.hamcrest.Matchers.*;
44
import static org.junit.Assert.*;
55

6+
import com.google.gson.Gson;
67
import com.google.gson.JsonObject;
78
import java.io.IOException;
89
import java.io.PipedInputStream;
@@ -14,6 +15,8 @@
1415
import org.junit.Test;
1516

1617
public class LspTest {
18+
19+
private static final Gson gson = new Gson();
1720
PipedInputStream buffer = new PipedInputStream(10 * 1024 * 1024); // 10 MB buffer
1821
PipedOutputStream writer = new PipedOutputStream();
1922

@@ -93,6 +96,27 @@ public void readMessage() throws IOException {
9396
assertThat(parse.params, equalTo(new JsonObject()));
9497
}
9598

99+
@Test
100+
public void readMessageWithMultiBytesCharacters() throws IOException {
101+
var params = new ShowMessageParams();
102+
params.message = "🔥";
103+
var message =
104+
String.format(
105+
"{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\": %s}", gson.toJson(params));
106+
var header = String.format("Content-Length: %d\r\n\r\n", message.getBytes().length);
107+
writer.write(header.getBytes());
108+
writer.write(message.getBytes());
109+
110+
var token = LSP.nextToken(buffer);
111+
assertThat(token, equalTo(message));
112+
113+
var parse = LSP.parseMessage(token);
114+
assertThat(parse.jsonrpc, equalTo("2.0"));
115+
assertThat(parse.id, equalTo(1));
116+
assertThat(parse.method, equalTo("initialize"));
117+
assertThat(parse.params, equalTo(gson.toJsonTree(params)));
118+
}
119+
96120
@Test
97121
public void excludeDefaults() {
98122
var item = new CompletionItem();

0 commit comments

Comments
 (0)