forked from Oooska/ircwebchat
/
client.go
147 lines (126 loc) · 3.82 KB
/
client.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package ircwebchat
import (
"fmt"
"log"
"github.com/oooska/irc"
)
/*IRCClient defines the behavior of a browser-based client.
DONE: Websocket client (websocket.go)
TODO: Longpoll client
*/
type ircClient interface {
SendMessage(irc.Message) error
ReceiveMessage() (irc.Message, error)
Close()
}
//ircManager takes the connection to the IRC server and then coordinates the
//communication between the irc server, and the active IRCClients
func ircManager(ircConn irc.IRCConn, newClients chan *ircClient) {
fmt.Println("*** Entering ircManager ***")
defer fmt.Println("*** Leaving ircManager ***")
var clients []*ircClient
fromServer := make(chan irc.Message)
fromClient := make(chan irc.Message)
errChan := make(chan error)
quitChan := make(chan bool)
//Listen for incoming messages form server
go ircServerListener(ircConn, fromServer, errChan, quitChan)
for {
select {
case msg := <-fromServer:
//Log it,
log.Println(msg)
//Repsond if it's a ping
if len(msg) >= 4 && msg[0:4] == "PING" {
var end string = msg[4:].String()
ircConn.Write(irc.NewMessage("PONG" + end))
//break
}
//...and send it to all clients
for k := 0; k < len(clients); k++ {
client := *clients[k]
err := client.SendMessage(msg)
if err != nil {
stopClientListener(&client)
client.Close()
clients = deleteNthItem(clients, k)
k-- //Account for socket deletion in slice
fmt.Println("*** Disconnected irc Client. ", len(clients), "remaining.")
}
}
//Received a message from the server
case msg := <-fromClient:
err := ircConn.Write(msg)
if err != nil {
fmt.Println("Error writing to server: ", err)
}
//A new client has connected
case client := <-newClients:
clients = append(clients, client)
startClientListener(client, fromClient)
fmt.Println("*** Accepted the ", len(clients), " client connection ***")
}
}
quitChan <- true
}
func deleteNthItem(a []*ircClient, n int) []*ircClient {
a, a[len(a)-1] = append(a[:n], a[n+1:]...), nil
return a
}
//ircServerListener continuallyu listens for messages from the IRC server.
//When one is receives, it sends the message into the msg channel.
func ircServerListener(ircConn irc.IRCConn, msgChan chan<- irc.Message, errChan chan<- error, quitChan <-chan bool) {
fmt.Println("*** Entering ircListenerClient ***")
defer fmt.Println("*** Leaving ircListenerClient ***")
for {
select {
case <-quitChan:
return
default:
msg, err := ircConn.Read()
if err != nil {
errChan <- err
return
}
msgChan <- msg
}
}
}
//ircCLientListener will indefinitely listen for input from an ircClient, putting
//it into the supplied channel, where it will be sent on to the server
func ircClientListener(client ircClient, to_server chan<- irc.Message, quit <-chan bool) {
for {
select {
case <-quit:
fmt.Println("Exiting ircClientListener")
return
default:
msg, err := client.ReceiveMessage()
if err != nil {
//fmt.Println(fmt.Sprintf("ircClientListener %v, error: %v", client, err))
//time.Sleep(1000 * time.Millisecond)
return
} else {
to_server <- msg
}
}
}
}
//startClientListener and stopClientListener start and stop the ircClientListener
//for the particular ircClient
//Keep track of the quit bool channel for each client listener
//TODO Find a better way to implement this
var clientListenerMap map[*ircClient]chan bool = make(map[*ircClient]chan bool)
func startClientListener(client *ircClient, to_server chan<- irc.Message) {
quitCh := make(chan bool, 1)
clientListenerMap[client] = quitCh
go ircClientListener(*client, to_server, quitCh)
}
func stopClientListener(client *ircClient) {
//fmt.Println(fmt.Sprintf("Telling client %+v listener to quit...", client))
ch, ok := clientListenerMap[client]
if ok {
ch <- true
//fmt.Println("... Successfully sent quit notice")
}
}