Firefox дублирует большие сообщения websocket, когда серверу требуется время для получения сообщений

#javascript #debugging #firefox #browser #websocket

#javascript #отладка #firefox #браузер #websocket

Вопрос:

Что я пытаюсь сделать

  • У меня есть клиент Javascript (в браузере), который отправляет 10 массивов данных размером 1 МБ (обычно это двоичные данные из файлов, но я постарался максимально упростить) на сервер websocket
  • Клиент отправляет все данные так быстро, как только может, в обычном for цикле
  • Сервер считывает данные из соединения websocket, считывает данные в память, а затем выполняет некоторую обработку этих данных, прежде чем двигаться дальше (в примере go time.Sleep используется для представления «обработки данных», версия Python уже довольно медленная, поэтому я не добавил режим ожидания)

Код для воспроизведения

Клиент

 const ws = new WebSocket('ws://127.0.0.1:8080/');

ws.onopen = async () => {
    for (let i = 0; i < 10; i  ) {
        // Create a 1mb array filled with the value of `i`.
        const data = new Uint8Array(1024 * 1024);
        for (let j = 0; j < 1024 * 1024; j  ) {
            data[j] = i;
        }
        ws.send(data);
    }
};
  

Серверы (любой из них будет воспроизводить проблему)

Python:

 from SimpleWebSocketServer import SimpleWebSocketServer, WebSocket

class SimpleEcho(WebSocket):
    def handleMessage(self):
        print(self.data[0:10])
    def handleConnected(self):
        print(self.address, 'connected')
    def handleClose(self):
        print(self.address, 'closed')

server = SimpleWebSocketServer('127.0.0.1', 8080, SimpleEcho)
server.serveforever()
  

Golang:

 package main

import (
    "fmt"
    "github.com/gorilla/mux"
    "github.com/gorilla/websocket"
    "net/http"
    "time"
)

func websocketHandler(w http.ResponseWriter, r *http.Request) {
    upgrader := websocket.Upgrader{}
    upgrader.CheckOrigin = func(r *http.Request) bool { return true }
    ws, _ := upgrader.Upgrade(w, r, nil)
    defer ws.Close()

    for i := 0; i < 10; i   {
        _, fileBytes, _ := ws.ReadMessage()
        fmt.Println(fileBytes[0:10])
        // Represents taking 1 second to process uploaded data.
        time.Sleep(time.Second * 1)
    }
}

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/", websocketHandler).Methods("GET")
    _ = http.ListenAndServe("127.0.0.1:8080", r)
}

  

Желаемое поведение

All of the data from the javascript client should end up on the go server, with the
server printing out a received array for each number, here’s an example server log which
shows the first 10 bytes of each message received:

 [0 0 0 0 0 0 0 0 0 0]
[1 1 1 1 1 1 1 1 1 1]
[2 2 2 2 2 2 2 2 2 2]
[3 3 3 3 3 3 3 3 3 3]
[4 4 4 4 4 4 4 4 4 4]
[5 5 5 5 5 5 5 5 5 5]
[6 6 6 6 6 6 6 6 6 6]
[7 7 7 7 7 7 7 7 7 7]
[8 8 8 8 8 8 8 8 8 8]
[9 9 9 9 9 9 9 9 9 9]
  

This is exactly what I want, each different array was received only once.


What actually happens

is that some of the data is read by the server, and then the server reads the same data
again from the websocket, instead of new data.

В приведенном ниже журнале сервера вы можете видеть, что он работает нормально, пока массив данных, содержащий «5», не будет получен 4 раза подряд, а затем перейдет к получению «8»:

 Serving at http://127.0.0.1:8080
[0 0 0 0 0 0 0 0 0 0]
[1 1 1 1 1 1 1 1 1 1]
[2 2 2 2 2 2 2 2 2 2]
[3 3 3 3 3 3 3 3 3 3]
[4 4 4 4 4 4 4 4 4 4]
[5 5 5 5 5 5 5 5 5 5]
[5 5 5 5 5 5 5 5 5 5]
[5 5 5 5 5 5 5 5 5 5]
[5 5 5 5 5 5 5 5 5 5]
[8 8 8 8 8 8 8 8 8 8]
  

Это происходит только в том случае, если Javascript запущен в Firefox (версия 82.0 64-разрядная), код работает так, как задумано, если я запускаю его в Chrome.

Я также тестировал в Firefox Developer edition (83.0b4), и происходит та же ошибка.


Странные вещи, которые останавливают проблему

Если time.Sleep вызов закомментирован в коде сервера go, дублирование не происходит.

Версия python по своей сути медленно обрабатывает запросы без режима ожидания (следовательно, нет вызова режима ожидания для представления обрабатываемых данных), поэтому нет способа протестировать на сервере Python, если получение сообщений websocket быстрее останавливает дублирование.

Это наводит меня на мысль, что проблема может быть связана со временем?

Дублирование также не происходит, если в Javascript 2 1024 * 1024 оператора заменяются переменной, которая предварительно вычисляется, например

 const ws = // websocket
const dataSize = 1024 * 1024;

// rest of code up to:

        const data = new Uint8Array(dataSize);
        for (let j = 0; j < dataSize; j  ) {
  

Я понятия не имею, почему это происходит, я начинаю подозревать, что это ошибка
в Firefox.

Любая помощь будет оценена.

Спасибо.

Редактировать:

Похоже, что это определенно ошибка Firefox, и здесь уже есть отчет об этом bugzilla

Комментарии:

1. Возможно, вы захотите использовать инструмент сетевого анализа, такой как Wireshark, чтобы проверить, что на самом деле отправляется через сокет.

2. Я столкнулся с той же проблемой, и она выглядит как ошибка FF82. Я также столкнулся с другой проблемой с большими сообщениями, из-за которой, когда на сервере включено сжатие «для каждого сообщения deflate», FF82 отправляет неработающие / бессмысленные полезные нагрузки. Для записи я использовал WireShark и проверил, что он отправляет дублирующиеся / поврежденные полезные нагрузки. Инспектор сообщений websocket инструментов разработчика говорит одно, а FF82, похоже, делает другое.

3. @Marius хорошо, спасибо, что дали мне знать. Я открыл отчет об ошибке в bugzilla, поэтому, думаю, я буду ждать официального ответа на этот вопрос