Merge branch 'websocket_rewrite'
This commit is contained in:
@@ -9,7 +9,6 @@ import (
|
|||||||
|
|
||||||
"golang.org/x/crypto/acme/autocert"
|
"golang.org/x/crypto/acme/autocert"
|
||||||
|
|
||||||
"github.com/googollee/go-socket.io"
|
|
||||||
gh "github.com/gorilla/handlers"
|
gh "github.com/gorilla/handlers"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/play-with-docker/play-with-docker/config"
|
"github.com/play-with-docker/play-with-docker/config"
|
||||||
@@ -21,7 +20,6 @@ import (
|
|||||||
|
|
||||||
var core pwd.PWDApi
|
var core pwd.PWDApi
|
||||||
var e event.EventApi
|
var e event.EventApi
|
||||||
var ws *socketio.Server
|
|
||||||
|
|
||||||
type HandlerExtender func(h *mux.Router)
|
type HandlerExtender func(h *mux.Router)
|
||||||
|
|
||||||
@@ -31,15 +29,6 @@ func Bootstrap(c pwd.PWDApi, ev event.EventApi) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Register(extend HandlerExtender) {
|
func Register(extend HandlerExtender) {
|
||||||
server, err := socketio.NewServer(nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
server.On("connection", WS)
|
|
||||||
server.On("error", WSError)
|
|
||||||
|
|
||||||
RegisterEvents(server)
|
|
||||||
|
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
corsRouter := mux.NewRouter()
|
corsRouter := mux.NewRouter()
|
||||||
|
|
||||||
@@ -71,7 +60,7 @@ func Register(extend HandlerExtender) {
|
|||||||
http.ServeFile(rw, r, "www/sdk.js")
|
http.ServeFile(rw, r, "www/sdk.js")
|
||||||
})
|
})
|
||||||
|
|
||||||
corsRouter.Handle("/sessions/{sessionId}/ws/", server)
|
corsRouter.HandleFunc("/sessions/{sessionId}/ws/", WSH)
|
||||||
r.Handle("/metrics", promhttp.Handler())
|
r.Handle("/metrics", promhttp.Handler())
|
||||||
|
|
||||||
// Generic routes
|
// Generic routes
|
||||||
@@ -129,12 +118,3 @@ func Register(extend HandlerExtender) {
|
|||||||
log.Fatal(httpServer.ListenAndServe())
|
log.Fatal(httpServer.ListenAndServe())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func RegisterEvents(s *socketio.Server) {
|
|
||||||
ws = s
|
|
||||||
e.OnAny(broadcastEvent)
|
|
||||||
}
|
|
||||||
|
|
||||||
func broadcastEvent(eventType event.EventType, sessionId string, args ...interface{}) {
|
|
||||||
ws.BroadcastTo(sessionId, eventType.String(), args...)
|
|
||||||
}
|
|
||||||
|
|||||||
154
handlers/ws.go
154
handlers/ws.go
@@ -1,14 +1,140 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/googollee/go-socket.io"
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
"github.com/play-with-docker/play-with-docker/event"
|
||||||
|
"github.com/twinj/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
func WS(so socketio.Socket) {
|
var upgrader = websocket.Upgrader{
|
||||||
|
CheckOrigin: func(r *http.Request) bool { return true },
|
||||||
|
}
|
||||||
|
|
||||||
|
type message struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Args []interface{} `json:"args"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type socket struct {
|
||||||
|
c *websocket.Conn
|
||||||
|
mx sync.Mutex
|
||||||
|
listeners map[string][]func(args ...interface{})
|
||||||
|
r *http.Request
|
||||||
|
id string
|
||||||
|
closed bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSocket(r *http.Request, c *websocket.Conn) *socket {
|
||||||
|
return &socket{
|
||||||
|
c: c,
|
||||||
|
listeners: map[string][]func(args ...interface{}){},
|
||||||
|
r: r,
|
||||||
|
id: uuid.NewV4().String(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *socket) Id() string {
|
||||||
|
return s.id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *socket) Request() *http.Request {
|
||||||
|
return s.r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *socket) Close() {
|
||||||
|
s.closed = true
|
||||||
|
s.onMessage(message{Name: "close"})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *socket) process() {
|
||||||
|
defer s.Close()
|
||||||
|
for {
|
||||||
|
mt, m, err := s.c.ReadMessage()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error reading message from websocket. Got: %v\n", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if mt != websocket.TextMessage {
|
||||||
|
log.Printf("Received websocket message, but it is not a text message.\n")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
var msg message
|
||||||
|
if err := json.Unmarshal(m, &msg); err != nil {
|
||||||
|
log.Printf("Cannot unmarshal message received from websocket. Got: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.onMessage(msg)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *socket) onMessage(msg message) {
|
||||||
|
s.mx.Lock()
|
||||||
|
defer s.mx.Unlock()
|
||||||
|
|
||||||
|
cbs, found := s.listeners[msg.Name]
|
||||||
|
if !found {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, cb := range cbs {
|
||||||
|
go cb(msg.Args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *socket) Emit(ev string, args ...interface{}) {
|
||||||
|
s.mx.Lock()
|
||||||
|
defer s.mx.Unlock()
|
||||||
|
|
||||||
|
if s.closed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
m := message{Name: ev, Args: args}
|
||||||
|
b, err := json.Marshal(m)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Cannot marshal event to json. Got: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := s.c.WriteMessage(websocket.TextMessage, b); err != nil {
|
||||||
|
log.Printf("Cannot write event to websocket connection. Got: %v\n", err)
|
||||||
|
s.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *socket) On(ev string, cb func(args ...interface{})) {
|
||||||
|
s.mx.Lock()
|
||||||
|
defer s.mx.Unlock()
|
||||||
|
listeners, found := s.listeners[ev]
|
||||||
|
if !found {
|
||||||
|
listeners = []func(args ...interface{}){}
|
||||||
|
}
|
||||||
|
listeners = append(listeners, cb)
|
||||||
|
s.listeners[ev] = listeners
|
||||||
|
}
|
||||||
|
|
||||||
|
func WSH(w http.ResponseWriter, r *http.Request) {
|
||||||
|
c, err := upgrader.Upgrade(w, r, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Print("upgrade:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer c.Close()
|
||||||
|
|
||||||
|
s := newSocket(r, c)
|
||||||
|
ws(s)
|
||||||
|
s.process()
|
||||||
|
}
|
||||||
|
|
||||||
|
func ws(so *socket) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
fmt.Println("Recovered from ", r)
|
fmt.Println("Recovered from ", r)
|
||||||
@@ -27,8 +153,6 @@ func WS(so socketio.Socket) {
|
|||||||
|
|
||||||
client := core.ClientNew(so.Id(), session)
|
client := core.ClientNew(so.Id(), session)
|
||||||
|
|
||||||
so.Join(session.Id)
|
|
||||||
|
|
||||||
m, err := NewManager(session)
|
m, err := NewManager(session)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error creating terminal manager. Got: %v", err)
|
log.Printf("Error creating terminal manager. Got: %v", err)
|
||||||
@@ -48,26 +172,32 @@ func WS(so socketio.Socket) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
so.On("session close", func() {
|
so.On("session close", func(args ...interface{}) {
|
||||||
m.Close()
|
m.Close()
|
||||||
core.SessionClose(session)
|
core.SessionClose(session)
|
||||||
})
|
})
|
||||||
|
|
||||||
so.On("instance terminal in", func(name, data string) {
|
so.On("instance terminal in", func(args ...interface{}) {
|
||||||
|
name := args[0].(string)
|
||||||
|
data := args[1].(string)
|
||||||
m.Send(name, []byte(data))
|
m.Send(name, []byte(data))
|
||||||
})
|
})
|
||||||
|
|
||||||
so.On("instance viewport resize", func(cols, rows uint) {
|
so.On("instance viewport resize", func(args ...interface{}) {
|
||||||
// User resized his viewport
|
// User resized his viewport
|
||||||
core.ClientResizeViewPort(client, cols, rows)
|
cols := args[0].(float64)
|
||||||
|
rows := args[1].(float64)
|
||||||
|
core.ClientResizeViewPort(client, uint(cols), uint(rows))
|
||||||
})
|
})
|
||||||
|
|
||||||
so.On("disconnection", func() {
|
so.On("close", func(args ...interface{}) {
|
||||||
m.Close()
|
m.Close()
|
||||||
core.ClientClose(client)
|
core.ClientClose(client)
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
func WSError(so socketio.Socket) {
|
e.OnAny(func(eventType event.EventType, sessionId string, args ...interface{}) {
|
||||||
log.Println("error ws")
|
if session.Id == sessionId {
|
||||||
|
so.Emit(eventType.String(), args...)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
$scope.selectedInstance = null;
|
$scope.selectedInstance = null;
|
||||||
$scope.isAlive = true;
|
$scope.isAlive = true;
|
||||||
$scope.ttl = '--:--:--';
|
$scope.ttl = '--:--:--';
|
||||||
$scope.connected = true;
|
$scope.connected = false;
|
||||||
$scope.type = {windows: false};
|
$scope.type = {windows: false};
|
||||||
$scope.isInstanceBeingCreated = false;
|
$scope.isInstanceBeingCreated = false;
|
||||||
$scope.newInstanceBtnText = '+ Add new instance';
|
$scope.newInstanceBtnText = '+ Add new instance';
|
||||||
@@ -35,7 +35,6 @@
|
|||||||
$scope.isInstanceBeingDeleted = false;
|
$scope.isInstanceBeingDeleted = false;
|
||||||
$scope.uploadProgress = 0;
|
$scope.uploadProgress = 0;
|
||||||
|
|
||||||
|
|
||||||
$scope.uploadFiles = function (files, invalidFiles) {
|
$scope.uploadFiles = function (files, invalidFiles) {
|
||||||
let total = files.length;
|
let total = files.length;
|
||||||
let uploadFile = function() {
|
let uploadFile = function() {
|
||||||
@@ -179,7 +178,65 @@
|
|||||||
$scope.idxByHostname[instance.hostname] = instance;
|
$scope.idxByHostname[instance.hostname] = instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
var socket = io({ path: '/sessions/' + sessionId + '/ws' });
|
var base = '';
|
||||||
|
if (window.location.protocol == 'http:') {
|
||||||
|
base = 'ws://';
|
||||||
|
} else {
|
||||||
|
base = 'wss://';
|
||||||
|
}
|
||||||
|
base += window.location.host;
|
||||||
|
if (window.location.port) {
|
||||||
|
base += ':' + window.location.port;
|
||||||
|
}
|
||||||
|
|
||||||
|
var socket = new ReconnectingWebSocket(base + '/sessions/' + sessionId + '/ws/', null, {reconnectInterval: 1000});
|
||||||
|
socket.listeners = {};
|
||||||
|
|
||||||
|
socket.on = function(name, cb) {
|
||||||
|
if (!socket.listeners[name]) {
|
||||||
|
socket.listeners[name] = [];
|
||||||
|
}
|
||||||
|
socket.listeners[name].push(cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.emit = function() {
|
||||||
|
var name = arguments[0]
|
||||||
|
var args = [];
|
||||||
|
for (var i = 1; i < arguments.length; i++) {
|
||||||
|
args.push(arguments[i]);
|
||||||
|
}
|
||||||
|
socket.send(JSON.stringify({name: name, args: args}));
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.addEventListener('open', function (event) {
|
||||||
|
$scope.connected = true;
|
||||||
|
for (var i in $scope.instances) {
|
||||||
|
var instance = $scope.instances[i];
|
||||||
|
if (instance.term) {
|
||||||
|
instance.term.setOption('disableStdin', false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
socket.addEventListener('close', function (event) {
|
||||||
|
$scope.connected = false;
|
||||||
|
for (var i in $scope.instances) {
|
||||||
|
var instance = $scope.instances[i];
|
||||||
|
if (instance.term) {
|
||||||
|
instance.term.setOption('disableStdin', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
socket.addEventListener('message', function (event) {
|
||||||
|
var m = JSON.parse(event.data);
|
||||||
|
var ls = socket.listeners[m.name];
|
||||||
|
if (ls) {
|
||||||
|
for (var i=0; i<ls.length; i++) {
|
||||||
|
var l = ls[i];
|
||||||
|
l.apply(l, m.args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
socket.on('instance terminal status', function(name, status) {
|
socket.on('instance terminal status', function(name, status) {
|
||||||
var instance = $scope.idx[name];
|
var instance = $scope.idx[name];
|
||||||
@@ -245,13 +302,6 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('connect_error', function() {
|
|
||||||
$scope.connected = false;
|
|
||||||
});
|
|
||||||
socket.on('connect', function() {
|
|
||||||
$scope.connected = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on('instance stats', function(stats) {
|
socket.on('instance stats', function(stats) {
|
||||||
$scope.idx[stats.instance].mem = stats.mem;
|
$scope.idx[stats.instance].mem = stats.mem;
|
||||||
$scope.idx[stats.instance].cpu = stats.cpu;
|
$scope.idx[stats.instance].cpu = stats.cpu;
|
||||||
|
|||||||
@@ -280,6 +280,7 @@
|
|||||||
</md-dialog-actions>
|
</md-dialog-actions>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/reconnecting-websocket/1.0.0/reconnecting-websocket.min.js" integrity="sha256-A4JwlcDvqO4JXpvEtvWY1RH8JAEMu5W21wP8GUXLUNs=" crossorigin="anonymous"></script>
|
||||||
<script
|
<script
|
||||||
src="https://code.jquery.com/jquery-3.2.1.min.js"
|
src="https://code.jquery.com/jquery-3.2.1.min.js"
|
||||||
integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
|
integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
|
||||||
@@ -294,7 +295,6 @@
|
|||||||
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/danialfarid-angular-file-upload/12.2.13/ng-file-upload-all.min.js" integrity="sha256-LrZq3efIkFX0BooX7x/rjWyYDvMKfFV2HJpy6HBw7cE=" crossorigin="anonymous"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/danialfarid-angular-file-upload/12.2.13/ng-file-upload-all.min.js" integrity="sha256-LrZq3efIkFX0BooX7x/rjWyYDvMKfFV2HJpy6HBw7cE=" crossorigin="anonymous"></script>
|
||||||
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.7.3/socket.io.js"></script>
|
|
||||||
<script src="/assets/app.js"></script>
|
<script src="/assets/app.js"></script>
|
||||||
<script src="/assets/xterm.js"></script>
|
<script src="/assets/xterm.js"></script>
|
||||||
<script src="/assets/xterm-addons/fit.js"></script>
|
<script src="/assets/xterm-addons/fit.js"></script>
|
||||||
|
|||||||
Reference in New Issue
Block a user