Untitled

mail@pastecode.io avatarunknown
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
		}
	}
}