Salta al contenuto

Menu

← Blog

RFC 8599 in pratica: notifiche push SIP con OpenSIPS e Gorush

Come facciamo squillare un'app chiusa: OpenSIPS trattiene l'INVITE, Gorush manda la notifica push e la chiamata parte quando il telefono si registra di nuovo.

VoIP6 min di lettura
In questo articolo
  1. Il punto di partenza
  2. OpenSIPS
  3. Gorush
  4. Come si registrano le app
  5. Vita di una chiamata
  6. Il risultato

Gestire un servizio SIP con app mobile proprie pone parecchie sfide. Una sfida che si presenta fin dal primo giorno: come si chiama un telefono che non è connesso?

In SIP un dispositivo deve essere registrato prima di poter ricevere qualsiasi cosa, che sia un INVITE o un CANCEL. Registrarsi significa dire al server «sono raggiungibile a questo indirizzo». I sistemi operativi mobile non se ne curano: quando l'app che abbiamo realizzato per SpecialCall passa in background, iOS e Android chiudono presto la sua WebSocket, e la registrazione resta legata a un socket che non esiste più. Se in quel momento mandiamo un INVITE, non risponde nessuno.

La soluzione è nota: inviare al telefono una notifica push, aspettare che l'app si svegli e si registri di nuovo, e solo allora mandare l'INVITE. La parte difficile è l'attesa. In questo articolo spieghiamo come la gestiamo.

Il punto di partenza

All'inizio se ne occupava tutto il nostro backend. Quando doveva far squillare un interprete, inviava lui stesso la notifica push e poi continuava a tentare di avviare la chiamata tramite Asterisk finché il dispositivo non si registrava, rinunciando dopo 60 secondi. Funzionava, ma era un ciclo di polling distribuito su due sistemi. La push e la chiamata non sapevano nulla l'una dell'altra, ogni tentativo costava tempo e il backend doveva conoscere sia lo stato SIP sia APNs/FCM.

Abbiamo quindi cambiato in modo sostanziale la nostra architettura SIP: abbiamo messo un proxy SIP davanti alle app. Un proxy può trattenere un INVITE e aspettare da solo una registrazione. Il ciclo di tentativi non serve più.

OpenSIPS

Il proxy che abbiamo scelto è OpenSIPS, un server SIP open source maturo, in produzione da quasi vent'anni. Non è un centralino come Asterisk: non mixa l'audio, non riproduce messaggi e non collega canali. Il suo compito è instradare i messaggi SIP, e lo fa in modo molto veloce e flessibile.

OpenSIPS si programma con un linguaggio di scripting proprio. Il file di configurazione è un insieme di route, blocchi di logica eseguiti per ogni richiesta, risposta, errore o evento interno. Le funzionalità arrivano da moduli che si caricano secondo necessità: per esempio registrar e usrloc fanno da registrar e mantengono la tabella delle posizioni, tm gestisce le transazioni con stato, rest_client esegue chiamate HTTP e auth_db si occupa dell'autenticazione digest.

Ciò che lo rende adatto a noi è che supporta nativamente l'RFC 8599. L'RFC 8599 definisce come un dispositivo SIP include i propri dati push nel Contact del suo REGISTER, e il modulo registrar interpreta questi parametri. Quando arriva un INVITE per un dispositivo il cui socket non c'è più, OpenSIPS può trattenere l'INVITE, generare un evento perché venga inviata una push e rilasciare l'INVITE appena il dispositivo si registra di nuovo.

Nella nostra configurazione OpenSIPS è anche il registrar. Le app vi si collegano tramite WebSocket sicure (WSS) e lì si registrano, mentre Asterisk passa a OpenSIPS le chiamate destinate a loro attraverso un semplice trunk UDP. In più, OpenSIPS verifica le credenziali delle app direttamente sulla tabella di autenticazione PJSIP di Asterisk, così non abbiamo dovuto migrare nessuna credenziale.

Gorush

OpenSIPS non invia le notifiche push da sé: segnala quando ne serve una e lascia a noi l'invio. Non volevamo scrivere l'ennesimo client APNs/FCM, quindi abbiamo usato un altro progetto open source: Gorush.

Gorush è un server di notifiche push scritto in Go. È un unico binario con un solo endpoint HTTP (POST /api/push), che accetta un elenco di notifiche in JSON e le consegna tramite APNs (iOS) o FCM (Android). Gestisce le connessioni ai provider, le chiavi di autenticazione e i nuovi tentativi, ed espone un controllo di salute e metriche Prometheus.

