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 } }