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" "fmt"
"log" "log"
"net" "net"
"strings"
"time" "time"
) )
@ -12,18 +11,6 @@ func IsNickname(nick string) bool {
return NicknameExpr.MatchString(nick) 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 { type Client struct {
atime time.Time atime time.Time
awayMessage string awayMessage string
@ -35,12 +22,10 @@ type Client struct {
hostname string hostname string
idleTimer *time.Timer idleTimer *time.Timer
loginTimer *time.Timer loginTimer *time.Timer
lookups chan string
nick string nick string
phase Phase phase Phase
quitTimer *time.Timer quitTimer *time.Timer
realname string realname string
replies chan string
server *Server server *Server
socket *Socket socket *Socket
username string username string
@ -53,83 +38,15 @@ func NewClient(server *Server, conn net.Conn) *Client {
channels: make(ChannelSet), channels: make(ChannelSet),
ctime: now, ctime: now,
flags: make(map[UserMode]bool), flags: make(map[UserMode]bool),
lookups: make(chan string),
phase: server.InitPhase(), phase: server.InitPhase(),
server: server, 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) client.loginTimer = time.AfterFunc(LOGIN_TIMEOUT, client.connectionTimeout)
go client.LookupHostname(IPString(conn.RemoteAddr()))
go client.readCommands()
go client.writeReplies()
return client 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 // idle timer goroutine
// //
@ -143,11 +60,7 @@ func (client *Client) connectionIdle() {
// //
func (client *Client) connectionTimeout() { func (client *Client) connectionTimeout() {
msg := &QuitCommand{ client.server.timeout <- client
message: "connection timeout",
}
msg.SetClient(client)
client.server.commands <- msg
} }
// //
@ -214,10 +127,10 @@ func (client *Client) destroy() {
client.quitTimer = nil client.quitTimer = nil
} }
client.lookups = nil client.socket.Close()
client.replies = nil
client.server = nil
client.socket = nil client.socket = nil
client.server = nil
if DEBUG_CLIENT { if DEBUG_CLIENT {
log.Printf("%s: destroyed", client) log.Printf("%s: destroyed", client)
@ -307,10 +220,7 @@ func (client *Client) ChangeNickname(nickname string) {
} }
func (client *Client) Reply(reply string) { func (client *Client) Reply(reply string) {
if client.hasQuit { client.socket.Write(reply)
return
}
client.replies <- reply
} }
func (client *Client) Quit(message string) { func (client *Client) Quit(message string) {
@ -319,7 +229,6 @@ func (client *Client) Quit(message string) {
} }
client.Reply(RplError("connection closed")) client.Reply(RplError("connection closed"))
client.Reply(EOF)
client.hasQuit = true client.hasQuit = true
friends := client.Friends() friends := client.Friends()

View File

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

View File

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

View File

@ -17,20 +17,20 @@ const (
type Socket struct { type Socket struct {
closed bool closed bool
conn net.Conn conn net.Conn
read chan string
reader *bufio.Reader reader *bufio.Reader
client *Client
writer *bufio.Writer writer *bufio.Writer
} }
func NewSocket(conn net.Conn) *Socket { func NewSocket(conn net.Conn, client *Client, commands chan<- Command) *Socket {
socket := &Socket{ socket := &Socket{
conn: conn, conn: conn,
read: make(chan string),
reader: bufio.NewReader(conn), reader: bufio.NewReader(conn),
client: client,
writer: bufio.NewWriter(conn), writer: bufio.NewWriter(conn),
} }
go socket.readLines() go socket.readLines(commands)
return socket 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 { for {
line, err := socket.reader.ReadString('\n') line, err := socket.reader.ReadString('\n')
if socket.isError(err, R) { if socket.isError(err, R) {
break break
} }
line = strings.TrimRight(line, "\r\n") line = strings.TrimRight(line, "\r\n")
if len(line) == 0 { if len(line) == 0 {
continue continue
@ -64,29 +69,27 @@ func (socket *Socket) readLines() {
if DEBUG_NET { if DEBUG_NET {
log.Printf("%s → %s", socket, line) 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
}
msg := &QuitCommand{
message: "connection closed",
}
msg.SetClient(socket.client)
commands <- msg
} }
func (socket *Socket) Read() <-chan string { func (socket *Socket) Write(line string) (err error) {
return socket.read
}
func (socket *Socket) Write(lines ...string) (err error) {
if socket.closed { if socket.closed {
return io.EOF 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) { if _, err = socket.writer.WriteString(line); socket.isError(err, W) {
return return
} }

View File

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