
Il rapporto, quando finalmente arriva, è che la pagina ha smesso di aggiornarsi. Non è comparso alcun errore. I numeri semplicemente non si muovevano più, e un aggiornamento l’ha risolto.
Questo è il motivo per cui raramente ne senti parlare. La persona che lo incontra ha una soluzione che funziona tra le mani in pochi secondi, quindi la maggior parte di loro non te lo dice mai. Quelli che te lo dicono non hanno nulla da descrivere se non un’assenza.
Una socket chiusa non è un errore
La prima cosa da imparare è che questo non arriverà come una richiesta fallita. Non arriverà così.
Un WebSocket attiva un evento error, e MDN lo descrive in tre parole: “Un Event generico”. Non esiste alcuna interfaccia personalizzata sottostante, il che significa nessun codice, nessuna ragione e nessuno stato sull’oggetto che ricevi. Nulla viene lanciato, quindi nulla raggiunge un try/catch. La connessione termina e l’unica cosa che trasporta informazioni è l’evento close che segue.
Quindi il codice di chiusura non è un dettaglio. È l’intero messaggio.
I codici che significano qualcosa
Quattro sono degni di conoscenza a memoria, nelle stesse parole di MDN.
- 1000, chiusura normale: “La connessione ha completato con successo lo scopo per il quale è stata creata”. Nulla da investigare.
- 1001, disconnessione: “L’endpoint si sta allontanando, a causa di un errore del server o perché il browser sta navigando via dalla pagina che ha aperto la connessione”.
- 1011, errore interno: “Il server sta terminando la connessione perché ha incontrato una condizione imprevista che gli ha impedito di soddisfare la richiesta”.
- 1006, chiusura anormale, che è quella che effettivamente vedrai e di cui tratta il resto di questo articolo.
Leggi 1001 due volte, perché copre due eventi non correlati con un solo numero. Un server che si spegne e un utente che fa clic su un collegamento producono entrambi il codice 1001. Se stai contando i codici 1001 come incidenti, stai contando le persone che lasciano il tuo sito come incidenti, e il numero seguirà il tuo traffico piuttosto che la tua affidabilità.
1011 è l’opposto ed è quello che merita un avviso. È il server che ammette il guasto a parole proprie.
Il codice che vedi più spesso non potrebbe provenire da nessuno
Questa è la parte che quasi mai è scritta e cambia il modo in cui investighi.
RFC 6455 definisce 1006 così:
1006 è un valore riservato e NON DEVE essere impostato come codice di stato in un frame di chiusura da un endpoint. È designato per l’uso in applicazioni che si aspettano un codice di stato per indicare che la connessione è stata chiusa anormalmente, ad esempio, senza inviare o ricevere un frame di chiusura.
1005 ha lo stesso divieto: “1005 è un valore riservato e NON DEVE essere impostato come codice di stato in un frame di chiusura da un endpoint”.
Quindi nessuno dei due può essere inviato. Non dal tuo server, non dal browser, non da nulla nel mezzo. Sono vietati sulla rete. Quando il tuo handler legge 1006, sta leggendo un numero che il tuo stesso browser ha inventato localmente per descrivere il fatto che la connessione è terminata e non è mai arrivato alcun frame di chiusura.
Questa è un’affermazione più forte di quanto possa sembrare. Significa che non esiste alcun evento corrispondente da andare a trovare da nessun’altra parte.
- Quello che cercano le persone
- Una riga di log del server che spiega il 1006, presupponendo che il server l'abbia inviato
- Quello che esiste
- Nulla. Il codice è stato generato nel browser. Il server potrebbe non sapere ancora che la connessione è terminata e quello che l'ha chiusa spesso non è il server affatto
1006 non è quindi una causa. È la forma dell’evidenza: qualcosa tra i due capi ha smesso di relazionare e nessun capo è riuscito a dire addio.
Chi effettivamente chiude una connessione che nessuno ha chiuso
Una volta che accetti che il browser sta segnalando il silenzio, i sospetti sono le cose che si trovano nel mezzo e hanno opinioni sulle connessioni inattive.
Un reverse proxy o load balancer con un timeout di inattività è il più comune. Questi timeout sono configurabili e spesso brevi, e un WebSocket che non trasporta nulla tra gli aggiornamenti sembra esattamente come una connessione abbandonata. Le apparecchiature di rete aziendale fanno lo stesso, più aggressivamente. Un telefono che passa dal wifi ai dati mobili cambia l’indirizzo del client sotto una socket aperta, il che la chiude senza cerimonia. I laptop in sospensione fanno lo stesso.
Nota cosa hanno in comune tutti questi: il tuo codice di applicazione è corretto, il tuo server è sano e nulla è sbagliato nei dati. Il trasporto se ne è andato. Questo è anche il motivo per cui colpisce alcuni utenti costantemente e altri mai, che è la firma che rimanda indietro un rapporto di bug contrassegnato come “non riproducibile”.
La difesa standard è un battito cardiaco: invia qualcosa di piccolo su un timer, più frequentemente del timeout di inattività più breve tra te e i tuoi utenti. Non ripara una connessione caduta. Impedisce a un’inattiva di essere scambiata per una morta, che è un problema diverso che produce lo stesso rapporto.
Cosa registrare, perché l’evento di chiusura è l’unico testimone
L’evento di chiusura accade una volta, nel browser, e poi la pagina di solito si riconnette e l’evidenza è sparita. Quattro campi sono degni di cattura in quel momento.
socket.addEventListener('close', (event) => {
report({
code: event.code, // 1006 significa che nessuno ha inviato un codice
wasClean: event.wasClean, // true se la connessione è stata chiusa in modo pulito
reason: event.reason, // di solito vuoto e vuoto è informativo
secondsSinceLastMessage: (Date.now() - lastMessageAt) / 1000,
reconnects: reconnectCount
});
});
wasClean è il segnale più economico dei quattro: MDN lo definisce come true “se la connessione è stata chiusa in modo pulito”, quindi un false separa una connessione che è terminata da una che è stata terminata. E secondsSinceLastMessage è il campo che cattura un timeout di inattività, perché l’intervallo sarà raggruppato attorno allo stesso valore per ogni utente interessato. Un cluster di poco meno di sessanta secondi non è una coincidenza, è un numero configurato da qualche parte nel percorso.
Session Replay
Estensione Chrome gratuita. Un clic sulla pagina che si comporta male cattura lo screenshot, la console e il log di rete e ti consegna un collegamento da incollare nel ticket.
La domanda da porre alla persona che l’ha segnalato
Una domanda, e non è una tecnica: un aggiornamento l’ha risolto?
Se l’ha fatto, i dati non erano mai sbagliati e il rendering non era mai sbagliato. Una connessione fresca era tutto ciò che serviva, il che punta al trasporto e lontano da tutto il resto. Quella sola risposta elimina più di qualsiasi quantità di ipotesi sulla loro rete, e possono darla senza sapere cos’è un WebSocket.
Il follow-up che merita di essere chiesto è da quanto tempo la pagina era stata aperta. Una scheda lasciata aperta durante il pranzo e una scheda aperta un minuto fa sono casi diversi, e il segnalante conosce la risposta.
Cosa ricavarne
Il guasto è silenzioso per costruzione. Nulla viene lanciato, l’evento error non contiene dettagli e la pagina continua a rendere l’ultima cosa di cui è stata informata.
Il codice di chiusura è l’intero messaggio e il più comune è un’invenzione locale: 1006 è il tuo browser che dice che non è arrivato nulla, non il tuo server che spiega perché se ne è andato. I codici che nessuno può inviare sono codici che nessuno può spiegare, quindi smetti di cercare la metà lato server e inizia a cercare quello che si trova tra te e l’utente.
La specifica stessa è RFC 6455, e i codici di chiusura con i loro significati di una riga si trovano in pagina CloseEvent.code di MDN.