3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-12-22 18:52:41 +01:00

move command parsing and hostname lookups into the socket routine

This commit is contained in:
Jeremy Latt 2014-02-23 17:04:24 -08:00
parent ff5656fdb4
commit 0bf968e19e
5 changed files with 49 additions and 140 deletions

View File

@ -4,7 +4,6 @@ import (
"fmt"
"log"
"net"
"strings"
"time"
)
@ -12,18 +11,6 @@ func IsNickname(nick string) bool {
return NicknameExpr.MatchString(nick)
}
type HostnameLookup struct {
client *Client
hostname string
}
func NewHostnameLookup(client *Client, ipAddr string) *HostnameLookup {
return &HostnameLookup{
client: client,
hostname: LookupHostname(ipAddr),
}
}
type Client struct {
atime time.Time
awayMessage string
@ -35,12 +22,10 @@ type Client struct {
hostname string
idleTimer *time.Timer
loginTimer *time.Timer
lookups chan string
nick string
phase Phase
quitTimer *time.Timer
realname string
replies chan string
server *Server
socket *Socket
username string
@ -53,83 +38,15 @@ func NewClient(server *Server, conn net.Conn) *Client {
channels: make(ChannelSet),
ctime: now,
flags: make(map[UserMode]bool),
lookups: make(chan string),
phase: server.InitPhase(),
server: server,
socket: NewSocket(conn),
replies: make(chan string, 16),
}
client.socket = NewSocket(conn, client, server.commands)
client.loginTimer = time.AfterFunc(LOGIN_TIMEOUT, client.connectionTimeout)
go client.LookupHostname(IPString(conn.RemoteAddr()))
go client.readCommands()
go client.writeReplies()
return client
}
//
// socket read gorountine
//
func (client *Client) readCommands() {
done := false
for !done {
select {
case ipAddr := <-client.lookups:
client.server.hostnames <- NewHostnameLookup(client, ipAddr)
case line := <-client.socket.Read():
if line == EOF {
done = true
break
}
msg, err := ParseCommand(line)
if err != nil {
switch err {
case NotEnoughArgsError:
parts := strings.SplitN(line, " ", 2)
client.ErrNeedMoreParams(parts[0])
}
continue
}
msg.SetClient(client)
client.server.commands <- msg
}
}
client.connectionClosed()
}
func (client *Client) LookupHostname(ipAddr string) {
client.lookups <- ipAddr
}
func (client *Client) connectionClosed() {
msg := &QuitCommand{
message: "connection closed",
}
msg.SetClient(client)
client.server.commands <- msg
}
//
// reply writing goroutine
//
func (client *Client) writeReplies() {
for reply := range client.replies {
if reply == EOF {
break
}
if client.socket.Write(reply) != nil {
break
}
}
client.socket.Close()
}
//
// idle timer goroutine
//
@ -143,11 +60,7 @@ func (client *Client) connectionIdle() {
//
func (client *Client) connectionTimeout() {
msg := &QuitCommand{
message: "connection timeout",
}
msg.SetClient(client)
client.server.commands <- msg
client.server.timeout <- client
}
//
@ -214,10 +127,10 @@ func (client *Client) destroy() {
client.quitTimer = nil
}
client.lookups = nil
client.replies = nil
client.server = nil
client.socket.Close()
client.socket = nil
client.server = nil
if DEBUG_CLIENT {
log.Printf("%s: destroyed", client)
@ -307,10 +220,7 @@ func (client *Client) ChangeNickname(nickname string) {
}
func (client *Client) Reply(reply string) {
if client.hasQuit {
return
}
client.replies <- reply
client.socket.Write(reply)
}
func (client *Client) Quit(message string) {
@ -319,7 +229,6 @@ func (client *Client) Quit(message string) {
}
client.Reply(RplError("connection closed"))
client.Reply(EOF)
client.hasQuit = true
friends := client.Friends()

View File

@ -56,8 +56,8 @@ func (command *BaseCommand) Client() *Client {
return command.client
}
func (command *BaseCommand) SetClient(c *Client) {
command.client = c
func (command *BaseCommand) SetClient(client *Client) {
command.client = client
}
func (command *BaseCommand) Code() StringCode {
@ -68,10 +68,6 @@ func (command *BaseCommand) SetCode(code StringCode) {
command.code = code
}
func (command *BaseCommand) Source() Identifier {
return command.Client()
}
func ParseCommand(line string) (cmd editableCommand, err error) {
code, args := parseLine(line)
constructor := parseCommandFuncs[code]
@ -362,7 +358,7 @@ type PartCommand struct {
func (cmd *PartCommand) Message() string {
if cmd.message == "" {
return cmd.Source().Nick()
return cmd.Client().Nick()
}
return cmd.message
}
@ -701,6 +697,7 @@ type ProxyCommand struct {
destIP string
sourcePort string
destPort string
hostname string // looked up in socket thread
}
func (msg *ProxyCommand) String() string {
@ -717,6 +714,7 @@ func NewProxyCommand(args []string) (editableCommand, error) {
destIP: args[2],
sourcePort: args[3],
destPort: args[4],
hostname: LookupHostname(args[1]),
}, nil
}
@ -801,7 +799,7 @@ type KickCommand struct {
func (msg *KickCommand) Comment() string {
if msg.comment == "" {
return msg.Source().Nick()
return msg.Client().Nick()
}
return msg.comment
}

View File

@ -21,13 +21,13 @@ type Server struct {
clients ClientNameMap
commands chan Command
ctime time.Time
hostnames chan *HostnameLookup
idle chan *Client
motdFile string
name string
newConns chan net.Conn
operators map[string]string
password string
timeout chan *Client
}
func NewServer(config *Config) *Server {
@ -36,13 +36,13 @@ func NewServer(config *Config) *Server {
clients: make(ClientNameMap),
commands: make(chan Command, 16),
ctime: time.Now(),
hostnames: make(chan *HostnameLookup, 16),
idle: make(chan *Client, 16),
motdFile: config.MOTD,
name: config.Name,
newConns: make(chan net.Conn, 16),
operators: make(map[string]string),
password: config.Password,
timeout: make(chan *Client),
}
for _, opConf := range config.Operators {
@ -62,16 +62,12 @@ func (server *Server) ReceiveCommands() {
case conn := <-server.newConns:
NewClient(server, conn)
case lookup := <-server.hostnames:
if DEBUG_SERVER {
log.Printf("%s setting hostname of %s to %s",
server, lookup.client, lookup.hostname)
}
lookup.client.hostname = lookup.hostname
case client := <-server.idle:
client.Idle()
case client := <-server.timeout:
client.Quit("connection timeout")
case cmd := <-server.commands:
client := cmd.Client()
if DEBUG_SERVER {
@ -255,7 +251,7 @@ func (s *Server) Nick() string {
//
func (msg *ProxyCommand) HandleAuthServer(server *Server) {
go msg.Client().LookupHostname(msg.sourceIP)
msg.Client().hostname = msg.hostname
}
func (msg *CapCommand) HandleAuthServer(server *Server) {
@ -267,7 +263,7 @@ func (m *PassCommand) HandleAuthServer(s *Server) {
if s.password != m.password {
client.ErrPasswdMismatch()
client.socket.Close()
client.Quit("bad password")
return
}
@ -282,6 +278,10 @@ func (msg *QuitCommand) HandleAuthServer(server *Server) {
// registration commands
//
func (msg *ProxyCommand) HandleRegServer(server *Server) {
msg.Client().hostname = msg.hostname
}
func (msg *CapCommand) HandleRegServer(server *Server) {
// TODO
}

View File

@ -17,20 +17,20 @@ const (
type Socket struct {
closed bool
conn net.Conn
read chan string
reader *bufio.Reader
client *Client
writer *bufio.Writer
}
func NewSocket(conn net.Conn) *Socket {
func NewSocket(conn net.Conn, client *Client, commands chan<- Command) *Socket {
socket := &Socket{
conn: conn,
read: make(chan string),
reader: bufio.NewReader(conn),
client: client,
writer: bufio.NewWriter(conn),
}
go socket.readLines()
go socket.readLines(commands)
return socket
}
@ -50,13 +50,18 @@ func (socket *Socket) Close() {
}
}
func (socket *Socket) readLines() {
func (socket *Socket) readLines(commands chan<- Command) {
hostnameLookup := &ProxyCommand{
hostname: AddrLookupHostname(socket.conn.RemoteAddr()),
}
hostnameLookup.SetClient(socket.client)
commands <- hostnameLookup
for {
line, err := socket.reader.ReadString('\n')
if socket.isError(err, R) {
break
}
line = strings.TrimRight(line, "\r\n")
if len(line) == 0 {
continue
@ -64,29 +69,27 @@ func (socket *Socket) readLines() {
if DEBUG_NET {
log.Printf("%s → %s", socket, line)
}
socket.read <- line
msg, err := ParseCommand(line)
if err != nil {
// TODO error messaging to client
continue
}
close(socket.read)
msg.SetClient(socket.client)
commands <- msg
}
func (socket *Socket) Read() <-chan string {
return socket.read
msg := &QuitCommand{
message: "connection closed",
}
msg.SetClient(socket.client)
commands <- msg
}
func (socket *Socket) Write(lines ...string) (err error) {
func (socket *Socket) Write(line string) (err error) {
if socket.closed {
return io.EOF
}
for _, line := range lines {
err = socket.WriteLine(line)
if err != nil {
break
}
}
return
}
func (socket *Socket) WriteLine(line string) (err error) {
if _, err = socket.writer.WriteString(line); socket.isError(err, W) {
return
}

View File

@ -176,7 +176,6 @@ type Replier interface {
type Command interface {
Code() StringCode
Client() *Client
Source() Identifier
}
type ServerCommand interface {