2019-10-06 18:15:26 +00:00
|
|
|
package xmpp
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2019-10-16 12:44:22 +00:00
|
|
|
"errors"
|
2019-10-06 18:15:26 +00:00
|
|
|
"net"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"nhooyr.io/websocket"
|
|
|
|
)
|
|
|
|
|
2019-10-15 18:56:11 +00:00
|
|
|
const pingTimeout = time.Duration(5) * time.Second
|
|
|
|
|
2019-10-16 12:44:22 +00:00
|
|
|
var ServerDoesNotSupportXmppOverWebsocket = errors.New("The websocket server does not support the xmpp subprotocol")
|
|
|
|
|
2019-10-06 18:15:26 +00:00
|
|
|
type WebsocketTransport struct {
|
2019-10-11 04:41:15 +00:00
|
|
|
Config TransportConfiguration
|
2019-10-06 18:15:26 +00:00
|
|
|
wsConn *websocket.Conn
|
|
|
|
netConn net.Conn
|
|
|
|
ctx context.Context
|
|
|
|
}
|
|
|
|
|
2019-10-11 04:41:15 +00:00
|
|
|
func (t *WebsocketTransport) Connect() error {
|
2019-10-06 18:15:26 +00:00
|
|
|
t.ctx = context.Background()
|
|
|
|
|
2019-10-12 15:50:00 +00:00
|
|
|
if t.Config.ConnectTimeout > 0 {
|
|
|
|
ctx, cancel := context.WithTimeout(t.ctx, time.Duration(t.Config.ConnectTimeout)*time.Second)
|
|
|
|
t.ctx = ctx
|
|
|
|
defer cancel()
|
|
|
|
}
|
2019-10-06 18:15:26 +00:00
|
|
|
|
2019-10-16 12:44:22 +00:00
|
|
|
wsConn, response, err := websocket.Dial(t.ctx, t.Config.Address, &websocket.DialOptions{
|
|
|
|
Subprotocols: []string{"xmpp"},
|
|
|
|
})
|
2019-10-12 15:46:53 +00:00
|
|
|
if err != nil {
|
|
|
|
return NewConnError(err, true)
|
2019-10-06 18:15:26 +00:00
|
|
|
}
|
2019-10-16 12:44:22 +00:00
|
|
|
if response.Header.Get("Sec-WebSocket-Protocol") != "xmpp" {
|
|
|
|
return ServerDoesNotSupportXmppOverWebsocket
|
|
|
|
}
|
2019-10-12 15:46:53 +00:00
|
|
|
t.wsConn = wsConn
|
|
|
|
t.netConn = websocket.NetConn(t.ctx, t.wsConn, websocket.MessageText)
|
|
|
|
return nil
|
2019-10-06 18:15:26 +00:00
|
|
|
}
|
|
|
|
|
2019-10-11 05:15:47 +00:00
|
|
|
func (t WebsocketTransport) StartTLS(domain string) error {
|
|
|
|
return TLSNotSupported
|
|
|
|
}
|
|
|
|
|
2019-10-06 18:15:26 +00:00
|
|
|
func (t WebsocketTransport) DoesStartTLS() bool {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2019-10-11 05:15:47 +00:00
|
|
|
func (t WebsocketTransport) IsSecure() bool {
|
|
|
|
return strings.HasPrefix(t.Config.Address, "wss:")
|
|
|
|
}
|
|
|
|
|
2019-10-15 18:56:11 +00:00
|
|
|
func (t WebsocketTransport) Ping() error {
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), pingTimeout)
|
|
|
|
defer cancel()
|
|
|
|
// Note that we do not use wsConn.Ping(), because not all websocket servers
|
|
|
|
// (ejabberd for example) implement ping frames
|
|
|
|
return t.wsConn.Write(ctx, websocket.MessageText, []byte(" "))
|
|
|
|
}
|
|
|
|
|
2019-10-06 18:15:26 +00:00
|
|
|
func (t WebsocketTransport) Read(p []byte) (n int, err error) {
|
|
|
|
return t.netConn.Read(p)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t WebsocketTransport) Write(p []byte) (n int, err error) {
|
|
|
|
return t.netConn.Write(p)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t WebsocketTransport) Close() error {
|
|
|
|
return t.netConn.Close()
|
|
|
|
}
|