Skip to content

Commit 8207a05

Browse files
Use UTF-8 to parse the debug protocol inputStream (microsoft#89)
Signed-off-by: Jinbo Wang <[email protected]>
1 parent 96812c9 commit 8207a05

File tree

5 files changed

+78
-36
lines changed

5 files changed

+78
-36
lines changed

org/eclipse/jdt/ls/debug/adapter/DebugAdapterContext.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
package org.eclipse.jdt.ls.debug.adapter;
1313

14+
import java.nio.charset.Charset;
1415
import java.util.Collections;
1516
import java.util.Map;
1617

@@ -31,6 +32,7 @@ public class DebugAdapterContext implements IDebugAdapterContext {
3132
private boolean clientPathsAreUri = false;
3233
private boolean isAttached = false;
3334
private String[] sourcePaths;
35+
private Charset debuggeeEncoding;
3436

3537
private IdCollection<String> sourceReferences = new IdCollection<>();
3638
private RecyclableObjectPool<Long, Object> recyclableIdPool = new RecyclableObjectPool<>();
@@ -157,4 +159,14 @@ public void setVariableFormatter(IVariableFormatter variableFormatter) {
157159
public Map<String, String> getSourceLookupCache() {
158160
return this.sourceMappingCache;
159161
}
162+
163+
@Override
164+
public void setDebuggeeEncoding(Charset encoding) {
165+
this.debuggeeEncoding = encoding;
166+
}
167+
168+
@Override
169+
public Charset getDebuggeeEncoding() {
170+
return this.debuggeeEncoding;
171+
}
160172
}

org/eclipse/jdt/ls/debug/adapter/IDebugAdapterContext.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
package org.eclipse.jdt.ls.debug.adapter;
1313

14+
import java.nio.charset.Charset;
1415
import java.util.Map;
1516

1617
import org.eclipse.jdt.ls.debug.IDebugSession;
@@ -87,4 +88,7 @@ public interface IDebugAdapterContext {
8788

8889
Map<String, String> getSourceLookupCache();
8990

91+
void setDebuggeeEncoding(Charset encoding);
92+
93+
Charset getDebuggeeEncoding();
9094
}

org/eclipse/jdt/ls/debug/adapter/ProcessConsole.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import java.io.IOException;
1616
import java.io.InputStream;
1717
import java.io.InputStreamReader;
18+
import java.nio.charset.Charset;
1819
import java.nio.charset.StandardCharsets;
1920

2021
import io.reactivex.functions.Consumer;
@@ -23,18 +24,29 @@
2324
public class ProcessConsole {
2425
private Process process;
2526
private String name;
27+
private Charset encoding;
2628
private PublishSubject<String> stdoutSubject = PublishSubject.<String>create();
2729
private PublishSubject<String> stderrSubject = PublishSubject.<String>create();
2830
private Thread stdoutThread = null;
2931
private Thread stderrThread = null;
3032

3133
public ProcessConsole(Process process) {
32-
this(process, "Process");
34+
this(process, "Process", StandardCharsets.UTF_8);
3335
}
3436

35-
public ProcessConsole(Process process, String name) {
37+
/**
38+
* Constructor.
39+
* @param process
40+
* the process
41+
* @param name
42+
* the process name
43+
* @param encoding
44+
* the process encoding format
45+
*/
46+
public ProcessConsole(Process process, String name, Charset encoding) {
3647
this.process = process;
3748
this.name = name;
49+
this.encoding = encoding;
3850
}
3951

4052
/**
@@ -82,7 +94,7 @@ public void onStderr(Consumer<String> callback) {
8294
}
8395

8496
private void monitor(InputStream input, PublishSubject<String> subject) {
85-
BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
97+
BufferedReader reader = new BufferedReader(new InputStreamReader(input, encoding));
8698
final int BUFFERSIZE = 4096;
8799
char[] buffer = new char[BUFFERSIZE];
88100
while (true) {

org/eclipse/jdt/ls/debug/adapter/ProtocolServer.java

Lines changed: 41 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.io.PrintWriter;
2222
import java.io.Reader;
2323
import java.io.Writer;
24+
import java.nio.charset.Charset;
2425
import java.nio.charset.StandardCharsets;
2526
import java.util.concurrent.ConcurrentLinkedQueue;
2627
import java.util.concurrent.atomic.AtomicInteger;
@@ -33,13 +34,14 @@ public class ProtocolServer {
3334
private static final int BUFFER_SIZE = 4096;
3435
private static final String TWO_CRLF = "\r\n\r\n";
3536
private static final Pattern CONTENT_LENGTH_MATCHER = Pattern.compile("Content-Length: (\\d+)");
37+
private static final Charset PROTOCOL_ENCODING = StandardCharsets.UTF_8; // vscode protocol uses UTF-8 as encoding format.
3638

3739
private Reader reader;
3840
private Writer writer;
3941

40-
private CharBuffer rawData;
42+
private ByteBuffer rawData;
4143
private boolean terminateSession = false;
42-
private int bodyLength = -1;
44+
private int contentLength = -1;
4345
private AtomicInteger sequenceNumber = new AtomicInteger(1);
4446

4547
private boolean isDispatchingData;
@@ -57,10 +59,10 @@ public class ProtocolServer {
5759
* provider context for a series of provider implementation
5860
*/
5961
public ProtocolServer(InputStream input, OutputStream output, IProviderContext context) {
60-
this.reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
61-
this.writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(output, StandardCharsets.UTF_8)));
62-
this.bodyLength = -1;
63-
this.rawData = new CharBuffer();
62+
this.reader = new BufferedReader(new InputStreamReader(input, PROTOCOL_ENCODING));
63+
this.writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(output, PROTOCOL_ENCODING)));
64+
this.contentLength = -1;
65+
this.rawData = new ByteBuffer();
6466
this.eventQueue = new ConcurrentLinkedQueue<>();
6567
this.debugAdapter = new DebugAdapter((debugEvent, willSendLater) -> {
6668
// If the protocolServer has been stopped, it'll no longer receive any event.
@@ -86,7 +88,7 @@ public void start() {
8688
break;
8789
}
8890

89-
this.rawData.append(buffer, read);
91+
this.rawData.append(new String(buffer, 0, read).getBytes(PROTOCOL_ENCODING));
9092
this.processData();
9193
}
9294
} catch (IOException e) {
@@ -134,16 +136,16 @@ private void sendMessage(Messages.ProtocolMessage message) {
134136
message.seq = this.sequenceNumber.getAndIncrement();
135137

136138
String jsonMessage = JsonUtils.toJson(message);
137-
byte[] jsonBytes = jsonMessage.getBytes(StandardCharsets.UTF_8);
139+
byte[] jsonBytes = jsonMessage.getBytes(PROTOCOL_ENCODING);
138140

139141
String header = String.format("Content-Length: %d%s", jsonBytes.length, TWO_CRLF);
140-
byte[] headerBytes = header.getBytes(StandardCharsets.UTF_8);
142+
byte[] headerBytes = header.getBytes(PROTOCOL_ENCODING);
141143

142144
byte[] data = new byte[headerBytes.length + jsonBytes.length];
143145
System.arraycopy(headerBytes, 0, data, 0, headerBytes.length);
144146
System.arraycopy(jsonBytes, 0, data, headerBytes.length, jsonBytes.length);
145147

146-
String utf8Data = new String(data, StandardCharsets.UTF_8);
148+
String utf8Data = new String(data, PROTOCOL_ENCODING);
147149

148150
try {
149151
Logger.logInfo("\n[[RESPONSE]]\n" + new String(data));
@@ -156,21 +158,25 @@ private void sendMessage(Messages.ProtocolMessage message) {
156158

157159
private void processData() {
158160
while (true) {
159-
if (this.bodyLength >= 0) {
160-
if (this.rawData.length() >= this.bodyLength) {
161-
char[] buf = this.rawData.removeFirst(this.bodyLength);
162-
this.bodyLength = -1;
163-
dispatchRequest(new String(buf));
161+
/**
162+
* In vscode debug protocol, the content length represents the message's byte length with utf8 format.
163+
*/
164+
if (this.contentLength >= 0) {
165+
if (this.rawData.length() >= this.contentLength) {
166+
byte[] buf = this.rawData.removeFirst(this.contentLength);
167+
this.contentLength = -1;
168+
dispatchRequest(new String(buf, PROTOCOL_ENCODING));
164169
continue;
165170
}
166171
} else {
167-
String body = this.rawData.getString();
168-
int idx = body.indexOf(TWO_CRLF);
172+
String rawMessage = this.rawData.getString(PROTOCOL_ENCODING);
173+
int idx = rawMessage.indexOf(TWO_CRLF);
169174
if (idx != -1) {
170-
Matcher matcher = CONTENT_LENGTH_MATCHER.matcher(body);
175+
Matcher matcher = CONTENT_LENGTH_MATCHER.matcher(rawMessage);
171176
if (matcher.find()) {
172-
this.bodyLength = Integer.parseInt(matcher.group(1));
173-
this.rawData.removeFirst(idx + TWO_CRLF.length());
177+
this.contentLength = Integer.parseInt(matcher.group(1));
178+
int headerByteLength = rawMessage.substring(0, idx + TWO_CRLF.length()).getBytes(PROTOCOL_ENCODING).length;
179+
this.rawData.removeFirst(headerByteLength); // Remove the header from the raw message.
174180
continue;
175181
}
176182
}
@@ -209,32 +215,36 @@ private void dispatchRequest(String request) {
209215
}
210216
}
211217

212-
class CharBuffer {
213-
private char[] buffer;
218+
class ByteBuffer {
219+
private byte[] buffer;
214220

215-
public CharBuffer() {
216-
this.buffer = new char[0];
221+
public ByteBuffer() {
222+
this.buffer = new byte[0];
217223
}
218224

219225
public int length() {
220226
return this.buffer.length;
221227
}
222228

223-
public String getString() {
224-
return new String(this.buffer);
229+
public String getString(Charset cs) {
230+
return new String(this.buffer, cs);
225231
}
226232

227-
public void append(char[] b, int length) {
228-
char[] newBuffer = new char[this.buffer.length + length];
233+
public void append(byte[] b) {
234+
append(b, b.length);
235+
}
236+
237+
public void append(byte[] b, int length) {
238+
byte[] newBuffer = new byte[this.buffer.length + length];
229239
System.arraycopy(buffer, 0, newBuffer, 0, this.buffer.length);
230240
System.arraycopy(b, 0, newBuffer, this.buffer.length, length);
231241
this.buffer = newBuffer;
232242
}
233243

234-
public char[] removeFirst(int n) {
235-
char[] b = new char[n];
244+
public byte[] removeFirst(int n) {
245+
byte[] b = new byte[n];
236246
System.arraycopy(this.buffer, 0, b, 0, n);
237-
char[] newBuffer = new char[this.buffer.length - n];
247+
byte[] newBuffer = new byte[this.buffer.length - n];
238248
System.arraycopy(this.buffer, n, newBuffer, 0, this.buffer.length - n);
239249
this.buffer = newBuffer;
240250
return b;

org/eclipse/jdt/ls/debug/adapter/handler/LaunchRequestHandler.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
package org.eclipse.jdt.ls.debug.adapter.handler;
1313

1414
import java.io.IOException;
15+
import java.nio.charset.StandardCharsets;
1516
import java.util.Arrays;
1617
import java.util.List;
1718
import java.util.Map;
@@ -56,6 +57,9 @@ public void handle(Command command, Arguments arguments, Response response, IDeb
5657

5758
context.setAttached(false);
5859
context.setSourcePaths(launchArguments.sourcePaths);
60+
// TODO Currently the debuggee console just supports UTF-8 format.
61+
// In future, we could let user to specify the debuggee encoding in launch.json.
62+
context.setDebuggeeEncoding(StandardCharsets.UTF_8);
5963

6064
IVirtualMachineManagerProvider vmProvider = context.getProvider(IVirtualMachineManagerProvider.class);
6165
ISourceLookUpProvider sourceProvider = context.getProvider(ISourceLookUpProvider.class);
@@ -71,9 +75,9 @@ public void handle(Command command, Arguments arguments, Response response, IDeb
7175
IDebugSession debugSession = DebugUtility.launch(vmProvider.getVirtualMachineManager(),
7276
launchArguments.mainClass, launchArguments.args, launchArguments.vmArgs, Arrays.asList(launchArguments.classPaths));
7377
context.setDebugSession(debugSession);
74-
7578
Logger.logInfo("Launching debuggee VM succeeded.");
76-
ProcessConsole debuggeeConsole = new ProcessConsole(debugSession.process(), "Debuggee");
79+
80+
ProcessConsole debuggeeConsole = new ProcessConsole(debugSession.process(), "Debuggee", context.getDebuggeeEncoding());
7781
debuggeeConsole.onStdout((output) -> {
7882
// When DA receives a new OutputEvent, it just shows that on Debug Console and doesn't affect the DA's dispatching workflow.
7983
// That means the debugger can send OutputEvent to DA at any time.

0 commit comments

Comments
 (0)