From d547d05205d04f9c2e6c1a459b48bab60f04441b Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Wed, 24 Feb 2021 14:08:04 -0500 Subject: [PATCH] implement candidate compromise proposal for websockets 1. Text and binary frames are accepted 2. Text frames are sent by default 3. Binary frames are sent to clients who negotiate `binary.ircv3.net` 4. Non-UTF8 data is not accepted (enabling websockets still enables UTFONLY) --- irc/client.go | 2 +- irc/ircconn.go | 25 +++++++++++++++---------- irc/listeners.go | 2 +- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/irc/client.go b/irc/client.go index 6fcb3cf6..7a03d0f1 100644 --- a/irc/client.go +++ b/irc/client.go @@ -691,7 +691,7 @@ func (client *Client) run(session *Session) { } else if err != nil { var quitMessage string switch err { - case ircreader.ErrReadQ, errWSBinaryMessage: + case ircreader.ErrReadQ: quitMessage = err.Error() default: quitMessage = "connection closed" diff --git a/irc/ircconn.go b/irc/ircconn.go index a1dc6387..0a424608 100644 --- a/irc/ircconn.go +++ b/irc/ircconn.go @@ -5,7 +5,6 @@ package irc import ( "bytes" - "errors" "net" "unicode/utf8" @@ -22,8 +21,7 @@ const ( ) var ( - crlf = []byte{'\r', '\n'} - errWSBinaryMessage = errors.New("WebSocket binary messages are unsupported") + crlf = []byte{'\r', '\n'} ) // IRCConn abstracts away the distinction between a regular @@ -90,11 +88,13 @@ func (cc *IRCStreamConn) Close() (err error) { // IRCWSConn is an IRCConn over a websocket. type IRCWSConn struct { - conn *websocket.Conn + conn *websocket.Conn + binary bool } func NewIRCWSConn(conn *websocket.Conn) IRCWSConn { - return IRCWSConn{conn: conn} + binary := conn.Subprotocol() == "binary.ircv3.net" + return IRCWSConn{conn: conn, binary: binary} } func (wc IRCWSConn) UnderlyingConn() *utils.WrappedConn { @@ -106,7 +106,11 @@ func (wc IRCWSConn) UnderlyingConn() *utils.WrappedConn { func (wc IRCWSConn) WriteLine(buf []byte) (err error) { buf = bytes.TrimSuffix(buf, crlf) // #1483: if we have websockets at all, then we're enforcing utf8 - return wc.conn.WriteMessage(websocket.TextMessage, buf) + messageType := websocket.TextMessage + if wc.binary { + messageType = websocket.BinaryMessage + } + return wc.conn.WriteMessage(messageType, buf) } func (wc IRCWSConn) WriteLines(buffers [][]byte) (err error) { @@ -122,11 +126,12 @@ func (wc IRCWSConn) WriteLines(buffers [][]byte) (err error) { func (wc IRCWSConn) ReadLine() (line []byte, err error) { messageType, line, err := wc.conn.ReadMessage() if err == nil { - if messageType == websocket.TextMessage { - return line, nil - } else { - return nil, errWSBinaryMessage + if messageType == websocket.BinaryMessage && globalUtf8EnforcementSetting { + if !utf8.Valid(line) { + return line, errInvalidUtf8 + } } + return line, nil } else if err == websocket.ErrReadLimit { return line, ircreader.ErrReadQ } else { diff --git a/irc/listeners.go b/irc/listeners.go index 740a0466..72f99a5f 100644 --- a/irc/listeners.go +++ b/irc/listeners.go @@ -166,7 +166,7 @@ func (wl *WSListener) handle(w http.ResponseWriter, r *http.Request) { } return false }, - Subprotocols: []string{"text.ircv3.net"}, + Subprotocols: []string{"text.ircv3.net", "binary.ircv3.net"}, } conn, err := wsUpgrader.Upgrade(w, r, nil)