144 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			144 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package main
 | 
						|
 | 
						|
import (
 | 
						|
	"log"
 | 
						|
	"sync"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/gorilla/websocket"
 | 
						|
)
 | 
						|
 | 
						|
const TIMEOUT = 6
 | 
						|
const IDLE_TIMEOUT = TIMEOUT * time.Second
 | 
						|
const PING_INTERVAL = (TIMEOUT / 2) * time.Second
 | 
						|
 | 
						|
type WSConnection struct {
 | 
						|
	alive     bool
 | 
						|
	url       string
 | 
						|
	conn      *websocket.Conn
 | 
						|
	errChan   chan error
 | 
						|
	writeLock sync.Mutex
 | 
						|
	ReadChan  chan string
 | 
						|
	WriteChan chan string
 | 
						|
	Dead      chan error
 | 
						|
}
 | 
						|
 | 
						|
func (ws *WSConnection) messageReader() {
 | 
						|
	log.Printf("Starting reader")
 | 
						|
	for {
 | 
						|
		if !ws.alive {
 | 
						|
			break
 | 
						|
		}
 | 
						|
 | 
						|
		_, message, err := ws.conn.ReadMessage()
 | 
						|
		ws.conn.SetReadDeadline(time.Now().Add(IDLE_TIMEOUT))
 | 
						|
		if err != nil {
 | 
						|
			ws.errChan <- err
 | 
						|
			break
 | 
						|
		}
 | 
						|
 | 
						|
		log.Printf("Received: %s, %d in output channel", message, len(ws.ReadChan))
 | 
						|
		ws.ReadChan <- string(message)
 | 
						|
	}
 | 
						|
	log.Printf("Reader done")
 | 
						|
}
 | 
						|
 | 
						|
func (ws *WSConnection) messageSender() {
 | 
						|
	log.Printf("Starting sender")
 | 
						|
	for {
 | 
						|
		msg, ok := <-ws.WriteChan
 | 
						|
		if !ok {
 | 
						|
			break
 | 
						|
		}
 | 
						|
		ws.doSend(msg)
 | 
						|
	}
 | 
						|
	log.Printf("Sender done")
 | 
						|
}
 | 
						|
 | 
						|
func (ws *WSConnection) doSend(msg string) {
 | 
						|
	ws.writeLock.Lock()
 | 
						|
	defer ws.writeLock.Unlock()
 | 
						|
 | 
						|
	ws.conn.SetWriteDeadline(time.Now().Add(IDLE_TIMEOUT))
 | 
						|
	log.Printf("Sending: %s, %d in input channel", msg, len(ws.WriteChan))
 | 
						|
	err := ws.conn.WriteMessage(websocket.TextMessage, []byte(msg))
 | 
						|
	if err != nil {
 | 
						|
		log.Printf("Error during message writing: %v", err)
 | 
						|
		ws.errChan <- err
 | 
						|
		return
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (ws *WSConnection) pinger() {
 | 
						|
	log.Printf("Starting pinger, sleeping for %v", PING_INTERVAL)
 | 
						|
	for {
 | 
						|
		if !ws.alive {
 | 
						|
			break
 | 
						|
		}
 | 
						|
		ws.doPing()
 | 
						|
		time.Sleep(PING_INTERVAL)
 | 
						|
	}
 | 
						|
	log.Printf("Pinger done")
 | 
						|
}
 | 
						|
 | 
						|
func (ws *WSConnection) doPing() {
 | 
						|
	ws.writeLock.Lock()
 | 
						|
	defer ws.writeLock.Unlock()
 | 
						|
 | 
						|
	// log.Printf("Ping")
 | 
						|
	err := ws.conn.WriteMessage(websocket.PingMessage, nil)
 | 
						|
	if err != nil {
 | 
						|
		log.Println("Error during ping:", err)
 | 
						|
		ws.errChan <- err
 | 
						|
		return
 | 
						|
	}
 | 
						|
	ws.conn.SetWriteDeadline(time.Now().Add(IDLE_TIMEOUT))
 | 
						|
	// log.Printf("Ping OK")
 | 
						|
}
 | 
						|
 | 
						|
func (ws *WSConnection) handleError() {
 | 
						|
	for {
 | 
						|
		err := <-ws.errChan
 | 
						|
		log.Printf("Client error: %+v", err)
 | 
						|
		ws.alive = false
 | 
						|
		ws.conn.Close()
 | 
						|
		close(ws.ReadChan)
 | 
						|
		close(ws.WriteChan)
 | 
						|
		close(ws.errChan)
 | 
						|
		ws.Dead <- err
 | 
						|
		return
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (ws *WSConnection) Open() {
 | 
						|
	log.Printf("Connecting to %s", ws.url)
 | 
						|
	ws.Dead = make(chan error, 1)
 | 
						|
 | 
						|
	conn, _, err := websocket.DefaultDialer.Dial(ws.url, nil)
 | 
						|
	if err != nil {
 | 
						|
		log.Println("Error during connection:", err)
 | 
						|
		ws.Dead <- err
 | 
						|
		return
 | 
						|
	}
 | 
						|
	log.Printf("Connected")
 | 
						|
	ws.conn = conn
 | 
						|
	ws.alive = true
 | 
						|
 | 
						|
	ws.errChan = make(chan error, 1)
 | 
						|
	ws.ReadChan = make(chan string, 128)
 | 
						|
	ws.WriteChan = make(chan string, 128)
 | 
						|
 | 
						|
	ws.conn.SetReadDeadline(time.Now().Add(IDLE_TIMEOUT))
 | 
						|
	ws.conn.SetWriteDeadline(time.Now().Add(IDLE_TIMEOUT))
 | 
						|
	ws.conn.SetPongHandler(func(string) error {
 | 
						|
		// log.Println("Pong")
 | 
						|
		ws.conn.SetReadDeadline(time.Now().Add(IDLE_TIMEOUT))
 | 
						|
		return nil
 | 
						|
	})
 | 
						|
 | 
						|
	go ws.handleError()
 | 
						|
	go ws.messageReader()
 | 
						|
	go ws.messageSender()
 | 
						|
	go ws.pinger()
 | 
						|
}
 |