parser

Написать ответ на текущее сообщение

 

 
   команды управления поиском

Потоковый вывод: ^response:stream[]

moko 11.10 23:49 / 15.10 02:09

В парсер добавлен метод ^response:stream[строка или файл]. Он позволяет отправлять клиенту ответ по частям, не дожидаясь конца работы скрипта. Например:
$response:content-type[ $.value[text/plain] $.charset[utf-8] ]
^response:stream[первая часть ...]
^sleep(1)
^response:stream[вторая часть ...]
Как это работает:
- Заголовки уходят вместе с первой частью: cookies и всё, что к этому моменту лежит в $response:..., Content-Length не выставляется. Поэтому после первой отправки менять заголовки уже бесполезно.
- После ^response:stream[] результат @main не отправляется, @postprocess не вызывается: отправлено будет только то, что ушло через stream.
- Формат: строки и текстовые файлы переводятся из исходной кодировки в кодировку клиента ($response:charset), как обычный вывод. Бинарный файл уходит как есть.
- Ошибка после начала отправки первой части. Статус уже ушёл, заменить ответ нельзя. Исключение попадёт в лог, в @unhandled_exception можно отправить ошибку клиенту через ^response:stream[].

^response:stream[] позволяет реализовывать SSE (Server-Sent Events) - односторонний канал от сервера к браузеру. Типичные применения: прогресс долгой операции, уведомления. Поддержка EventSource есть во всех современных браузерах (Chrome, Firefox, Safari, Edge) больше десяти лет. SSE это обычный ответ с Content-Type: text/event-stream и последовательностью текстовых событий в формате:
event: имя
id: 5
data: строка 1
data: строка 2
Событие заканчивается пустой строкой. Передавать можно только текст в UTF-8. Для использования в парсере нет необходимости в отдельном классе: достаточно небольшого метода поверх ^response:stream[]. Пример реализации доступен в тесте 467, там меньше страницы кода.

Использование:
^sse[ $.event[json] $.data[^json:string[ $.text[привет] $.login[user] ]] ]
уйдёт клиенту как
event: json
data: {
data: "text":"привет",
data: "login":"user"
data: }
Многострочную data браузер склеит сам, поэтому такой JSON разбирается как обычно.
Обработка на фронте:
const es = new EventSource('/events.html');

// событие с именем json
es.addEventListener('json', (e) => {
const msg = JSON.parse(e.data);
console.log(msg.text, msg.login);
});

// события без поля event
es.onmessage = (e) => console.log(e.data);
es.onerror = () => console.log('соединение потеряно, браузер переподключится сам');
EventSource переподключается автоматически. Пауза между попытками задаётся событием retry: 3000 (миллисекунды). Если у событий есть id, при переподключении браузер отправит его в заголовке Last-Event-ID, и скрипт сможет продолжить с нужного места.

Кроме SSE, через ^response:stream[] реализовать свой потоковый формат или стандартный multipart/x-mixed-replace (в <img> это работает как MJPEG).

  • Потоковый вывод: ^response:stream[], moko [M] 11.10 23:49 / 15.10 02:09