Der Bericht, wenn er denn kommt, besagt, dass die Seite nicht mehr aktualisiert wurde. Es erschien keine Fehlermeldung. Die Zahlen bewegten sich einfach nicht mehr, und ein Neuladen behob das Problem.

Das ist auch der Grund, warum man davon selten erfährt. Die Person, die damit konfrontiert wird, hat innerhalb von Sekunden eine funktionierende Reparatur in den Händen, also teilen es dir die meisten nie mit. Die, die es dir berichten, können nur eine Abwesenheit beschreiben.

Eine geschlossene Verbindung ist kein Fehler

Das erste Ding, das man verlernen muss: Dies kommt nicht wie ein fehlgeschlagener Request an. Das wird es nicht.

Ein WebSocket feuert zwar ein error Event, und MDN beschreibt seinen Typ in drei Worten: “Ein generisches Event.” Es gibt keine benutzerdefinierte Schnittstelle darunter, das heisst: kein Code, kein Grund und kein Status auf dem Objekt, das dir übergeben wird. Nichts wirft eine Exception, also erreicht nichts einen try/catch. Die Verbindung endet, und das einzige, was Information trägt, ist das close Event, das danach folgt.

Der Close-Code ist also kein Detail. Er ist die ganze Nachricht.

Die Codes, die etwas bedeuten

Vier sind es wert, auswendig zu lernen, in MDNs eigenen Worten.

  • 1000, normale Beendigung: “Die Verbindung hat den Zweck, für den sie geschaffen wurde, erfolgreich erfüllt.” Nichts zum Untersuchen.
  • 1001, geht weg: “Der Endpunkt entfernt sich entweder wegen eines Serverfehlers oder weil der Browser die Seite verlässt, die die Verbindung öffnete.”
  • 1011, interner Fehler: “Der Server beendet die Verbindung, weil er einen unerwarteten Zustand antraf, der ihn an der Erfüllung des Requests hinderte.”
  • 1006, abnormale Beendigung, was derjenige ist, den du tatsächlich sehen wirst und der der Rest dieses Artikels behandelt.

Lies 1001 zweimal, denn es deckt zwei nicht zusammenhängende Ereignisse mit einer Nummer ab. Ein Server, der herunterfährt, und ein Benutzer, der auf einen Link klickt, produzieren beide. Wenn du 1001s als Vorfälle zählst, zählst du Menschen, die deine Seite verlassen, als Vorfälle, und die Zahl wird deinem Traffic folgen, nicht deiner Zuverlässigkeit.

1011 ist das Gegenteil, und es ist derjenige, auf den man alarmieren sollte. Es ist der Server, der den Fehler in seinen eigenen Worten eingesteht.

Der Code, den du am meisten siehst, kann von niemandem gekommen sein

Hier ist der Teil, der fast nie aufgeschrieben wird, und er verändert, wie du untersuchst.

RFC 6455 definiert 1006 wie folgt:

1006 ist ein reservierter Wert und DARF NICHT als Statuscode in einem Close-Frame von einem Endpunkt gesetzt werden. Es ist für die Verwendung in Anwendungen vorgesehen, die einen Statuscode erwarten, um anzugeben, dass die Verbindung abnormal beendet wurde, z.B. ohne ein Close-Frame zu senden oder zu empfangen.

1005 trägt dasselbe Verbot: “1005 ist ein reservierter Wert und DARF NICHT als Statuscode in einem Close-Frame von einem Endpunkt gesetzt werden.”

Also können keiner von beiden gesendet werden. Nicht von deinem Server, nicht vom Browser, nicht von irgendetwas dazwischen. Sie sind auf dem Draht verboten. Wenn dein Handler 1006 liest, liest er eine Nummer, die dein eigener Browser lokal erfunden hat, um die Tatsache zu beschreiben, dass die Verbindung endete und kein Close-Frame je ankam.

Das ist eine stärkere Aussage, als sie aussieht. Es bedeutet, dass es kein entsprechendes Ereignis irgendwo anders gibt, das man gehen und finden kann.

Was Menschen suchen
Eine Server-Log-Zeile, die die 1006 erklärt, unter der Annahme, dass der Server sie gesendet hat
Was existiert
Nichts. Der Code wurde im Browser generiert. Der Server weiß möglicherweise nicht einmal, dass die Verbindung weg ist, und das, was sie tötete, ist oft nicht einmal der Server

1006 ist daher keine Ursache. Es ist die Form des Beweises: Etwas zwischen den beiden Enden hörte auf weiterzuleiten, und keiner der Enden bekam die Chance sich zu verabschieden.

Wer schliesst tatsächlich eine Verbindung, die niemand geschlossen hat

