Nokia123543 commited on
Commit
fe95a45
·
verified ·
1 Parent(s): 12d1ae2

PLUS: SSE chunked fix

Browse files
Files changed (1) hide show
  1. plus/sse_proxy.py +54 -34
plus/sse_proxy.py CHANGED
@@ -314,14 +314,17 @@ class Proxy(BaseHTTPRequestHandler):
314
  stream = content_type.startswith("text/event-stream")
315
  for key, value in answer.getheaders():
316
  low = key.lower()
317
- if low in HOP_HEADERS or (stream and low == "content-length"):
 
 
318
  continue
319
  self.send_header(key, value)
320
  if stream:
321
- self.send_header("Cache-Control", "no-cache")
322
- self.send_header("X-Accel-Buffering", "no")
323
- self.end_headers()
324
- self.stream_events(answer)
 
325
  else:
326
  data = answer.read()
327
  content_type = (answer.getheader("Content-Type") or "").lower()
@@ -334,12 +337,41 @@ class Proxy(BaseHTTPRequestHandler):
334
  and data[:4] == b"MThd"):
335
  data, stats = apply_velocity(self.last_audio, data)
336
  print("velocity: %s" % stats, flush=True)
337
- self.send_header("Content-Length", str(len(data)))
 
 
 
 
338
  self.end_headers()
339
- if method != "HEAD":
340
  self.wfile.write(data)
341
  upstream.close()
342
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
343
  def refine(self, notes: list[dict], skip_filter: bool = False) -> list[dict]:
344
  """Пересчитать концы нот и артикуляцию по записи."""
345
  if not notes or not self.last_audio:
@@ -416,34 +448,26 @@ class Proxy(BaseHTTPRequestHandler):
416
  threading.Thread(target=worker, daemon=True).start()
417
  self.send_response(200)
418
  self.send_header("Content-Type", "text/event-stream")
419
- self.send_header("Cache-Control", "no-cache")
420
- self.send_header("X-Accel-Buffering", "no")
421
- self.end_headers()
422
  try:
423
- self.wfile.write(b": open\n\n")
424
- self.wfile.flush()
425
  except Exception:
426
  return
427
  payload = None
428
- while True:
429
- try:
430
- payload = outcome.get(timeout=self.ping_seconds)
431
- break
432
- except queue.Empty:
433
- try:
434
- self.wfile.write(b": ping\n\n")
435
- self.wfile.flush()
436
- except Exception:
437
- return
438
- if payload is None:
439
- payload = fusion.to_sse([])
440
  try:
441
- self.wfile.write(payload)
442
- self.wfile.write(b": done\n\n")
443
- self.wfile.flush()
 
 
 
 
 
 
 
444
  except Exception:
445
  pass
446
- self.close_connection = True
 
447
 
448
  def stream_events(self, answer):
449
  """Переливаем SSE, вставляя ": ping" во время молчания модели."""
@@ -462,23 +486,19 @@ class Proxy(BaseHTTPRequestHandler):
462
  lines.put(None)
463
 
464
  threading.Thread(target=pump, daemon=True).start()
465
- self.wfile.write(b": open\n\n")
466
- self.wfile.flush()
467
  while True:
468
  try:
469
  line = lines.get(timeout=PING_SECONDS)
470
  except queue.Empty:
471
  try:
472
- self.wfile.write(b": ping\n\n")
473
- self.wfile.flush()
474
  except Exception:
475
  return
476
  continue
477
  if line is None:
478
  return
479
  try:
480
- self.wfile.write(line)
481
- self.wfile.flush()
482
  except Exception:
483
  return
484
 
 
314
  stream = content_type.startswith("text/event-stream")
315
  for key, value in answer.getheaders():
316
  low = key.lower()
317
+ if low in HOP_HEADERS or low in ("server", "date"):
318
+ continue
319
+ if stream and low in ("cache-control", "x-accel-buffering"):
320
  continue
321
  self.send_header(key, value)
322
  if stream:
323
+ self.begin_stream()
324
+ try:
325
+ self.stream_events(answer)
326
+ finally:
327
+ self.end_stream()
328
  else:
329
  data = answer.read()
330
  content_type = (answer.getheader("Content-Type") or "").lower()
 
337
  and data[:4] == b"MThd"):
338
  data, stats = apply_velocity(self.last_audio, data)
339
  print("velocity: %s" % stats, flush=True)
340
+ if method == "HEAD":
341
+ length = answer.getheader("Content-Length")
342
+ self.send_header("Content-Length", length if length is not None else "0")
343
+ elif answer.status not in (204, 304) and answer.status >= 200:
344
+ self.send_header("Content-Length", str(len(data)))
345
  self.end_headers()
346
+ if method != "HEAD" and answer.status not in (204, 304):
347
  self.wfile.write(data)
348
  upstream.close()
349
 
350
+ def begin_stream(self) -> None:
351
+ """Заголовки SSE: chunked + Connection: close, чтобы туннель (HTTP/2)
352
+ видел конец ответа и не рвал поток с ERR_HTTP2_PROTOCOL_ERROR."""
353
+ self.send_header("Cache-Control", "no-cache, no-transform")
354
+ self.send_header("X-Accel-Buffering", "no")
355
+ self.send_header("Transfer-Encoding", "chunked")
356
+ self.send_header("Connection", "close")
357
+ self.end_headers()
358
+ self.close_connection = True
359
+ self.emit(b": open\n\n")
360
+
361
+ def emit(self, data: bytes) -> None:
362
+ if not data:
363
+ return
364
+ self.wfile.write(b"%x\r\n" % len(data) + data + b"\r\n")
365
+ self.wfile.flush()
366
+
367
+ def end_stream(self) -> None:
368
+ self.close_connection = True
369
+ try:
370
+ self.wfile.write(b"0\r\n\r\n")
371
+ self.wfile.flush()
372
+ except Exception:
373
+ pass
374
+
375
  def refine(self, notes: list[dict], skip_filter: bool = False) -> list[dict]:
376
  """Пересчитать концы нот и артикуляцию по записи."""
377
  if not notes or not self.last_audio:
 
448
  threading.Thread(target=worker, daemon=True).start()
449
  self.send_response(200)
450
  self.send_header("Content-Type", "text/event-stream")
 
 
 
451
  try:
452
+ self.begin_stream()
 
453
  except Exception:
454
  return
455
  payload = None
 
 
 
 
 
 
 
 
 
 
 
 
456
  try:
457
+ while True:
458
+ try:
459
+ payload = outcome.get(timeout=self.ping_seconds)
460
+ break
461
+ except queue.Empty:
462
+ self.emit(b": ping\n\n")
463
+ if payload is None:
464
+ payload = fusion.to_sse([])
465
+ self.emit(payload)
466
+ self.emit(b": done\n\n")
467
  except Exception:
468
  pass
469
+ finally:
470
+ self.end_stream()
471
 
472
  def stream_events(self, answer):
473
  """Переливаем SSE, вставляя ": ping" во время молчания модели."""
 
486
  lines.put(None)
487
 
488
  threading.Thread(target=pump, daemon=True).start()
 
 
489
  while True:
490
  try:
491
  line = lines.get(timeout=PING_SECONDS)
492
  except queue.Empty:
493
  try:
494
+ self.emit(b": ping\n\n")
 
495
  except Exception:
496
  return
497
  continue
498
  if line is None:
499
  return
500
  try:
501
+ self.emit(line)
 
502
  except Exception:
503
  return
504