Exemplo n.º 1
0
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)
	}
}
Exemplo n.º 2
0
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))
}