Merge master

This commit is contained in:
Daniel Oaks 2017-03-24 12:29:13 +10:00
commit 6842d2ead6
7 changed files with 123 additions and 17 deletions

View File

@ -19,8 +19,9 @@ New release of Oragono!
* Added draft IRCv3 capability [draft/sts](http://ircv3.net/specs/core/sts-3.3.html). * Added draft IRCv3 capability [draft/sts](http://ircv3.net/specs/core/sts-3.3.html).
### Changed ### Changed
* Logging is now much better and useful. * `DLINE` and `KLINE` now let you specify years, months and days (e.g. `1y12m30d`) in durations.
* Can now specify years, months and days (e.g. `1y12m30d`) with DLINE and KLINE. * Logging is now much more useful, displays colours and can log to disk.
* Socket handling has been rewritten, which means we should support more connections more effectively (thanks dp- for the suggestion!).
### Removed ### Removed

View File

@ -74,7 +74,8 @@ type Client struct {
// NewClient returns a client with all the appropriate info setup. // NewClient returns a client with all the appropriate info setup.
func NewClient(server *Server, conn net.Conn, isTLS bool) *Client { func NewClient(server *Server, conn net.Conn, isTLS bool) *Client {
now := time.Now() now := time.Now()
socket := NewSocket(conn) socket := NewSocket(conn, server.MaxSendQBytes)
go socket.RunSocketWriter()
client := &Client{ client := &Client{
atime: now, atime: now,
authorized: server.password == nil, authorized: server.password == nil,

View File

@ -14,6 +14,8 @@ import (
"strings" "strings"
"time" "time"
"code.cloudfoundry.org/bytefmt"
"github.com/DanielOaks/oragono/irc/custime" "github.com/DanielOaks/oragono/irc/custime"
"github.com/DanielOaks/oragono/irc/logger" "github.com/DanielOaks/oragono/irc/logger"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
@ -171,6 +173,8 @@ type Config struct {
RestAPI RestAPIConfig `yaml:"rest-api"` RestAPI RestAPIConfig `yaml:"rest-api"`
CheckIdent bool `yaml:"check-ident"` CheckIdent bool `yaml:"check-ident"`
MOTD string MOTD string
MaxSendQString string `yaml:"max-sendq"`
MaxSendQBytes uint64
ConnectionLimits ConnectionLimitsConfig `yaml:"connection-limits"` ConnectionLimits ConnectionLimitsConfig `yaml:"connection-limits"`
ConnectionThrottle ConnectionThrottleConfig `yaml:"connection-throttling"` ConnectionThrottle ConnectionThrottleConfig `yaml:"connection-throttling"`
} }
@ -433,5 +437,10 @@ func LoadConfig(filename string) (config *Config, err error) {
} }
config.Logging = newLogConfigs config.Logging = newLogConfigs
config.Server.MaxSendQBytes, err = bytefmt.ToBytes(config.Server.MaxSendQString)
if err != nil {
return nil, fmt.Errorf("Could not parse maximum SendQ size (make sure it only contains whole numbers): %s", err.Error())
}
return config, nil return config, nil
} }

View File

@ -212,7 +212,7 @@ func (logger *singleLogger) Log(level Level, logType string, messageParts ...str
sep := grey(":") sep := grey(":")
fullStringFormatted := fmt.Sprintf("%s %s %s %s %s %s ", timeGrey(time.Now().UTC().Format("2006-01-02T15:04:05Z")), sep, levelDisplay, sep, section(logType), sep) fullStringFormatted := fmt.Sprintf("%s %s %s %s %s %s ", timeGrey(time.Now().UTC().Format("2006-01-02T15:04:05Z")), sep, levelDisplay, sep, section(logType), sep)
fullStringRaw := fmt.Sprintf("%s : %s : %s : ", time.Now().UTC().Format("2006-01-02T15:04:05Z"), LogLevelDisplayNames[level], section(logType)) fullStringRaw := fmt.Sprintf("%s : %s : %s : ", time.Now().UTC().Format("2006-01-02T15:04:05Z"), LogLevelDisplayNames[level], logType)
for i, p := range messageParts { for i, p := range messageParts {
fullStringFormatted += p fullStringFormatted += p
fullStringRaw += p fullStringRaw += p

View File

@ -104,6 +104,7 @@ type Server struct {
listeners map[string]ListenerInterface listeners map[string]ListenerInterface
listenerUpdateMutex sync.Mutex listenerUpdateMutex sync.Mutex
logger *logger.Manager logger *logger.Manager
MaxSendQBytes uint64
monitoring map[string][]Client monitoring map[string][]Client
motdLines []string motdLines []string
name string name string
@ -214,6 +215,7 @@ func NewServer(configFilename string, config *Config, logger *logger.Manager) (*
}, },
listeners: make(map[string]ListenerInterface), listeners: make(map[string]ListenerInterface),
logger: logger, logger: logger,
MaxSendQBytes: config.Server.MaxSendQBytes,
monitoring: make(map[string][]Client), monitoring: make(map[string][]Client),
name: config.Server.Name, name: config.Server.Name,
nameCasefolded: casefoldedName, nameCasefolded: casefoldedName,
@ -1423,6 +1425,18 @@ func (server *Server) rehash() error {
accountReg := NewAccountRegistration(config.Accounts.Registration) accountReg := NewAccountRegistration(config.Accounts.Registration)
server.accountRegistration = &accountReg server.accountRegistration = &accountReg
// set new sendqueue size
if config.Server.MaxSendQBytes != server.MaxSendQBytes {
server.MaxSendQBytes = config.Server.MaxSendQBytes
// update on all clients
server.clients.ByNickMutex.RLock()
for _, sClient := range server.clients.ByNick {
sClient.socket.MaxSendQBytes = config.Server.MaxSendQBytes
}
server.clients.ByNickMutex.RUnlock()
}
// set RPL_ISUPPORT // set RPL_ISUPPORT
oldISupportList := server.isupport oldISupportList := server.isupport
server.setISupport() server.setISupport()

View File

@ -10,9 +10,11 @@ import (
"crypto/tls" "crypto/tls"
"encoding/hex" "encoding/hex"
"errors" "errors"
"fmt"
"io" "io"
"net" "net"
"strings" "strings"
"sync"
"time" "time"
) )
@ -27,23 +29,33 @@ type Socket struct {
Closed bool Closed bool
conn net.Conn conn net.Conn
reader *bufio.Reader reader *bufio.Reader
MaxSendQBytes uint64
lineToSendExists chan bool
linesToSend []string
linesToSendMutex sync.Mutex
} }
// NewSocket returns a new Socket. // NewSocket returns a new Socket.
func NewSocket(conn net.Conn) Socket { func NewSocket(conn net.Conn, maxSendQBytes uint64) Socket {
return Socket{ return Socket{
conn: conn, conn: conn,
reader: bufio.NewReader(conn), reader: bufio.NewReader(conn),
MaxSendQBytes: maxSendQBytes,
lineToSendExists: make(chan bool),
} }
} }
// Close stops a Socket from being able to send/receive any more data. // Close stops a Socket from being able to send/receive any more data.
func (socket *Socket) Close() { func (socket *Socket) Close() {
if socket.Closed {
return
}
socket.Closed = true socket.Closed = true
socket.conn.Close()
// 'send data' to force close loop to happen
socket.linesToSendMutex.Lock()
socket.linesToSend = append(socket.linesToSend, "")
socket.linesToSendMutex.Unlock()
go socket.fillLineToSendExists()
} }
// CertFP returns the fingerprint of the certificate provided by the client. // CertFP returns the fingerprint of the certificate provided by the client.
@ -104,15 +116,80 @@ func (socket *Socket) Write(data string) error {
return io.EOF return io.EOF
} }
// write data socket.linesToSendMutex.Lock()
_, err := socket.conn.Write([]byte(data)) socket.linesToSend = append(socket.linesToSend, data)
if err != nil { socket.linesToSendMutex.Unlock()
socket.Close() go socket.fillLineToSendExists()
return err
}
return nil return nil
} }
// fillLineToSendExists only exists because you can't goroutine single statements.
func (socket *Socket) fillLineToSendExists() {
socket.lineToSendExists <- true
}
// RunSocketWriter starts writing messages to the outgoing socket.
func (socket *Socket) RunSocketWriter() {
var errOut bool
for {
// wait for new lines
select {
case <-socket.lineToSendExists:
socket.linesToSendMutex.Lock()
// check sendq
var sendQBytes uint64
for _, line := range socket.linesToSend {
sendQBytes += uint64(len(line))
if socket.MaxSendQBytes < sendQBytes {
break
}
}
if socket.MaxSendQBytes < sendQBytes {
socket.conn.Write([]byte("\r\nERROR :SendQ Exceeded\r\n"))
fmt.Println("SendQ exceeded, disconnected client")
break
}
// get data
data := socket.linesToSend[0]
if len(socket.linesToSend) > 1 {
socket.linesToSend = socket.linesToSend[1:]
} else {
socket.linesToSend = []string{}
}
// write data
if 0 < len(data) {
_, err := socket.conn.Write([]byte(data))
if err != nil {
errOut = true
fmt.Println(err.Error())
break
}
}
// check if we're closed
if socket.Closed {
socket.linesToSendMutex.Unlock()
break
}
socket.linesToSendMutex.Unlock()
}
if errOut {
// error out, bad stuff happened
break
}
}
//TODO(dan): empty socket.lineToSendExists queue
socket.conn.Close()
if !socket.Closed {
socket.Closed = true
}
}
// WriteLine writes the given line out of Socket. // WriteLine writes the given line out of Socket.
func (socket *Socket) WriteLine(line string) error { func (socket *Socket) WriteLine(line string) error {
return socket.Write(line + "\r\n") return socket.Write(line + "\r\n")

View File

@ -65,6 +65,10 @@ server:
# if you change the motd, you should move it to ircd.motd # if you change the motd, you should move it to ircd.motd
motd: oragono.motd motd: oragono.motd
# maximum length of clients' sendQ in bytes
# this should be big enough to hold /LIST and HELP replies
max-sendq: 16k
# maximum number of connections per subnet # maximum number of connections per subnet
connection-limits: connection-limits:
# whether to throttle limits or not # whether to throttle limits or not