|
4 | 4 |
|
5 | 5 | package com.github.copilot.sdk; |
6 | 6 |
|
7 | | -import java.io.BufferedReader; |
| 7 | +import java.io.BufferedInputStream; |
8 | 8 | import java.io.IOException; |
9 | 9 | import java.io.InputStream; |
10 | | -import java.io.InputStreamReader; |
11 | 10 | import java.io.OutputStream; |
12 | 11 | import java.net.Socket; |
13 | 12 | import java.nio.charset.StandardCharsets; |
@@ -185,42 +184,61 @@ private synchronized void sendMessage(Object message) throws IOException { |
185 | 184 | private void startReader() { |
186 | 185 | readerExecutor.submit(() -> { |
187 | 186 | try { |
188 | | - BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); |
| 187 | + // We need to read bytes because Content-Length specifies bytes, not characters. |
| 188 | + // Using BufferedReader would cause issues with multi-byte UTF-8 characters. |
| 189 | + BufferedInputStream bis = new BufferedInputStream(inputStream); |
189 | 190 |
|
190 | 191 | while (running) { |
191 | | - String line = reader.readLine(); |
192 | | - if (line == null) { |
193 | | - break; |
194 | | - } |
195 | | - |
196 | | - // Parse headers |
| 192 | + // Read headers line by line |
197 | 193 | int contentLength = -1; |
198 | | - while (!line.isEmpty()) { |
199 | | - if (line.toLowerCase().startsWith("content-length:")) { |
200 | | - contentLength = Integer.parseInt(line.substring(15).trim()); |
201 | | - } |
202 | | - line = reader.readLine(); |
203 | | - if (line == null) { |
| 194 | + StringBuilder headerLine = new StringBuilder(); |
| 195 | + boolean lastWasCR = false; |
| 196 | + boolean inHeaders = true; |
| 197 | + |
| 198 | + while (inHeaders) { |
| 199 | + int b = bis.read(); |
| 200 | + if (b == -1) { |
204 | 201 | return; |
205 | 202 | } |
| 203 | + |
| 204 | + if (b == '\r') { |
| 205 | + lastWasCR = true; |
| 206 | + } else if (b == '\n') { |
| 207 | + String line = headerLine.toString(); |
| 208 | + headerLine.setLength(0); |
| 209 | + lastWasCR = false; |
| 210 | + |
| 211 | + if (line.isEmpty()) { |
| 212 | + // End of headers (blank line) |
| 213 | + inHeaders = false; |
| 214 | + } else if (line.toLowerCase().startsWith("content-length:")) { |
| 215 | + contentLength = Integer.parseInt(line.substring(15).trim()); |
| 216 | + } |
| 217 | + } else { |
| 218 | + if (lastWasCR) { |
| 219 | + headerLine.append('\r'); |
| 220 | + lastWasCR = false; |
| 221 | + } |
| 222 | + headerLine.append((char) b); |
| 223 | + } |
206 | 224 | } |
207 | 225 |
|
208 | 226 | if (contentLength <= 0) { |
209 | 227 | continue; |
210 | 228 | } |
211 | 229 |
|
212 | | - // Read content |
213 | | - char[] buffer = new char[contentLength]; |
| 230 | + // Read content as bytes (Content-Length specifies bytes, not characters) |
| 231 | + byte[] buffer = new byte[contentLength]; |
214 | 232 | int read = 0; |
215 | 233 | while (read < contentLength) { |
216 | | - int result = reader.read(buffer, read, contentLength - read); |
| 234 | + int result = bis.read(buffer, read, contentLength - read); |
217 | 235 | if (result == -1) { |
218 | 236 | return; |
219 | 237 | } |
220 | 238 | read += result; |
221 | 239 | } |
222 | 240 |
|
223 | | - String content = new String(buffer); |
| 241 | + String content = new String(buffer, StandardCharsets.UTF_8); |
224 | 242 | LOG.fine("Received: " + content); |
225 | 243 |
|
226 | 244 | handleMessage(content); |
|
0 commit comments