コード例 #1
0
ファイル: websocket.go プロジェクト: Jeffail/leaps
func serveWebsocketIO(
	ws *websocket.Conn,
	portal binder.Portal,
	timeout time.Duration,
	logger log.Modular,
	stats metrics.Aggregator,
) {
	defer portal.Exit(timeout)

	// Signal to close
	var incomingCloseInt uint32
	outgoingCloseChan := make(chan struct{})

	// Signals that goroutine is closing
	incomingClosedChan := make(chan struct{})
	outgoingClosedChan := make(chan struct{})

	// Loop incoming messages.
	go func() {
		var err error
		defer func() {
			if err != nil {
				websocket.JSON.Send(ws, leapSocketServerMessage{
					Type:  "error",
					Error: err.Error(),
				})
			}
			close(incomingClosedChan)
		}()

		for atomic.LoadUint32(&incomingCloseInt) == 0 {
			var msg leapSocketClientMessage
			if socketErr := websocket.JSON.Receive(ws, &msg); socketErr != nil {
				return
			}
			switch msg.Command {
			case "submit":
				if msg.Transform == nil {
					err = errors.New("submit error: transform was nil")
					return
				}
				var ver int
				if ver, err = portal.SendTransform(*msg.Transform, timeout); err != nil {
					return
				}
				websocket.JSON.Send(ws, leapSocketServerMessage{
					Type:    "correction",
					Version: ver,
				})
			case "update":
				if msg.Position != nil || len(msg.Message) > 0 {
					portal.SendMessage(binder.Message{
						Content:  msg.Message,
						Position: msg.Position,
						Active:   true,
					})
				}
			case "ping":
				// Do nothing
			default:
				err = errors.New("command not recognised")
			}
		}
	}()

	// Loop outgoing messages.
	go func() {
		defer close(outgoingClosedChan)
		for {
			select {
			case <-outgoingCloseChan:
				return
			case tform, open := <-portal.TransformReadChan():
				if !open {
					return
				}
				websocket.JSON.Send(ws, leapSocketServerMessage{
					Type:       "transforms",
					Transforms: []text.OTransform{tform},
				})
			case msg, open := <-portal.UpdateReadChan():
				if !open {
					return
				}
				websocket.JSON.Send(ws, leapSocketServerMessage{
					Type:    "update",
					Updates: []binder.ClientUpdate{msg},
				})
			}
		}
	}()

	// If one channel closes, close the other, if the socket is being closed then close both.
	select {
	case <-incomingClosedChan:
		close(outgoingCloseChan)
		<-outgoingClosedChan
		portal.SendMessage(binder.Message{
			Active: false,
		})
	case <-outgoingClosedChan:
		atomic.StoreUint32(&incomingCloseInt, 1)
		<-incomingClosedChan
		portal.SendMessage(binder.Message{
			Active: false,
		})
	}
}