diff --git a/examples/websocket/main.go b/examples/websocket/main.go new file mode 100644 index 00000000..c7035a12 --- /dev/null +++ b/examples/websocket/main.go @@ -0,0 +1,141 @@ +package main + +import ( + "fmt" + "log" + "net/http" + + "github.com/gorilla/websocket" + + "github.com/anthdm/hollywood/actor" +) + +type server struct { + ctx *actor.Context + sessions map[string]*actor.PID +} + +func newServer() actor.Receiver { + return &server{ + sessions: make(map[string]*actor.PID), + } +} + +func (s *server) Receive(ctx *actor.Context) { + switch msg := ctx.Message().(type) { + case actor.Started: + log.Println("Server started on port 8080") + s.serve() + s.ctx = ctx + _ = msg + case message: + s.broadcast(ctx.Sender(), msg) + default: + fmt.Printf("Server received %v\n", msg) + } +} + +func (s *server) serve() { + go func() { + http.HandleFunc("/ws", s.handleWebsocket) + http.ListenAndServe(":8080", nil) + }() +} + +func (s *server) handleWebsocket(w http.ResponseWriter, r *http.Request) { + fmt.Println("New connection") + upgrader := websocket.Upgrader{} + conn, err := upgrader.Upgrade(w, r, nil) + if err != nil { + fmt.Println(err) + return + } + + username := r.URL.Query().Get("username") + pid := s.ctx.SpawnChild(newUser(username, conn, s.ctx.PID()), username) + + s.sessions[pid.GetID()] = pid +} + +func (s *server) broadcast(sender *actor.PID, msg message) { + for _, pid := range s.sessions { + if !pid.Equals(sender) { + s.ctx.Send(pid, msg) + } + } +} + +type message struct { + Content string `json:"content"` + Owner string `json:"owner"` +} + +type user struct { + conn *websocket.Conn + ctx *actor.Context + serverPid *actor.PID + Name string +} + +func newUser(name string, conn *websocket.Conn, serverPid *actor.PID) actor.Producer { + return func() actor.Receiver { + return &user{ + Name: name, + conn: conn, + serverPid: serverPid, + } + } +} + +func (u *user) Receive(ctx *actor.Context) { + switch msg := ctx.Message().(type) { + case actor.Started: + u.ctx = ctx + go u.listen() + case message: + u.send(&msg) + case actor.Stopped: + _ = msg + u.conn.Close() + default: + fmt.Printf("%s received %v\n", u.Name, msg) + } +} + +func (u *user) listen() { + var msg message + for { + if err := u.conn.ReadJSON(&msg); err != nil { + fmt.Printf("Error reading message: %v\n", err) + return + } + + msg.Owner = u.Name + + go u.handleMessage(msg) + } +} + +func (u *user) handleMessage(msg message) { + switch msg.Content { + case "exit": // Send exit message to stop the actor and close the websocket connection + u.ctx.Engine().Poison(u.ctx.PID()) + default: + // Note that this is the server pid, so it will broadcast the message + u.ctx.Send(u.serverPid, msg) + } +} + +func (u *user) send(msg *message) { + if err := u.conn.WriteJSON(msg); err != nil { + fmt.Printf("Error writing message: %v\n", err) + return + } +} + +func main() { + engine := actor.NewEngine() + engine.Spawn(newServer, "server") + + select {} +} diff --git a/go.mod b/go.mod index 2afec8b2..e4939e74 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,9 @@ module github.com/anthdm/hollywood go 1.19 require ( + github.com/gorilla/websocket v1.5.0 github.com/planetscale/vtprotobuf v0.4.0 + github.com/prometheus/client_golang v1.15.0 github.com/sirupsen/logrus v1.9.0 github.com/stretchr/testify v1.8.1 google.golang.org/grpc v1.53.0 @@ -15,13 +17,12 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/klauspost/cpuid/v2 v2.0.9 // indirect - github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/prometheus/client_golang v1.15.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.42.0 // indirect github.com/prometheus/procfs v0.9.0 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + github.com/rogpeppe/go-internal v1.9.0 // indirect ) require ( diff --git a/go.sum b/go.sum index c1fe271f..91f10138 100644 --- a/go.sum +++ b/go.sum @@ -13,18 +13,15 @@ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/planetscale/vtprotobuf v0.4.0 h1:NEI+g4woRaAZgeZ3sAvbtyvMBRjIv5kE7EWYQ8m4JwY= github.com/planetscale/vtprotobuf v0.4.0/go.mod h1:wm1N3qk9G/4+VM1WhpkLbvY/d8+0PbwYYpP5P5VhTks= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -74,7 +71,6 @@ google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cn google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=