Untitled
golang
21 days ago
4.0 kB
1
Indexable
Never
package main import ( "flag" "log" "net/http" "path/filepath" "sync" "text/template" "encoding/json" "github.com/gorilla/websocket" "github.com/google/uuid" ) // templ represents a single template type templateHandler struct { once sync.Once filename string templ *template.Template } var clientNames = make(map[*websocket.Conn]string) // ServeHTTP handles the HTTP request. func (t *templateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { t.once.Do(func() { t.templ = template.Must(template.ParseFiles(filepath.Join("templates", t.filename))) }) t.templ.Execute(w, r) } func main() { var addr = flag.String("addr", ":8080", "The addr of the application.") flag.Parse() // parse the flags r := newRoom() http.Handle("/", &templateHandler{filename: "chat.html"}) http.HandleFunc("/register", registerHandler) http.Handle("/room", r) // get the room going go r.run() // start the web server log.Println("Starting web server on", *addr) if err := http.ListenAndServe(*addr, nil); err != nil { log.Fatal("ListenAndServe:", err) } } type room struct { clients map[*client]bool join chan *client leave chan *client forward chan []byte } type client struct { info clientInfo receive chan []byte room *room } func newRoom() *room { return &room{ forward: make(chan []byte), join: make(chan *client), leave: make(chan *client), clients: make(map[*client]bool), } } var messages []string var globalName string var clientsMap = make(map[string]clientInfo) func registerHandler(w http.ResponseWriter, r *http.Request) { err := r.ParseForm() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } globalName = r.FormValue("name") http.Redirect(w, r, "/", http.StatusFound) } func (r *room) run() { for { select { case client := <-r.join: r.clients[client] = true for _, msg := range messages { client.receive <- []byte(msg) } case client := <-r.leave: delete(r.clients, client) close(client.receive) case msg := <-r.forward: for client := range r.clients { client.receive <- msg } messages = append(messages, string(msg)) } } } const ( socketBufferSize = 1024 messageBufferSize = 256 ) type clientInfo struct { id string conn *websocket.Conn name string } var upgrader = &websocket.Upgrader{ReadBufferSize: socketBufferSize, WriteBufferSize: messageBufferSize} func (r *room) ServeHTTP(w http.ResponseWriter, req *http.Request) { id := uuid.New().String() socket, err := upgrader.Upgrade(w, req, nil) if err != nil { return } if globalName == "" { globalName = "Unknown" } client := &client{ info: clientInfo{ id: id, conn: socket, name: globalName, }, receive: make(chan []byte, messageBufferSize), room: r, } globalName = "" err = client.info.conn.WriteMessage(websocket.TextMessage, []byte("You are connected as: "+client.info.name)) if err != nil { log.Println("Error sending name message:", err) } clientsMap[id] = client.info r.join <- client defer func() { // When client leaves, remove it from the clientsMap delete(clientsMap, id) r.leave <- client }() go client.write() client.read() } type chatMessage struct { Name string `json:"name"` Message string `json:"message"` } func (c *client) read() { defer c.info.conn.Close() for { _, msg, err := c.info.conn.ReadMessage() if err != nil { return } msgObj := chatMessage{ Name: c.info.name, Message: string(msg), } jsonData, err := json.Marshal(msgObj) if err != nil { return } c.room.forward <- jsonData } } func (c *client) write() { defer c.info.conn.Close() for msg := range c.receive { err := c.info.conn.WriteMessage(websocket.TextMessage, msg) if err != nil { return } } }