Gira sulla stessa macchina di OpenSIPS e ascolta solo su 127.0.0.1: gli parla soltanto OpenSIPS. Lo usiamo in modalità sincrona, quindi ogni richiesta HTTP attende la risposta di Apple o Google e restituisce l'esito per ogni token. Così abbiamo una riga di log per chiamata che dice se la push di risveglio è partita.

Come si registrano le app

Le app si registrano con i parametri dell'RFC 8599 nella URI del Contact:

REGISTER · Contact
Contact: <sip:interpreter@...;transport=ws; pn-provider=apns; pn-param=<TEAM_ID>.example.voip; pn-prid=<PushKit token>>
  • pn-provider vale apns su iOS e fcm su Android.
  • pn-param identifica l'app: il team APNs e il bundle ID con il suffisso .voip, oppure il progetto Firebase.
  • pn-prid è il token push del dispositivo. Su iOS è il token PushKit VoIP, non quello delle normali notifiche remote: solo le push VoIP possono svegliare un'app completamente chiusa e mostrare la schermata di chiamata nativa tramite CallKit.

Usiamo una scadenza della registrazione di una settimana. Gli interpreti devono restare raggiungibili a lungo anche se non aprono l'app, e quindi non si registrano di nuovo, da giorni. La WebSocket dietro la registrazione sarà morta molto prima che passi la settimana, ma non importa: quello che ci serve conservare è il contatto con i suoi parametri push. Finché OpenSIPS li ha, può svegliare il telefono.

Vita di una chiamata

BackendAsteriskOpenSIPSGorushAppdorme · socket chiusosveglialookup: contatto pushINVITE trattenutooriginate (ARI)INVITE · UDPPOST /api/pushAPNs / FCM pushREGISTER · WSSINVITE rilasciatosquilla
  • SIP
  • push / HTTP
  • controllo chiamata (ARI)
  • INVITE trattenuto
OpenSIPS trattiene l'INVITE finché l'app, svegliata dalla push, non si registra di nuovo: poi lo rilascia sulla nuova connessione e il telefono squilla.

Backend. Decide chi chiamare: gli interpreti che parlano la lingua giusta, sono disponibili e non sono già impegnati in una chiamata. Poi chiede ad Asterisk di avviare una chiamata verso ciascuno di loro.

Asterisk. Crea il canale e invia l'INVITE al trunk di OpenSIPS.

OpenSIPS. Riconosce che l'INVITE è per uno dei suoi utenti registrati e lo cerca nella tabella delle posizioni. Gli esiti possibili sono tre:

  • Il dispositivo è connesso. L'INVITE viene inoltrato subito e nessuna push viene inviata.
  • Il dispositivo ha un contatto abilitato alle push. Il registrar trattiene l'INVITE e genera un evento E_UL_CONTACT_REFRESH. La nostra route di evento compone la richiesta per Gorush e la invia con una POST. Quando l'app si registra di nuovo, OpenSIPS rilascia l'INVITE trattenuto sulla nuova connessione.
  • Il dispositivo non è registrato affatto. Non c'è niente da svegliare, quindi OpenSIPS rifiuta subito l'INVITE e il backend passa oltre.

Gorush. Consegna la notifica tramite APNs o FCM.

App. Si sveglia, mostra la chiamata in arrivo (con CallKit su iOS), si registra e riceve l'INVITE.

Il risultato

Il backend non sa più che le notifiche push esistono. Chiede ad Asterisk di avviare una chiamata e Asterisk invia un INVITE, come se chiamasse un telefono da scrivania. Svegliare il dispositivo, aspettarlo e gestire la corsa con un altro interprete avviene tutto in un unico punto, dentro una sola transazione SIP, con due componenti open source e qualche centinaio di righe di configurazione di OpenSIPS.

VoIP

Volete saperne di più?

Scriveteci o chiamateci: vi raccontiamo come lavoriamo e come potrebbe funzionare per la vostra azienda.

Il servizio VoIPContattaci

Altri articoli

  • VoIP

    Monitorare Asterisk con Prometheus

    Da Asterisk 17 le metriche escono già nel formato di Prometheus: quali sono e come raccoglierle, con un piccolo progetto Docker da provare.

    · 4 min
  • Sviluppo

    Un ufficio più smart, a portata di mano

    Con Home Assistant gestiamo luci, climatizzatori, telecamere e altri dispositivi dell'ufficio da un unico punto, anche dal telefono.

    · 3 min
  • Helpdesk

    Un gestionale per gli interventi hardware in tutta Italia

    Il gestionale che abbiamo costruito per l'assistenza hardware: tecnici sul territorio, apparati, contatti e ticket.

    · 3 min

Tutti gli articoli