func WSServer(ws *websocket.Conn) { name := "" if path := ws.Request().URL.Path[len("/ws"):]; strings.HasPrefix(path, "/") && len(path) > 1 { //This is a named connection name = path[1:] } //TODO Set this connection up as a listener for system events isOpen := true if name == "" { name = GenUUIDv4() } if _, ok := sockets[name]; ok { ws.Write([]byte("Name already in use")) ws.Close() return } fmt.Println("Named Connection for:", name) n := make(chan Message, 1) defer close(n) sockets[name] = n defer delete(sockets, name) subscriptions := make(map[string]chan Message) if o, ok := objects["system"]; ok { (&o.Mutex).Lock() c := make(chan Message, 1) subscriptions["system"] = c o.Subscribers = append(o.Subscribers, c) defer removeSub("system", c) (&o.Mutex).Unlock() } else { fmt.Println("No Object found for system", "system") } //Start a go routine to listen go func() { for isOpen { var m Message err := websocket.JSON.Receive(ws, &m) if err != nil { isOpen = false fmt.Println("Receiver Closing", err) break } fmt.Println("Recieved", m.Source, m.Command, m.Path) //If this is a subscription message, make sure this ws recieves notifications if m.Source == "client" && m.Command == "subscribe" && m.Path != "" { if o, ok := objects[m.Path]; ok { (&o.Mutex).Lock() c := make(chan Message, 1) subscriptions[m.Path] = c o.Subscribers = append(o.Subscribers, c) defer removeSub(m.Path, c) (&o.Mutex).Unlock() } else { fmt.Println("No Object found", m.Path) } } //Publish will just publish the message out to the given channel if m.Source == "client" && m.Command == "publish" && m.Path != "" { if o, ok := objects[m.Path]; ok { (&o.Mutex).Lock() o.Json = m.Message (&o.Mutex).Unlock() } else { o := new(Object) o.Json = m.Message objects[m.Path] = o } sendMessageToSubs(objects[m.Path].Subscribers, m) } //Notify will send a message directly to a websocket if m.Source == "client" && m.Command == "notify" && m.Path != "" { if s, ok := sockets[m.Path]; ok { m.Source = name s <- m } } //If this is an object update message if m.Source == "client" && m.Command == "merge" && m.Path != "" { if o, ok := objects[m.Path]; ok { (&o.Mutex).Lock() o.Json, err = giu.MergePatch(o.Json, m.Message) if err != nil { fmt.Println("MergingError", m.Path, err) } (&o.Mutex).Unlock() } else { o := new(Object) o.Json = m.Message objects[m.Path] = o } signalSubs(objects[m.Path].Subscribers) } if m.Source == "client" && m.Command == "patch" && m.Path != "" { if o, ok := objects[m.Path]; ok { (&o.Mutex).Lock() fmt.Println("Before", o.Json) o.Json, err = giu.Patch(o.Json, m.Message) if err != nil { fmt.Println("PatchingError", m.Path, err) } fmt.Println("After", o.Json) (&o.Mutex).Unlock() } else { o := new(Object) o.Json, err = giu.MergePatch(o.Json, m.Message) objects[m.Path] = o } signalSubs(objects[m.Path].Subscribers) } if m.Source == "client" && m.Command == "delete" && m.Path != "" { if o, ok := objects[m.Path]; ok { (&o.Mutex).Lock() closeSubs(objects[m.Path].Subscribers) delete(objects, m.Path) (&o.Mutex).Unlock() } } } }() //Block this routine to send var count int for isOpen { select { case m, ok := <-n: if ok { m.Count = count err := websocket.JSON.Send(ws, m) if err != nil { isOpen = false fmt.Println("Sender Closing", err) break } fmt.Println("Sent", count) count++ } default: } for k, c := range subscriptions { select { case _, ok := <-c: if ok { err := websocket.JSON.Send(ws, Message{"server", "", k, objects[k].Json, count}) if err != nil { isOpen = false fmt.Println("Sender Closing", err) break } fmt.Println("Sent", count) count++ } else { c = nil delete(subscriptions, k) } default: } time.Sleep(time.Millisecond * 10) } } for k, c := range subscriptions { close(c) delete(subscriptions, k) } }
func main() { o := new(Object) o.Json = map[string]interface{}{"messages": []interface{}{}, "users": []interface{}{}} objects["default"] = o s := new(Object) objects["system"] = s http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Println("Recieved http request", r) fmt.Fprintf(w, mainpage) }) http.Handle("/ws", websocket.Handler(WSServer)) http.Handle("/ws/", websocket.Handler(WSServer)) //REST API http.HandleFunc("/publish/", func(w http.ResponseWriter, r *http.Request) { mt, _, _ := mime.ParseMediaType(r.Header.Get("Content-Type")) if r.Method == "POST" && mt == "application/json" { path := r.URL.Path[len("/publish/"):] if len(path) > 0 { m := Message{} dec := json.NewDecoder(r.Body) err := dec.Decode(&m) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } if m.Source == "client" && m.Command == "publish" && m.Path != "" { if o, ok := objects[m.Path]; ok { (&o.Mutex).Lock() o.Json = m.Message (&o.Mutex).Unlock() } else { o := new(Object) o.Json = m.Message objects[m.Path] = o } sendMessageToSubs(objects[m.Path].Subscribers, m) } return } } http.Error(w, "Only POST application/json with typed object supported", http.StatusBadRequest) }) http.HandleFunc("/notify/", func(w http.ResponseWriter, r *http.Request) { mt, _, _ := mime.ParseMediaType(r.Header.Get("Content-Type")) if r.Method == "POST" && mt == "application/json" { path := r.URL.Path[len("/notify/"):] if len(path) > 0 { m := Message{} dec := json.NewDecoder(r.Body) err := dec.Decode(&m) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } if m.Source == "client" && m.Command == "notify" && m.Path != "" { if s, ok := sockets[m.Path]; ok { m.Source = "http-request" s <- m } } return } } http.Error(w, "Only POST application/json with typed object supported", http.StatusBadRequest) }) http.HandleFunc("/sharedobject/", func(w http.ResponseWriter, r *http.Request) { path := r.URL.Path[len("/sharedobject/"):] if !(len(path) > 0) { http.Error(w, "Only POST application/json with typed object supported", http.StatusBadRequest) return } mt, _, _ := mime.ParseMediaType(r.Header.Get("Content-Type")) if r.Method == "POST" && mt == "application/json" { m := Message{} dec := json.NewDecoder(r.Body) err := dec.Decode(&m) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } if m.Source == "client" && m.Path != "" { if o, ok := objects[m.Path]; ok { (&o.Mutex).Lock() o.Json, err = giu.MergePatch(o.Json, m.Message) if err != nil { fmt.Println("MergingError", m.Path, err) } (&o.Mutex).Unlock() } else { o := new(Object) o.Json = m.Message objects[m.Path] = o } signalSubs(objects[m.Path].Subscribers) } return } else if r.Method == "PATCH" && mt == "application/json-patch+json" { m := Message{} dec := json.NewDecoder(r.Body) err := dec.Decode(&m) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } if m.Source == "client" && m.Path != "" { if o, ok := objects[m.Path]; ok { (&o.Mutex).Lock() fmt.Println("Before", o.Json) o.Json, err = giu.Patch(o.Json, m.Message) if err != nil { fmt.Println("PatchingError", m.Path, err) } fmt.Println("After", o.Json) (&o.Mutex).Unlock() } else { o := new(Object) o.Json, err = giu.MergePatch(o.Json, m.Message) objects[m.Path] = o } signalSubs(objects[m.Path].Subscribers) } } else if r.Method == "PATCH" && mt == "application/merge-patch+json" { m := Message{} dec := json.NewDecoder(r.Body) err := dec.Decode(&m) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } if m.Source == "client" && m.Path != "" { if o, ok := objects[m.Path]; ok { (&o.Mutex).Lock() o.Json, err = giu.MergePatch(o.Json, m.Message) if err != nil { fmt.Println("MergingError", m.Path, err) } (&o.Mutex).Unlock() } else { o := new(Object) o.Json = m.Message objects[m.Path] = o } signalSubs(objects[m.Path].Subscribers) } return } http.Error(w, "Only POST application/json with typed object supported", http.StatusBadRequest) }) fmt.Println(http.ListenAndServe(":8080", nil)) }