Skip to content

Commit

Permalink
Merge pull request #13 from composer22/feature/server
Browse files Browse the repository at this point in the history
Feature/server
  • Loading branch information
composer22 committed Apr 30, 2015
2 parents 729e8e5 + b64f0dc commit e89d8d1
Show file tree
Hide file tree
Showing 8 changed files with 348 additions and 148 deletions.
163 changes: 109 additions & 54 deletions server/chat_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,40 @@ package server

import (
"errors"
"fmt"
"sync"

"golang.org/x/net/websocket"
)

var (
chatManagerErrMaxRooms = errors.New("maximum number of rooms reached")
chatManagerErrRoomExists = errors.New("room already exists")
chatManagerErrRoomNotEmpty = errors.New("room is not empty")
chatManagerErrRoomNotFound = errors.New("chatroom not found")
)

// ChatManager represents a control hub of chat rooms and chatters for the server.
type ChatManager struct {
mu sync.RWMutex // Lock for update.
rooms map[string]*ChatRoom // A list of rooms on the server.
chatters map[*Chatter]bool // A list of chatters on the server.
done chan bool // Shut down chatters and rooms
maxRooms int // Maximum number of rooms allowed to be created.
maxIdle int // Maximum idle time allowed for a ws connection.
log *ChatLogger // Application log for events.
wg sync.WaitGroup // Synchronizer for manager reqq.
mu sync.Mutex // Lock for update.

done chan bool // Shut down chatters and rooms
log *ChatLogger // Application log for events.
wg sync.WaitGroup // Synchronizer for manager reqq.
}

