use the new goshuirc ircreader

This commit is contained in:
Shivaram Lingamneni
2021-02-13 20:58:19 -05:00
parent ec8ed2ae2a
commit d0e11f49ad
7 changed files with 141 additions and 199 deletions
+3 -1
View File
@@ -18,7 +18,9 @@ import (
"github.com/goshuirc/irc-go/ircfmt"
"github.com/goshuirc/irc-go/ircmsg"
"github.com/goshuirc/irc-go/ircreader"
ident "github.com/oragono/go-ident"
"github.com/oragono/oragono/irc/caps"
"github.com/oragono/oragono/irc/connection_limits"
"github.com/oragono/oragono/irc/flatip"
@@ -689,7 +691,7 @@ func (client *Client) run(session *Session) {
} else if err != nil {
var quitMessage string
switch err {
case errReadQ, errWSBinaryMessage:
case ircreader.ErrReadQ, errWSBinaryMessage:
quitMessage = err.Error()
default:
quitMessage = "connection closed"
+14 -61
View File
@@ -6,12 +6,12 @@ package irc
import (
"bytes"
"errors"
"io"
"net"
"unicode/utf8"
"github.com/gorilla/websocket"
"github.com/goshuirc/irc-go/ircmsg"
"github.com/goshuirc/irc-go/ircreader"
"github.com/oragono/oragono/irc/utils"
)
@@ -23,7 +23,6 @@ const (
var (
crlf = []byte{'\r', '\n'}
errReadQ = errors.New("ReadQ Exceeded")
errWSBinaryMessage = errors.New("WebSocket binary messages are unsupported")
)
@@ -48,17 +47,14 @@ type IRCConn interface {
type IRCStreamConn struct {
conn *utils.WrappedConn
buf []byte
start int // start of valid (i.e., read but not yet consumed) data in the buffer
end int // end of valid data in the buffer
searchFrom int // start of valid data in the buffer not yet searched for \n
eof bool
reader ircreader.IRCReader
}
func NewIRCStreamConn(conn *utils.WrappedConn) *IRCStreamConn {
return &IRCStreamConn{
conn: conn,
}
var c IRCStreamConn
c.conn = conn
c.reader.Initialize(conn.Conn, initialBufferSize, maxReadQBytes)
return &c
}
func (cc *IRCStreamConn) UnderlyingConn() *utils.WrappedConn {
@@ -78,56 +74,13 @@ func (cc *IRCStreamConn) WriteLines(buffers [][]byte) (err error) {
}
func (cc *IRCStreamConn) ReadLine() ([]byte, error) {
for {
// try to find a terminated line in the buffered data already read
nlidx := bytes.IndexByte(cc.buf[cc.searchFrom:cc.end], '\n')
if nlidx != -1 {
// got a complete line
line := cc.buf[cc.start : cc.searchFrom+nlidx]
cc.start = cc.searchFrom + nlidx + 1
cc.searchFrom = cc.start
if globalUtf8EnforcementSetting && !utf8.Valid(line) {
return line, errInvalidUtf8
} else {
return line, nil
}
}
if cc.start == 0 && len(cc.buf) == maxReadQBytes {
return nil, errReadQ // out of space, can't expand or slide
}
if cc.eof {
return nil, io.EOF
}
if len(cc.buf) < maxReadQBytes && (len(cc.buf)-(cc.end-cc.start) < initialBufferSize/2) {
// allocate a new buffer, copy any remaining data
newLen := utils.RoundUpToPowerOfTwo(len(cc.buf) + 1)
if newLen > maxReadQBytes {
newLen = maxReadQBytes
} else if newLen < initialBufferSize {
newLen = initialBufferSize
}
newBuf := make([]byte, newLen)
copy(newBuf, cc.buf[cc.start:cc.end])
cc.buf = newBuf
} else if cc.start != 0 {
// slide remaining data back to the front of the buffer
copy(cc.buf, cc.buf[cc.start:cc.end])
}
cc.end = cc.end - cc.start
cc.start = 0
cc.searchFrom = cc.end
n, err := cc.conn.Read(cc.buf[cc.end:])
cc.end += n
if n != 0 && err == io.EOF {
// we may have received new \n-terminated lines, try to parse them
cc.eof = true
} else if err != nil {
return nil, err
}
line, err := cc.reader.ReadLine()
if err != nil {
return nil, err
} else if globalUtf8EnforcementSetting && !utf8.Valid(line) {
return line, errInvalidUtf8
} else {
return line, nil
}
}
@@ -175,7 +128,7 @@ func (wc IRCWSConn) ReadLine() (line []byte, err error) {
return nil, errWSBinaryMessage
}
} else if err == websocket.ErrReadLimit {
return line, errReadQ
return line, ircreader.ErrReadQ
} else {
return line, err
}
-135
View File
@@ -1,135 +0,0 @@
// Copyright (c) 2020 Shivaram Lingamneni <slingamn@cs.stanford.edu>
// released under the MIT license
package irc
import (
"io"
"math/rand"
"net"
"reflect"
"testing"
"time"
"github.com/oragono/oragono/irc/utils"
)
// mockConn is a fake net.Conn / io.Reader that yields len(counts) lines,
// each consisting of counts[i] 'a' characters and a terminating '\n'
type mockConn struct {
counts []int
}
func min(i, j int) (m int) {
if i < j {
return i
} else {
return j
}
}
func (c *mockConn) Read(b []byte) (n int, err error) {
for len(b) > 0 {
if len(c.counts) == 0 {
return n, io.EOF
}
if c.counts[0] == 0 {
b[0] = '\n'
c.counts = c.counts[1:]
b = b[1:]
n += 1
continue
}
size := min(c.counts[0], len(b))
for i := 0; i < size; i++ {
b[i] = 'a'
}
c.counts[0] -= size
b = b[size:]
n += size
}
return n, nil
}
func (c *mockConn) Write(b []byte) (n int, err error) {
return
}
func (c *mockConn) Close() error {
c.counts = nil
return nil
}
func (c *mockConn) LocalAddr() net.Addr {
return nil
}
func (c *mockConn) RemoteAddr() net.Addr {
return nil
}
func (c *mockConn) SetDeadline(t time.Time) error {
return nil
}
func (c *mockConn) SetReadDeadline(t time.Time) error {
return nil
}
func (c *mockConn) SetWriteDeadline(t time.Time) error {
return nil
}
func newMockConn(counts []int) *utils.WrappedConn {
cpCounts := make([]int, len(counts))
copy(cpCounts, counts)
c := &mockConn{
counts: cpCounts,
}
return &utils.WrappedConn{
Conn: c,
}
}
// construct a mock reader with some number of \n-terminated lines,
// verify that IRCStreamConn can read and split them as expected
func doLineReaderTest(counts []int, t *testing.T) {
c := newMockConn(counts)
r := NewIRCStreamConn(c)
var readCounts []int
for {
line, err := r.ReadLine()
if err == nil {
readCounts = append(readCounts, len(line))
} else if err == io.EOF {
break
} else {
panic(err)
}
}
if !reflect.DeepEqual(counts, readCounts) {
t.Errorf("expected %#v, got %#v", counts, readCounts)
}
}
const (
maxMockReaderLen = 100
maxMockReaderLineLen = 4096 + 511
)
func TestLineReader(t *testing.T) {
counts := []int{44, 428, 3, 0, 200, 2000, 0, 4044, 33, 3, 2, 1, 0, 1, 2, 3, 48, 555}
doLineReaderTest(counts, t)
// fuzz
r := rand.New(rand.NewSource(time.Now().UnixNano()))
for i := 0; i < 1000; i++ {
countsLen := r.Intn(maxMockReaderLen) + 1
counts := make([]int, countsLen)
for i := 0; i < countsLen; i++ {
counts[i] = r.Intn(maxMockReaderLineLen)
}
doLineReaderTest(counts, t)
}
}