Sobald du akzeptierst, dass der Browser Stille meldet, sind die Verdächtigen die Dinge, die in der Mitte sitzen und Meinungen über untätige Verbindungen haben.

Ein Reverse Proxy oder Load Balancer mit einem Idle-Timeout ist der häufigste. Diese Timeouts sind konfigurierbar und oft kurz, und ein WebSocket, das zwischen Updates nichts trägt, sieht genau wie eine aufgegebene Verbindung aus. Unternehmens-Netzwerk-Equipment macht dasselbe, aggressiver. Ein Telefon, das von WiFi zu Mobilfunk wechselt, ändert die Adresse des Clients unter einem offenen Socket, was ihn ohne Zeremonie beendet. Laptops, die in den Standby-Modus gehen, machen dasselbe.

Beachte, was jedes davon gemeinsam hat: Dein Anwendungscode ist korrekt, dein Server ist gesund, und mit den Daten ist alles in Ordnung. Der Transport ist weg. Das ist auch der Grund, warum es manche Benutzer ständig trifft und andere nie, was die Signatur ist, die einen Bug-Report mit “kann nicht reproduziert werden” zurück markiert.

Die Standard-Verteidigung ist ein Heartbeat: Sende etwas Kleines auf einem Timer, öfter als das kürzeste Idle-Timeout zwischen dir und deinen Benutzern. Es behebt keine abgebrochene Verbindung. Es verhindert, dass eine untätige als eine tote verwechselt wird, was ein anderes Problem ist, das denselben Report produziert.

Was zu erfassen ist, denn das Close-Event ist der einzige Zeuge

Das Close-Event findet einmal im Browser statt, und dann verbindet die Seite sich normalerweise neu und der Beweis ist weg. Vier Felder sind es wert, in diesem Moment erfasst zu werden.

socket.addEventListener('close', (event) => {
  report({
    code: event.code,                      // 1006 bedeutet, niemand hat einen Code gesendet
    wasClean: event.wasClean,              // true wenn die Verbindung sauber geschlossen wurde
    reason: event.reason,                  // normalerweise leer, und leer ist informativ
    secondsSinceLastMessage: (Date.now() - lastMessageAt) / 1000,
    reconnects: reconnectCount
  });
});

wasClean ist das billigste Signal der vier: MDN definiert es als true “wenn die Verbindung sauber geschlossen wurde”, also trennt false eine Verbindung, die endete, von einer, die beendet wurde. Und secondsSinceLastMessage ist das Feld, das einen Idle-Timeout erfasst, denn das Intervall wird sich um denselben Wert für jeden betroffenen Benutzer clustern. Ein Cluster knapp unter sechzig Sekunden ist kein Zufall, es ist eine konfigurierte Zahl irgendwo auf dem Weg.

Session Replay

Kostenlose Chrome-Erweiterung. Ein Klick auf die Seite, die sich nicht richtig verhält, erfasst den Screenshot, die Konsole und das Netzwerk-Log und gibt dir einen Link zum Einfügen ins Ticket.

Erweiterung installieren

Die Frage, die man derjenigen stellen sollte, die es gemeldet hat

Eine Frage, und es ist keine technische: Hat ein Neuladen das Problem behoben?

Wenn ja, waren die Daten nie falsch und dein Rendering war nie falsch. Eine frische Verbindung war alles, was es brauchte, was auf den Transport und weg von allem anderen hindeutet. Diese eine Antwort beseitigt mehr als jedes Raten über ihr Netzwerk, und sie können sie geben, ohne zu wissen, was ein WebSocket ist.

Die Nachfrage, die es wert ist, gestellt zu werden, ist, wie lange die Seite offen war. Ein Tab, der über Mittag offen gelassen wurde, und ein Tab, der vor einer Minute geöffnet wurde, sind unterschiedliche Fälle, und der Reporter kennt die Antwort.

Was man davon mitnehmen sollte

Der Fehler ist still von Natur aus. Nichts wirft eine Exception, das error Event trägt kein Detail, und die Seite rendert weiterhin das Letzte, das ihr mitgeteilt wurde.

Der Close-Code ist die ganze Nachricht, und der häufigste ist eine lokale Erfindung: 1006 ist dein Browser, der sagt, dass nichts ankam, nicht dein Server, der sagt, warum er ging. Codes, die niemand senden kann, sind Codes, die niemand erklären kann, also höre auf, die Server-Seite zu suchen, und fang an, nach dem zu schauen, das zwischen dir und dem Benutzer sitzt.

Die Spezifikation selbst ist RFC 6455, und die Close-Codes mit ihren einzeiligen Bedeutungen sind auf MDNs CloseEvent.code Seite.