3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-11-25 13:29:27 +01:00

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)
This commit is contained in:
Shivaram Lingamneni 2021-02-24 14:08:04 -05:00
parent 640572e151
commit d547d05205
3 changed files with 17 additions and 12 deletions

View File

@ -691,7 +691,7 @@ func (client *Client) run(session *Session) {
} else if err != nil { } else if err != nil {
var quitMessage string var quitMessage string
switch err { switch err {
case ircreader.ErrReadQ, errWSBinaryMessage: case ircreader.ErrReadQ:
quitMessage = err.Error() quitMessage = err.Error()
default: default:
quitMessage = "connection closed" quitMessage = "connection closed"

View File

@ -5,7 +5,6 @@ package irc
import ( import (
"bytes" "bytes"
"errors"
"net" "net"
"unicode/utf8" "unicode/utf8"
@ -22,8 +21,7 @@ const (
) )
var ( var (
crlf = []byte{'\r', '\n'} crlf = []byte{'\r', '\n'}
errWSBinaryMessage = errors.New("WebSocket binary messages are unsupported")
) )
// IRCConn abstracts away the distinction between a regular // 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. // IRCWSConn is an IRCConn over a websocket.
type IRCWSConn struct { type IRCWSConn struct {
conn *websocket.Conn conn *websocket.Conn
binary bool
} }
func NewIRCWSConn(conn *websocket.Conn) IRCWSConn { 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 { func (wc IRCWSConn) UnderlyingConn() *utils.WrappedConn {
@ -106,7 +106,11 @@ func (wc IRCWSConn) UnderlyingConn() *utils.WrappedConn {
func (wc IRCWSConn) WriteLine(buf []byte) (err error) { func (wc IRCWSConn) WriteLine(buf []byte) (err error) {
buf = bytes.TrimSuffix(buf, crlf) buf = bytes.TrimSuffix(buf, crlf)
// #1483: if we have websockets at all, then we're enforcing utf8 // #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) { 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) { func (wc IRCWSConn) ReadLine() (line []byte, err error) {
messageType, line, err := wc.conn.ReadMessage() messageType, line, err := wc.conn.ReadMessage()
if err == nil { if err == nil {
if messageType == websocket.TextMessage { if messageType == websocket.BinaryMessage && globalUtf8EnforcementSetting {
return line, nil if !utf8.Valid(line) {
} else { return line, errInvalidUtf8
return nil, errWSBinaryMessage }
} }
return line, nil
} else if err == websocket.ErrReadLimit { } else if err == websocket.ErrReadLimit {
return line, ircreader.ErrReadQ return line, ircreader.ErrReadQ
} else { } else {

View File

@ -166,7 +166,7 @@ func (wl *WSListener) handle(w http.ResponseWriter, r *http.Request) {
} }
return false return false
}, },
Subprotocols: []string{"text.ircv3.net"}, Subprotocols: []string{"text.ircv3.net", "binary.ircv3.net"},
} }
conn, err := wsUpgrader.Upgrade(w, r, nil) conn, err := wsUpgrader.Upgrade(w, r, nil)