You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

177 lines
4.4 KiB
Go

package chat
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
"strconv"
"sync"
"time"
)
const DefaultCacheLength = 20
type Server struct {
clients map[string]*Client
messageCache List
Name string `json:"name"`
wg sync.WaitGroup
}
type Message struct {
Text string `json:"text"`
Time int64 `json:"time"`
User string `json:"user"`
ServerId string `json:"serverId"`
}
var Servers map[string]*Server
func StartServer(name string) (s Server) {
s = Server{make(map[string]*Client), newList(DefaultCacheLength), name, sync.WaitGroup{}}
Servers[name] = &s
s.putServer()
s.loadInitMessages()
return
}
func getServer(name string) (*Server, error) {
if name == "" {
name = "root"
}
if s, ok := Servers[name]; ok {
return s, nil
}
_, exists := retrieveServerFromDb(name)
if !exists {
return nil, fmt.Errorf("server does not exist")
}
server := StartServer(name)
return &server, nil
}
func retrieveServerFromDb(name string) (s *Server, found bool) {
res, err := http.Get(DbAddress + "/server?name=" + name)
if err != nil {
fmt.Fprintln(os.Stderr, "could not reach db server", err)
return
}
if res.StatusCode != http.StatusOK {
fmt.Println("server", name, "not found on db")
return
}
if err := json.NewDecoder(res.Body).Decode(&s); err != nil {
fmt.Fprintln(os.Stderr, "error retrieving server details from db", err)
return
}
return s, true
}
func (s *Server) reportClients() (count int) {
for range s.clients {
count++
}
return
}
func (s *Server) sendMessage(message string) {
mess := Message{message, time.Now().UnixMilli(), s.Name, ""}
wm := WrappedMessage{"server", mess}
for _, client := range s.clients {
client.receivedMessages <- wm
}
}
func (s *Server) broadcastMessage(mess *Message) {
wm := WrappedMessage{"", *mess}
for _, client := range s.clients {
client.receivedMessages <- wm
}
}
func (s *Server) storeMessage(mess *Message) {
s.messageCache.add(&ListNode{nil, *mess})
s.wg.Done()
jsonData, ok := encodeJson(mess)
if !ok {
return
}
if res, err := http.Post(DbAddress+"/message?name="+s.Name, "application/json", bytes.NewBuffer(jsonData)); err != nil {
fmt.Fprintln(os.Stderr, "db server post error", err.Error())
} else {
body, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Fprintln(os.Stderr, "error reading response body:", err)
}
if res.StatusCode != http.StatusOK {
fmt.Println("could not store message", string(body))
}
}
}
func (s *Server) loadInitMessages() {
status, _, messages := s.loadMessages(0, DefaultCacheLength)
if status != http.StatusOK {
fmt.Fprintln(os.Stderr, "Could not load initial messages from db")
return
}
s.messageCache.fromMessageSlice(messages)
}
func (s *Server) loadMessages(offset, count int) (status int, message string, messages []Message) {
if res, err := http.Get(DbAddress + "/message?name=" + s.Name + "&offset=" + strconv.Itoa(offset) + "&count=" + strconv.Itoa(count)); err != nil {
fmt.Fprintln(os.Stderr, "db server get messages error", err.Error())
} else {
body, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Fprintln(os.Stderr, "error reading response body:", err)
}
if res.StatusCode != http.StatusOK {
fmt.Println("load messages error", string(body))
return res.StatusCode, string(body), nil
} else {
messages = make([]Message, 0)
if err := json.Unmarshal(body, &messages); err != nil {
fmt.Fprintln(os.Stderr, "Error unmarshalling messages from db", err.Error())
return http.StatusInternalServerError, "Error unmarshalling messages from db " + err.Error(), nil
}
}
}
return http.StatusOK, "", messages
}
func (s *Server) putServer() {
jsonData, ok := encodeJson(&s)
if !ok {
fmt.Fprintln(os.Stderr, "error encoding server")
return
}
res, err := http.Post(DbAddress+"/server", "application/json", bytes.NewBuffer(jsonData))
if err != nil {
fmt.Fprintln(os.Stderr, "POST server to db error:", err.Error())
return
}
if res.StatusCode == http.StatusOK {
fmt.Println("server", s.Name, "already in db")
var temp Server
if err := json.NewDecoder(res.Body).Decode(&temp); err != nil {
fmt.Println("error updating local server details with those from db")
return
}
//TODO: update details which get pulled from db rather than on creation
return
}
if res.StatusCode == http.StatusCreated {
fmt.Println("server", s.Name, "added to db")
return
}
}