Как проверить, что соединение XMLHttpRequest было установлено до поступления данных - PullRequest
4 голосов
/ 10 октября 2019

Я реализую некоторые серверные события с EventSource, и мне нужно знать, когда установлено соединение EventSource и сервер ответил начальным 200 OK, тогда я могу начать выполнять некоторые другие запросы, которые в конечном итоге приводят к сообщениямотправлено через EventSource с сервера.

Я на самом деле использую этот полифилл https://github.com/AlexGalays/EventSource, который внутренне использует XMLHttpRequest.

Проблема: Когда сервер отправляет 200 OK+ заголовки, onreadystatechange не запускается (xhr.readyState по-прежнему 1). Это общая проблема, связанная с любым XHR, а не только с EventSource.

Пример PHP-сервера:

<?php
sleep(5); // our actual implementation does some non-trivial startup here
// send 200 OK + headers, but no data
flush();

sleep(5);
echo "bye";

Пример - клиент:

<script>
    xhr = new XMLHttpRequest();
    xhr.onreadystatechange = () => console.log(`readystate = ${xhr.readyState}`);
    xhr.open('GET', 'http://localhost/longpoll.php');
    xhr.send();
</script>

Ожидаемое поведение:

  • readystate = 1 (открыто)
  • 5 секунд задержки
  • readystate = 2(полученные заголовки)
  • 5 секундная задержка
  • readystate = 3 (загрузка)
  • readystate = 4 (готово)

Actualповедение:

  • readystate = 1 (открыто)
  • 10 секунд задержки
  • readystate = 2 (заголовки получены)
  • readystate =3 (загрузка)
  • readystate = 4 (готово)

Протестировано в последних версиях Chrome (77) и Firefox (69), оба ведут себя одинаково.

КогдаЯ наблюдаю за подключением на вкладке Инструменты Chrome Dev Сеть , на самом деле я вижу заголовки ответов и код состояния после первых 5 секунд задержки (см. https://youtu.be/sIgQnbfwxjM). Это означает, что браузер действительно получает заголовки и обновления cсостояние подключения через первые 5 секунд, но JavaScript не подтверждается.

Можно ли как-то обойти это? Или это какое-то ограничение безопасности, которое не позволяет мне получать обновленное состояние соединения на этом этапе?

1 Ответ

0 голосов
/ 21 октября 2019

По умолчанию Chrome и другие браузеры буферизируют весь ответ (заголовки и тело) XMLHttpRequest. Поэтому нет простого способа сначала проверить заголовки, а затем извлечь тело, используя XMLHttpRequest. Единственное решение - обрабатывать ответ как поток / чанки, а не как один ответ. В Firefox было "moz-chunked-arraybuffer" для xhr.responseType, но было удалено . Google Chrome реализован в какой-то момент xhr.responseType "stream" ( как экспериментальный ), но он был отброшен в пользу whatwg Streams . На сегодняшний день самый простой fetch API в большинстве современных браузеров дает нам возможность получать заголовки ответа без тела (если они уже отправлены сервером). Протестировано в Chrome 77:

let response = await fetch('http://localhost:9615/');

if (response.ok) {
  console.log('status code:', response.status);
  let text = await response.text();
  console.log('text', text);
} else {
  console.error('HTTP-Error:', response.status);
}

Вы также можете использовать прямо упомянутые потоки whatwg ( ReadableStream ), как здесь:

fetch("http://localhost:9615/").then((response) => {
  const reader = response.body.getReader();
  const stream = new ReadableStream({
    start(controller) {
      console.log('status code:', response.status);
      function push() {
        reader.read().then(({ done, value }) => {
          console.log('read', value);
          if (done) {
            console.log('done');
            controller.close();
            return;
          }

          controller.enqueue(value);
          push();
        });
      };

      push();
    }
  });

  return new Response(stream, { headers: { "Content-Type": "text/html" } });
});

В качестве тестового сервера я использовалкод ниже (NodeJS):

const http = require('http');

http.createServer(function (req, res) {
    setTimeout(() => {
      res.writeHead(200, {'Content-Type': 'text/plain'});
      res.flushHeaders();
      setTimeout(() => {
        res.write("some content");
        res.end();
      }, 3000);
    }, 3000);
}).listen(9615);

В обоих решениях (Fetch API и ReadableStream) вы получите status code: 200 после первых 3000 мсек, а остальные ответы после следующих 3000 мс.

К сожалению, Потоки еще широко не поддерживаются браузерами, но здесь - это полифилл. Смотри также this .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...