Files
lang/doc/ws-dpi-solution.md
T

6.3 KiB
Raw Blame History

Обход DPI РКН для передачи аудио на Groq Whisper

Дата: 2026-05-22 Итог: WebSocket — тупик. Рабочее решение — fetch POST с multipart/form-data.


Результаты тестов DPI (Россия -> Германия, lang.kube5s.ru)

Метод Размер Результат
HTTP POST с JSON-телом 40KB JSON
HTTP POST с JSON-телом 133KB JSON
HTTP POST с JSON-телом 200KB JSON ⚠️ погранично
HTTP POST multipart/form-data 30-150KB blob все
WebSocket text-фрейм <=45KB JSON
WebSocket text-фрейм >45KB JSON
WebSocket чанки по 14KB + задержки любой
WebSocket binary-фрейм любой

Вывод: DPI целенаправленно блокирует WebSocket-трафик. Обычный HTTPS POST проходит до ~130KB JSON или 150KB multipart.


Рабочее решение (v62+)

Архитектура

Браузер (РФ)                  lang.kube5s.ru (Германия)        api.groq.com
    |                                |                              |
    |  POST /openai/v1/audio/        |                              |
    |  transcriptions                |                              |
    |  Content-Type: multipart/      |                              |
    |  form-data                     |                              |
    |  Authorization: Bearer KEY     |                              |
    |  file=<blob.webm>              |                              |
    |  model=whisper-large-v3        |                              |
    |  language=it ---------------->|                              |
    |                                |  прокси пробрасывает ------>|
    |                                |                         Whisper
    |                                |  <------- JSON {text} -----|
    |  <------- JSON {text} --------|                              |

Ключевые моменты

  1. Никаких WebSocket — DPI их палит
  2. Никакого base64 — Blob идёт как есть в multipart
  3. Никакого JSON для аудио — бинарные данные в FormData
  4. Сервер НЕ менялся — универсальный Flask-прокси форвардит всё в Groq

Клиент (index.html v62)

async function transcribe(blob) {
  const form = new FormData();
  form.append('file', blob, 'audio.webm');
  form.append('model', 'whisper-large-v3');
  form.append('language', 'it');

  const r = await fetch(
    'https://lang.kube5s.ru/openai/v1/audio/transcriptions',
    { method: 'POST',
      headers: { 'Authorization': 'Bearer ' + GROQ_API_KEY },
      body: form }
  );
  const data = await r.json();
  return { text: data.text || '', error: data.error ? data.error : null };
}

Сервер (proxy.py) — без изменений

Универсальный прокси: любой метод, любой путь -> api.groq.com. Пробрасывает Authorization и Content-Type, стримит ответ.

Nginx (groq-proxy.conf)

location / {
    proxy_pass http://127.0.0.1:8765;
    proxy_read_timeout 60s;
    proxy_send_timeout 60s;
    proxy_request_buffering off;
}

Тупиковые подходы

WebSocket + base64 (v45-v61)

Идея: кодировать аудио в base64, отправлять текстовыми WS-фреймами.

Почему провалилось:

  • Одиночный фрейм до ~45KB JSON проходил, больше — нет
  • Чанки по 14-30KB с задержками — DPI обрывал соединение на ~3-4 фрейме
  • Задержки между фреймами (150-200мс) не помогали
  • Результат хаотичный: 3 фрейма ОК -> 4 таймаут -> 5 ОК -> 8 таймаут
  • DPI детектит WebSocket-паттерн и режет независимо от содержимого

Фонетический анализ: таймаут decodeAudioData

decodeAudioData в Chrome может навсегда зависнуть на WebM/Opus >2с (баг Chrome, не DPI).

const phonTimeout = new Promise(resolve =>
  setTimeout(() => resolve({ pitchHz:0, pitchStability:0, voiceClarity:0, phonScore:0 }), 15000)
);
[whisperResult, phon] = await Promise.all([
  transcribe(blob),
  Promise.race([phoneticAnalysis(blob), phonTimeout])
]);

При таймауте — фонетика показывает нули, транскрипция работает.


Файлы и деплой

Сервер (Vultr 95.179.252.111):
  /opt/groq-proxy/proxy.py        — HTTP прокси (Flask/gunicorn :8765) ОСНОВНОЙ
  /opt/groq-proxy/ws_server.py    — WS сервер (:8766) БОЛЬШЕ НЕ ИСПОЛЬЗУЕТСЯ
  /etc/nginx/conf.d/groq-proxy.conf

Клиент (K8s lang.kube5s.ru):
  ~/lang/index.html               — SPA
  ~/lang/deploy_lang.sh           — деплой

Команды:
  ssh -i ~/.ssh/vultr_openssh root@95.179.252.111
  cd ~/lang && bash deploy_lang.sh
  systemctl restart groq-http

Хронология версий

Версия Подход Результат
v34-v44 WAV->WebM, HTTP chunked
v45-v47 WS binary, чанки base64
v48 WS один JSON base64 (~30KB) короткие
v49-v53 Фиксы WS race condition откачены
v54-v59 Чанки base64 в WS
v60-v61 Чанки 14KB + задержки
v62 fetch POST multipart/form-data РАБОТАЕТ

Тестовые скрипты (в репозитории)

Файл Что тестирует
test_http.py HTTP POST JSON разных размеров
test_multipart.py HTTP POST multipart/form-data
test_chunks.py WS чанки с задержками
test_threshold.py Порог DPI для кол-ва WS фреймов
test_hello.py Минимальный WS: connect + 1 сообщение
test_single.py Один большой WS фрейм