Commits

Yann Malet  committed 76286df

Implemented the djangoAuthWebsocketHandler

  • Participants
  • Parent commits c7def91

Comments (0)

Files changed (2)

 package main
 
 import (
-	"io"
+	"code.google.com/p/go.net/websocket"
+	//"github.com/garyburd/redigo/redis"
+	"encoding/json"
+	"io/ioutil"
 	"log"
 	"net/http"
 	"net/http/httputil"
 	"net/url"
-	"code.google.com/p/go.net/websocket"
 )
 
 const (
-	STATIC_ROOT string = "/home/yml/Developments/python/bereal/var/static"
-	STATIC_URL string = "/static"
+	STATIC_ROOT    string = "/home/yml/Developments/python/bereal/var/static"
+	STATIC_URL     string = "/static"
+	DJANGO_SERVER string = "http://127.0.0.1:8000"
+	REALTIME_URL   string = "/auth_ping/"
+	GO_ADDR string = ":8080"
 )
 
-func EchoServer(ws *websocket.Conn) {
-    log.Println("got something")
-    io.Copy(ws, ws)
+// Websocket Connection
+type connection struct {
+	// The websocket connection.
+	ws *websocket.Conn
+
+	// Redis Channels the connection subscribes to
+	channels []string
+
+	// Buffered channel of outbound messages.
+	send chan string
 }
 
-func main() {
-	log.Println("start bereal go server")
-	dstUrl, err := url.Parse("http://127.0.0.1:8000")
+type PubSubInfo struct {
+	user_id  int
+	channels []string
+}
+
+
+// authenticationCheck  contact the django backend and veryfy that the user
+// that issued the websocket connection is authenticated. Django returns
+// a json string with the user_id and the list of redis pubsub channel to 
+// connect to this websocket.
+func (c *connection) authenticationCheck(authUrl string) (is_authenticated bool) {
+	is_authenticated = false
+	// Craft a request with the same cookie than the current websocket
+	client := &http.Client{}
+	req, err := http.NewRequest("GET", authUrl, nil)
+	if err != nil {
+		log.Fatal(err)
+	}
+	for _, cookie := range c.ws.Request().Cookies() {
+		req.AddCookie(cookie)
+	}
+	resp, err := client.Do(req)
 	if err != nil {
-	  log.Fatal(err)
+		log.Fatal(err)
+	}
+	if resp.StatusCode == 200 {
+		defer resp.Body.Close()
+		body, err := ioutil.ReadAll(resp.Body)
+		if err != nil {
+			log.Fatal(err)
+		}
+		log.Println("user is authenticated. pubsubinfo:", string(body))
+		var info = &PubSubInfo{}
+		json.Unmarshal(body, info)
+		c.channels = info.channels
+		is_authenticated = true
+	} else {
+		log.Println("Not authorized")
 	}
+	return
+}
 
-	http.Handle("/ws", websocket.Handler(EchoServer))
+func (c *connection) reader() {
+	log.Println("reader")
+	for {
+		var message string
+		err := websocket.Message.Receive(c.ws, &message)
+		if err != nil {
+			break
+		}
+		h.broadcast <- message
+	}
+	c.ws.Close()
+}
+
+func (c *connection) writer() {
+	log.Println("Writer")
+	for message := range c.send {
+		err := websocket.Message.Send(c.ws, message)
+		if err != nil {
+			break
+		}
+	}
+	c.ws.Close()
+}
+
+func djangoAuthWebsocketHandler(ws *websocket.Conn) {
+	log.Println("djangoAuthWebsocketHandler")
+	c := &connection{send: make(chan string, 256), ws: ws}
+	is_authenticated := c.authenticationCheck(DJANGO_SERVER + REALTIME_URL)
+	if is_authenticated {
+		h.register <- c
+		defer func() { h.unregister <- c }()
+		go c.writer()
+		c.reader()
+	}
+}
+
+// hub is used to do the booking off all open websocket connections
+type hub struct {
+	// Registered connections.
+	connections map[*connection]bool
+
+	// Inbound messages from the connections.
+	broadcast chan string
+
+	// Register requests from the connections.
+	register chan *connection
+
+	// Unregister requests from connections.
+	unregister chan *connection
+}
+
+func (h *hub) run() {
+	log.Println("Start the hub")
+	for {
+		select {
+		case c := <-h.register:
+			log.Println("Register:", c)
+			h.connections[c] = true
+		case c := <-h.unregister:
+			log.Println("Unregister", c)
+			delete(h.connections, c)
+			close(c.send)
+		case m := <-h.broadcast:
+			log.Println("broadcast", m)
+			for c := range h.connections {
+				select {
+				case c.send <- m:
+				default:
+					delete(h.connections, c)
+					close(c.send)
+					go c.ws.Close()
+				}
+			}
+		}
+	}
+}
+
+var h = hub{
+	broadcast:   make(chan string),
+	register:    make(chan *connection),
+	unregister:  make(chan *connection),
+	connections: make(map[*connection]bool),
+}
+
+func main() {
+	dstUrl, err := url.Parse(DJANGO_SERVER)
+	if err != nil {
+		log.Fatal(err)
+	}
+	go h.run()
+	http.Handle("/ws", websocket.Handler(djangoAuthWebsocketHandler))
+	// serve the static files located in STATIC_ROOT
 	http.Handle(STATIC_URL, http.FileServer(http.Dir(STATIC_ROOT)))
+	// Proxy all the requests to the django app
 	reverse_proxy := httputil.NewSingleHostReverseProxy(dstUrl)
 	http.Handle("/", reverse_proxy)
 
-	if err = http.ListenAndServe(":8080", nil); err != nil {
-	  log.Fatal(err)
+	log.Println("start bereal go server", GO_ADDR)
+	if err = http.ListenAndServe(GO_ADDR, nil); err != nil {
+		log.Fatal(err)
 	}
 }

File bereal/apps/core/views.py

+import json
+
 from django.http import HttpResponse
 from django.contrib.auth.decorators import login_required
 
+
 def ping(request):
     return HttpResponse("pong")
 
+
 @login_required
 def authenticated_ping(request):
-    return HttpResponse("authenticated pong")
+    user_id = request.user.id
+    return HttpResponse(json.dumps({"user_id": user_id,
+                                    "channels": ["chan_all",
+                                                 "chan_user_%s" % user_id]}))