// ChatManagerNew is a factory function that returns a new instance of a chat manager.
func ChatManagerNew(n int, mi int, cl *ChatLogger) *ChatManager {
func ChatManagerNew(maxr int, maxi int, l *ChatLogger) *ChatManager {
return &ChatManager{
rooms: make(map[string]*ChatRoom),
chatters: make(map[*Chatter]bool),
maxRooms: maxr,
maxIdle: maxi,
done: make(chan bool),
maxRooms: n,
maxIdle: mi,
log: cl,
log: l,
}
}

Expand All @@ -44,75 +51,123 @@ func (m *ChatManager) list() []string {
}

// find will find a chat room for a given name.
func (m *ChatManager) find(n string) (*ChatRoom, error) {
m.mu.Lock()
r, ok := m.rooms[n]
m.mu.Unlock()
func (m *ChatManager) find(name string) (*ChatRoom, error) {
m.mu.RLock()
rm, ok := m.rooms[name]
m.mu.RUnlock()
if !ok {
return nil, errors.New(fmt.Sprintf(`Chatroom "%s" not found.`, n))
return nil, chatManagerErrRoomNotFound
}
return r, nil
return rm, nil
}

// findCreate returns a chat room for a given name or create a new one.
func (m *ChatManager) findCreate(n string) (*ChatRoom, error) {
r, err := m.find(n)
if err != nil {
mr := m.MaxRooms()
m.mu.Lock() // cover rooms
if mr > 0 && mr == len(m.rooms) {
m.mu.Unlock()
return nil, errors.New("Maximum number of rooms reached. Cannot create new room.")
}
r = ChatRoomNew(n, m.done, m.log, &m.wg)
m.rooms[n] = r
m.wg.Add(1)
go r.Run()
func (m *ChatManager) findCreate(name string) (*ChatRoom, error) {
room, err := m.find(name)
if err == nil {
return room, nil
}
return m.createRoom(name)
}

// createRoom returns a new chat room,
func (m *ChatManager) createRoom(name string) (*ChatRoom, error) {
_, err := m.find(name)
if err == nil {
return nil, chatManagerErrRoomExists
}
maxr := m.MaxRooms()
m.mu.Lock() // cover rooms
if maxr > 0 && maxr == len(m.rooms) {
m.mu.Unlock()
return nil, chatManagerErrMaxRooms
}
return r, nil
room := ChatRoomNew(name, m.done, m.log, &m.wg)
m.rooms[name] = room
m.wg.Add(1)
go room.Run()
m.mu.Unlock()
return room, nil
}

// removeChatterAllRooms sends a broadcast to all rooms to release the chatter.
func (m *ChatManager) removeChatterAllRooms(c *Chatter) {
// renameRoom is used to change the name of a room.
func (m *ChatManager) renameRoom(oldName string, newName string) error {
m.mu.Lock()
defer m.mu.Unlock()
room, ok := m.rooms[oldName]
if !ok {
return chatManagerErrRoomNotFound
}
_, ok = m.rooms[newName]
if ok {
return chatManagerErrRoomExists
}
if !room.isEmpty() {
return chatManagerErrRoomNotEmpty
}

delete(m.rooms, oldName)
room.SetName(newName)
m.rooms[newName] = room
return nil
}

// deleteRoom stops a chat room from running and removes it from the directory.
func (m *ChatManager) deleteRoom(name string) error {
m.mu.Lock()
defer m.mu.Unlock()
room, ok := m.rooms[name]
if !ok {
return chatManagerErrRoomNotFound
}
if !room.isEmpty() {
return chatManagerErrRoomNotEmpty
}
delete(m.rooms, name)
close(room.reqq)
return nil
}

// removeChatterAllRooms sends a broadcast to all rooms to release the chatter.
func (m *ChatManager) removeChatterAllRooms(c *Chatter) {
m.mu.RLock()
defer m.mu.RUnlock()
for _, r := range m.rooms {
if q, err := ChatRequestNew(c, r.name, ChatReqTypeLeave, ""); err == nil {
r.reqq <- q
if req, err := ChatRequestNew(c, r.Name(), ChatReqTypeLeave, ""); err == nil {
r.reqq <- req
}
}
}

// getRoomStats returns statistics from each room.
func (m *ChatManager) getRoomStats() []*ChatRoomStats {
m.mu.Lock()
defer m.mu.Unlock()
var s = []*ChatRoomStats{}
m.mu.RLock()
defer m.mu.RUnlock()
var stats = []*ChatRoomStats{}
for _, r := range m.rooms {
s = append(s, r.ChatRoomStatsNew())
stats = append(stats, r.ChatRoomStatsNew())
}
return s
return stats
}

// registerChatter registers a new chatter with the chat manager.
func (m *ChatManager) registerNewChatter(ws *websocket.Conn) *Chatter {
m.mu.Lock()
defer m.mu.Unlock()
c := ChatterNew(m, ws, m.log)
m.chatters[c] = true
return c
chatr := ChatterNew(m, ws, m.log)
m.chatters[chatr] = true
return chatr
}

// getChatterStats returns statistics from all chatters
func (m *ChatManager) getChatterStats() []*ChatterStats {
m.mu.Lock()
defer m.mu.Unlock()
var s = []*ChatterStats{}
m.mu.RLock()
defer m.mu.RUnlock()
var stats = []*ChatterStats{}
for c := range m.chatters {
s = append(s, c.ChatterStatsNew())
stats = append(stats, c.ChatterStatsNew())
}
return s
return stats
}

// unregisterChatter removes a new chatter from the chat manager.
Expand All @@ -136,28 +191,28 @@ func (m *ChatManager) shutdownAll() {

// MaxRooms returns the current maximum number of rooms allowed on the server.
func (m *ChatManager) MaxRooms() int {
m.mu.Lock()
defer m.mu.Unlock()
m.mu.RLock()
defer m.mu.RUnlock()
return m.maxRooms
}

// SetMaxRooms sets the maximum number of rooms allowed on the server.
func (m *ChatManager) SetMaxRooms(mr int) {
func (m *ChatManager) SetMaxRooms(maxr int) {
m.mu.Lock()
defer m.mu.Unlock()
m.maxRooms = mr
m.maxRooms = maxr
}

// MaxIdle returns the current maximum idle time for a connection.
func (m *ChatManager) MaxIdle() int {
m.mu.Lock()
defer m.mu.Unlock()
m.mu.RLock()
defer m.mu.RUnlock()
return m.maxIdle
}

// SetMaxIdle sets the maximum idle time for a connection.
func (m *ChatManager) SetMaxIdle(mi int) {
func (m *ChatManager) SetMaxIdle(maxi int) {
m.mu.Lock()
defer m.mu.Unlock()
m.maxIdle = mi
m.maxIdle = maxi
}
10 changes: 5 additions & 5 deletions server/chat_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ type ChatRequest struct {
}

// ChatMessageNew is a factory method that returns a new chat room message instance.
func ChatRequestNew(c *Chatter, m string, rt int, n string) (*ChatRequest, error) {
if rt < ChatReqTypeSetNickname || rt > ChatReqTypeLeave {
func ChatRequestNew(c *Chatter, room string, reqt int, cont string) (*ChatRequest, error) {
if reqt < ChatReqTypeSetNickname || reqt > ChatReqTypeLeave {
return nil, errors.New("Request Type is out of range.")
}
return &ChatRequest{
Who: c,
RoomName: m,
ReqType: rt,
Content: n,
RoomName: room,
ReqType: reqt,
Content: cont,
}, nil
}

Expand Down
16 changes: 8 additions & 8 deletions server/chat_response.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ const (
const (
ChatRspTypeErrRoomMandatory = 1001 + iota
ChatRspTypeErrMaxRoomsReached
ChatRspTypeErrRoomUnavailable
ChatRspTypeErrNicknameMandatory
ChatRspTypeErrAlreadyJoined
ChatRspTypeErrNicknameUsed
ChatRspTypeErrHiddenNickname
ChatRspTypeErrNotInRoom
ChatRspTypeErrUnknownReq
)

Expand All @@ -37,16 +37,16 @@ type ChatResponse struct {
}

// ChatResponseNew is a factory method that returns a new chat room message instance.
func ChatResponseNew(m string, rt int, c string, l []string) (*ChatResponse, error) {
if rt < ChatRspTypeSetNickname ||
(rt > ChatRspTypeLeave && rt < ChatRspTypeErrRoomMandatory) ||
rt > ChatRspTypeErrUnknownReq {
func ChatResponseNew(name string, rspt int, cont string, l []string) (*ChatResponse, error) {
if rspt < ChatRspTypeSetNickname ||
(rspt > ChatRspTypeLeave && rspt < ChatRspTypeErrRoomMandatory) ||
rspt > ChatRspTypeErrUnknownReq {
return nil, errors.New("Response Type is out of range.")
}
return &ChatResponse{
RoomName: m,
RspType: rt,
Content: c,
RoomName: name,
RspType: rspt,
Content: cont,
List: l,
}, nil
}
Expand Down
Loading

0 comments on commit e89d8d1

Please sign in to comment.