HelloSun commited on
Commit
aba198f
·
verified ·
1 Parent(s): c1180b1

Keep UTF-8 terminal output intact across packets

Browse files
Files changed (1) hide show
  1. app.py +12 -6
app.py CHANGED
@@ -10,6 +10,7 @@ import termios
10
  import asyncio
11
  import json
12
  import base64
 
13
  from collections import deque
14
  import logging
15
  import uuid
@@ -923,6 +924,7 @@ class TerminalSession:
923
  self.buffer = deque() # 有界輸出回放區,保留序號供重連補播
924
  self.buffer_size = 0
925
  self.output_seq = 0
 
926
  self.subscribers: dict[int, asyncio.Queue] = {}
927
  self._next_sub_id = 0
928
  self.cleanup_task: asyncio.Task | None = None
@@ -952,16 +954,20 @@ class TerminalSession:
952
  await kill_session(self.id, loop, only_if_idle=True)
953
 
954
  def feed(self, data: bytes):
 
 
 
955
  self.output_seq += 1
956
- self.buffer.append((self.output_seq, data))
957
- self.buffer_size += len(data)
 
958
  while self.buffer_size > SESSION_BUFFER_MAX and self.buffer:
959
  _seq, old_data = self.buffer.pop(0)
960
  self.buffer_size -= len(old_data)
961
  dead = []
962
  for sid, q in self.subscribers.items():
963
  try:
964
- q.put_nowait((self.output_seq, data))
965
  except Exception:
966
  dead.append(sid)
967
  for sid in dead:
@@ -1150,12 +1156,12 @@ async def terminal_ws(websocket: WebSocket):
1150
  except Exception:
1151
  pass
1152
  break
1153
- seq, chunk = item
1154
  try:
1155
  await websocket.send_text(
1156
  json.dumps({
1157
  "type": "output", "seq": seq,
1158
- "data": chunk.decode("utf-8", errors="replace"),
1159
  })
1160
  )
1161
  except Exception:
@@ -1173,7 +1179,7 @@ async def terminal_ws(websocket: WebSocket):
1173
  continue
1174
  await websocket.send_text(json.dumps({
1175
  "type": "output", "seq": seq,
1176
- "data": chunk.decode("utf-8", errors="replace"),
1177
  }))
1178
  await websocket.send_text(json.dumps({
1179
  "type": "system",
 
10
  import asyncio
11
  import json
12
  import base64
13
+ import codecs
14
  from collections import deque
15
  import logging
16
  import uuid
 
924
  self.buffer = deque() # 有界輸出回放區,保留序號供重連補播
925
  self.buffer_size = 0
926
  self.output_seq = 0
927
+ self.utf8_decoder = codecs.getincrementaldecoder("utf-8")(errors="replace")
928
  self.subscribers: dict[int, asyncio.Queue] = {}
929
  self._next_sub_id = 0
930
  self.cleanup_task: asyncio.Task | None = None
 
954
  await kill_session(self.id, loop, only_if_idle=True)
955
 
956
  def feed(self, data: bytes):
957
+ text = self.utf8_decoder.decode(data, final=False)
958
+ if not text:
959
+ return
960
  self.output_seq += 1
961
+ encoded = text.encode("utf-8")
962
+ self.buffer.append((self.output_seq, encoded))
963
+ self.buffer_size += len(encoded)
964
  while self.buffer_size > SESSION_BUFFER_MAX and self.buffer:
965
  _seq, old_data = self.buffer.pop(0)
966
  self.buffer_size -= len(old_data)
967
  dead = []
968
  for sid, q in self.subscribers.items():
969
  try:
970
+ q.put_nowait((self.output_seq, text))
971
  except Exception:
972
  dead.append(sid)
973
  for sid in dead:
 
1156
  except Exception:
1157
  pass
1158
  break
1159
+ seq, text = item
1160
  try:
1161
  await websocket.send_text(
1162
  json.dumps({
1163
  "type": "output", "seq": seq,
1164
+ "data": text,
1165
  })
1166
  )
1167
  except Exception:
 
1179
  continue
1180
  await websocket.send_text(json.dumps({
1181
  "type": "output", "seq": seq,
1182
+ "data": chunk.decode("utf-8", errors="strict"),
1183
  }))
1184
  await websocket.send_text(json.dumps({
1185
  "type": "system",