Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2200657ba7 | |||
| 07f7893a26 | |||
| 7f21b733ed |
@@ -3,6 +3,7 @@ package internal
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
@@ -10,9 +11,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
writeWait = 10 * time.Second
|
writeWait = 10 * time.Second
|
||||||
pongWait = 60 * time.Second
|
pongWait = 60 * time.Second
|
||||||
pingPeriod = 25 * time.Second
|
pingPeriod = (pongWait * 9) / 10
|
||||||
|
maxMessageSize = 512
|
||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
@@ -20,16 +22,16 @@ type Client struct {
|
|||||||
Conn *websocket.Conn
|
Conn *websocket.Conn
|
||||||
Send chan []byte
|
Send chan []byte
|
||||||
SubscribedPath string
|
SubscribedPath string
|
||||||
done chan struct{}
|
mu sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(conn *websocket.Conn, subscribedPath string) *Client {
|
func NewClient(conn *websocket.Conn, subscribedPath string) *Client {
|
||||||
return &Client{
|
return &Client{
|
||||||
ID: uuid.NewString(),
|
ID: uuid.NewString(),
|
||||||
Conn: conn,
|
Conn: conn,
|
||||||
Send: make(chan []byte, 1),
|
Send: make(chan []byte, 256),
|
||||||
SubscribedPath: subscribedPath,
|
SubscribedPath: subscribedPath,
|
||||||
done: make(chan struct{}, 1),
|
mu: sync.Mutex{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,9 +44,9 @@ type Hub struct {
|
|||||||
|
|
||||||
func NewHub() *Hub {
|
func NewHub() *Hub {
|
||||||
return &Hub{
|
return &Hub{
|
||||||
Broadcast: make(chan []byte, 1),
|
Broadcast: make(chan []byte, 256),
|
||||||
Register: make(chan *Client, 1),
|
Register: make(chan *Client, 10),
|
||||||
Unregister: make(chan *Client, 1),
|
Unregister: make(chan *Client, 10),
|
||||||
Clients: make(map[*Client]bool),
|
Clients: make(map[*Client]bool),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -55,23 +57,22 @@ func (h *Hub) Run() {
|
|||||||
select {
|
select {
|
||||||
case client := <-h.Register:
|
case client := <-h.Register:
|
||||||
h.Clients[client] = true
|
h.Clients[client] = true
|
||||||
log.Println("Client registered")
|
log.Printf("Client registered %s\n", client.ID)
|
||||||
case c := <-h.Unregister:
|
case client := <-h.Unregister:
|
||||||
if v, ok := h.Clients[c]; ok {
|
if _, ok := h.Clients[client]; ok {
|
||||||
fmt.Println(v, c)
|
delete(h.Clients, client)
|
||||||
delete(h.Clients, c)
|
close(client.Send)
|
||||||
close(c.Send)
|
|
||||||
}
|
}
|
||||||
log.Println("Client Unregistered")
|
log.Printf("Client Unregistered %s\n", client.ID)
|
||||||
case message := <-h.Broadcast:
|
case message := <-h.Broadcast:
|
||||||
for client := range h.Clients {
|
for client := range h.Clients {
|
||||||
client.Send <- message
|
select {
|
||||||
// select {
|
case client.Send <- message:
|
||||||
// case client.Send <- message:
|
default:
|
||||||
// default:
|
close(client.Send)
|
||||||
// close(client.Send)
|
delete(h.Clients, client)
|
||||||
// delete(h.Clients, client)
|
log.Printf("Client %s removed (slow/disconnected)", client.ID)
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -90,23 +91,36 @@ func WritePump(c *Client, h *Hub) {
|
|||||||
select {
|
select {
|
||||||
case message, ok := <-c.Send:
|
case message, ok := <-c.Send:
|
||||||
c.Conn.SetWriteDeadline(time.Now().Add(writeWait))
|
c.Conn.SetWriteDeadline(time.Now().Add(writeWait))
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
c.Conn.WriteMessage(websocket.CloseMessage, []byte{})
|
c.Conn.WriteMessage(websocket.CloseMessage, []byte{})
|
||||||
fmt.Println(ok)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.Conn.WriteMessage(websocket.TextMessage, message); err != nil {
|
w, err := c.Conn.NextWriter(websocket.TextMessage)
|
||||||
fmt.Println(err)
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Write(message)
|
||||||
|
|
||||||
|
// Queue queued messages in the same buffer (optional optimization)
|
||||||
|
n := len(c.Send)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
w.Write(<-c.Send)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := w.Close(); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
case <-pingTicker.C:
|
case <-pingTicker.C:
|
||||||
c.Conn.SetWriteDeadline(time.Now().Add(writeWait))
|
c.Conn.SetWriteDeadline(time.Now().Add(writeWait))
|
||||||
if err := c.Conn.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
|
|
||||||
|
if err := c.Conn.WriteMessage(websocket.PingMessage, nil); err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,15 +130,16 @@ func ReadPump(c *Client, h *Hub) {
|
|||||||
c.Conn.Close()
|
c.Conn.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
c.Conn.SetReadLimit(1024)
|
c.Conn.SetReadLimit(maxMessageSize)
|
||||||
c.Conn.SetReadDeadline(time.Now().Add(pongWait))
|
c.Conn.SetReadDeadline(time.Now().Add(pongWait))
|
||||||
|
|
||||||
c.Conn.SetPongHandler(func(string) error {
|
c.Conn.SetPongHandler(func(string) error {
|
||||||
c.Conn.SetReadDeadline(time.Now().Add(pongWait))
|
c.Conn.SetReadDeadline(time.Now().Add(pongWait))
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
for {
|
for {
|
||||||
messageType, message, err := c.Conn.ReadMessage()
|
_, message, err := c.Conn.ReadMessage()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
|
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
|
||||||
log.Printf("WebSocket error: %v", err)
|
log.Printf("WebSocket error: %v", err)
|
||||||
@@ -132,8 +147,6 @@ func ReadPump(c *Client, h *Hub) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if messageType == websocket.TextMessage {
|
log.Printf("Received: %s\n", message)
|
||||||
log.Printf("Received: %s\n", message)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
pingPeriod = 10 * time.Second
|
pingPeriod = 10 * time.Second
|
||||||
readDeadline = 30 * time.Second
|
readDeadline = 30 * time.Second
|
||||||
|
writeDeadline = 10 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
type MessageType uint
|
type MessageType uint
|
||||||
@@ -169,7 +170,6 @@ type SafeWebsocketClient struct {
|
|||||||
doneMap *safemap.SafeMap[string, chan struct{}]
|
doneMap *safemap.SafeMap[string, chan struct{}]
|
||||||
|
|
||||||
writeChan chan Message
|
writeChan chan Message
|
||||||
pongChan chan error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wsClient *SafeWebsocketClient) connect() error {
|
func (wsClient *SafeWebsocketClient) connect() error {
|
||||||
@@ -183,20 +183,16 @@ func (wsClient *SafeWebsocketClient) connect() error {
|
|||||||
Scheme: scheme,
|
Scheme: scheme,
|
||||||
Host: fmt.Sprintf("%s:%d", wsClient.baseHost, wsClient.basePort),
|
Host: fmt.Sprintf("%s:%d", wsClient.baseHost, wsClient.basePort),
|
||||||
}
|
}
|
||||||
|
|
||||||
if wsClient.path != nil && strings.TrimSpace(*wsClient.path) != "" {
|
if wsClient.path != nil && strings.TrimSpace(*wsClient.path) != "" {
|
||||||
newURL.Path = *wsClient.path
|
newURL.Path = *wsClient.path
|
||||||
}
|
}
|
||||||
|
|
||||||
if wsClient.rawQuery != nil && strings.TrimSpace(*wsClient.rawQuery) != "" {
|
if wsClient.rawQuery != nil && strings.TrimSpace(*wsClient.rawQuery) != "" {
|
||||||
newURL.RawQuery = *wsClient.rawQuery
|
newURL.RawQuery = *wsClient.rawQuery
|
||||||
}
|
}
|
||||||
|
|
||||||
header := make(http.Header)
|
header := make(http.Header)
|
||||||
|
|
||||||
if wsClient.headers != nil {
|
if wsClient.headers != nil {
|
||||||
for k, v := range *wsClient.headers {
|
for k, v := range *wsClient.headers {
|
||||||
fmt.Println(k, v)
|
|
||||||
header.Set(k, v)
|
header.Set(k, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -207,51 +203,110 @@ func (wsClient *SafeWebsocketClient) connect() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pingCtx, pingCancel := context.WithCancel(context.Background())
|
pingCtx, pingCancel := context.WithCancel(context.Background())
|
||||||
|
pumpCtx, pumpCancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
wsClient.mu.WriteHandler(func() error {
|
wsClient.mu.WriteHandler(func() error {
|
||||||
wsClient.cancelFuncs = append(wsClient.cancelFuncs, pingCancel)
|
if wsClient.conn != nil {
|
||||||
|
wsClient.conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
wsClient.conn = conn
|
||||||
|
|
||||||
|
wsClient.cancelFuncs = append(wsClient.cancelFuncs, pingCancel, pumpCancel)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
go wsClient.startPingTicker(pingCtx)
|
go wsClient.startPingTicker(pingCtx)
|
||||||
|
go wsClient.writePump(pumpCtx, conn)
|
||||||
|
go wsClient.readPump(pumpCtx, conn)
|
||||||
|
|
||||||
|
conn.SetPingHandler(func(pingData string) error {
|
||||||
|
if err := conn.SetReadDeadline(time.Now().Add(readDeadline)); err != nil {
|
||||||
|
log.Printf("error on read deadline: %v\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
wsClient.writeChan <- Message{
|
||||||
|
MessageType: MessageTypePong,
|
||||||
|
Data: []byte(pingData),
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
conn.SetPongHandler(func(pingData string) error {
|
||||||
|
if err := conn.SetReadDeadline(time.Now().Add(readDeadline)); err != nil {
|
||||||
|
log.Printf("error on read deadline: %v\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
if wsClient.conn != nil {
|
|
||||||
wsClient.conn.Close()
|
|
||||||
}
|
|
||||||
wsClient.conn = conn
|
|
||||||
wsClient.isConnected = true
|
wsClient.isConnected = true
|
||||||
|
|
||||||
go wsClient.writePump()
|
|
||||||
go wsClient.readPump()
|
|
||||||
|
|
||||||
// conn.SetPingHandler(func(pingData string) error {
|
|
||||||
// wsClient.writeChan <- Message{
|
|
||||||
// MessageType: MessageTypePong,
|
|
||||||
// Data: []byte(pingData),
|
|
||||||
// }
|
|
||||||
|
|
||||||
// select {
|
|
||||||
// case err := <-wsClient.pongChan:
|
|
||||||
// return err
|
|
||||||
// default:
|
|
||||||
// }
|
|
||||||
// return nil
|
|
||||||
// })
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wsClient *SafeWebsocketClient) writePump() {
|
func (wsClient *SafeWebsocketClient) reconnectHandler() {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
backoff := 1 * time.Second
|
||||||
wsClient.mu.WriteHandler(func() error {
|
maxBackoff := 15 * time.Second
|
||||||
wsClient.cancelFuncs = append(wsClient.cancelFuncs, cancel)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
var c *websocket.Conn
|
for {
|
||||||
wsClient.mu.ReadHandler(func() error {
|
select {
|
||||||
c = wsClient.conn
|
case <-wsClient.reconnectCh:
|
||||||
return nil
|
log.Println("Reconnect triggered")
|
||||||
})
|
|
||||||
|
wsClient.mu.WriteHandler(func() error {
|
||||||
|
if wsClient.cancelFuncs != nil {
|
||||||
|
for _, cancel := range wsClient.cancelFuncs {
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
wsClient.cancelFuncs = nil
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
wsClient.isConnected = false
|
||||||
|
isInnerLoop := true
|
||||||
|
for isInnerLoop {
|
||||||
|
log.Printf("Attempting reconnect in %v...", backoff)
|
||||||
|
select {
|
||||||
|
case <-time.After(backoff):
|
||||||
|
if err := wsClient.connect(); err != nil {
|
||||||
|
log.Printf("Reconnect failed: %v", err)
|
||||||
|
if backoff < maxBackoff {
|
||||||
|
backoff *= 2
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log.Println("Reconnected successfully")
|
||||||
|
backoff = 1 * time.Second
|
||||||
|
isInnerLoop = false
|
||||||
|
continue
|
||||||
|
case <-wsClient.ctx.Done():
|
||||||
|
log.Println("reconnect handler stopped due to client shutdown")
|
||||||
|
wsClient.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if wsClient.reconnectChans != nil {
|
||||||
|
for _, reconnectCh := range wsClient.reconnectChans {
|
||||||
|
select {
|
||||||
|
case reconnectCh <- struct{}{}:
|
||||||
|
default: // prevent blocking if chan is full
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case <-wsClient.ctx.Done():
|
||||||
|
log.Println("reconnect handler stopped due to client shutdown")
|
||||||
|
wsClient.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wsClient *SafeWebsocketClient) writePump(ctx context.Context, c *websocket.Conn) {
|
||||||
|
defer func() {
|
||||||
|
c.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
@@ -259,33 +314,25 @@ func (wsClient *SafeWebsocketClient) writePump() {
|
|||||||
log.Println("Writer canceled by context")
|
log.Println("Writer canceled by context")
|
||||||
return
|
return
|
||||||
case data := <-wsClient.writeChan:
|
case data := <-wsClient.writeChan:
|
||||||
if c == nil {
|
if err := c.SetWriteDeadline(time.Now().Add(writeDeadline)); err != nil {
|
||||||
|
log.Printf("error setting write deadline: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.WriteMessage(int(data.MessageType), data.Data); err != nil {
|
if err := c.WriteMessage(int(data.MessageType), data.Data); err != nil {
|
||||||
log.Printf("error on write message: %v\n", err)
|
log.Printf("error on write message: %v\n", err)
|
||||||
if data.MessageType == MessageTypePong {
|
wsClient.triggerReconnect() // Trigger reconnect on write failure
|
||||||
wsClient.pongChan <- err
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wsClient *SafeWebsocketClient) readPump() {
|
func (wsClient *SafeWebsocketClient) readPump(ctx context.Context, c *websocket.Conn) {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
defer func() {
|
||||||
wsClient.mu.WriteHandler(func() error {
|
wsClient.triggerReconnect()
|
||||||
wsClient.cancelFuncs = append(wsClient.cancelFuncs, cancel)
|
c.Close()
|
||||||
return nil
|
}()
|
||||||
})
|
|
||||||
|
|
||||||
var c *websocket.Conn
|
|
||||||
wsClient.mu.ReadHandler(func() error {
|
|
||||||
c = wsClient.conn
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
@@ -293,19 +340,14 @@ func (wsClient *SafeWebsocketClient) readPump() {
|
|||||||
log.Println("Reader canceled by context")
|
log.Println("Reader canceled by context")
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
if c == nil {
|
// Set read deadline
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.SetReadDeadline(time.Now().Add(readDeadline)); err != nil {
|
if err := c.SetReadDeadline(time.Now().Add(readDeadline)); err != nil {
|
||||||
log.Printf("error on read deadline: %v\n", err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
messageType, data, err := c.ReadMessage()
|
messageType, data, err := c.ReadMessage()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("error on read message: %v\n", err)
|
log.Printf("error on read message: %v\n", err)
|
||||||
wsClient.triggerReconnect()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -351,60 +393,6 @@ func (wsClient *SafeWebsocketClient) triggerReconnect() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wsClient *SafeWebsocketClient) reconnectHandler() {
|
|
||||||
backoff := 1 * time.Second
|
|
||||||
maxBackoff := 30 * time.Second
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-wsClient.reconnectCh:
|
|
||||||
log.Println("Reconnect triggered")
|
|
||||||
|
|
||||||
wsClient.mu.ReadHandler(func() error {
|
|
||||||
if wsClient.cancelFuncs != nil {
|
|
||||||
for _, cancel := range wsClient.cancelFuncs {
|
|
||||||
cancel()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
wsClient.isConnected = false
|
|
||||||
|
|
||||||
isInnerLoop := true
|
|
||||||
for isInnerLoop {
|
|
||||||
log.Printf("Attempting reconnect in %v...", backoff)
|
|
||||||
select {
|
|
||||||
case <-time.After(backoff):
|
|
||||||
if err := wsClient.connect(); err != nil {
|
|
||||||
log.Printf("Reconnect failed: %v", err)
|
|
||||||
if backoff < maxBackoff {
|
|
||||||
backoff *= 2
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
log.Println("Reconnected successfully")
|
|
||||||
backoff = 1 * time.Second
|
|
||||||
isInnerLoop = false
|
|
||||||
continue
|
|
||||||
case <-wsClient.ctx.Done():
|
|
||||||
log.Println("reconnect handler stopped due to client shutdown")
|
|
||||||
wsClient.Close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if wsClient.reconnectChans != nil {
|
|
||||||
for _, reconnectCh := range wsClient.reconnectChans {
|
|
||||||
reconnectCh <- struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case <-wsClient.ctx.Done():
|
|
||||||
log.Println("reconnect handler stopped due to client shutdown")
|
|
||||||
wsClient.Close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wsClient *SafeWebsocketClient) ReconnectChannel() <-chan struct{} {
|
func (wsClient *SafeWebsocketClient) ReconnectChannel() <-chan struct{} {
|
||||||
reconnectCh := make(chan struct{}, 1)
|
reconnectCh := make(chan struct{}, 1)
|
||||||
wsClient.mu.WriteHandler(func() error {
|
wsClient.mu.WriteHandler(func() error {
|
||||||
@@ -450,7 +438,7 @@ func (wsClient *SafeWebsocketClient) Close() error {
|
|||||||
wsClient.conn.Close()
|
wsClient.conn.Close()
|
||||||
}
|
}
|
||||||
wsClient.isConnected = false
|
wsClient.isConnected = false
|
||||||
// close(wsClient.dataChannel)
|
close(wsClient.dataChannel)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,18 @@ import (
|
|||||||
"os/signal"
|
"os/signal"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"net/http"
|
||||||
|
_ "net/http/pprof"
|
||||||
|
|
||||||
"git.neurocipta.com/rogerferdinan/safe-web-socket/v1/client"
|
"git.neurocipta.com/rogerferdinan/safe-web-socket/v1/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
go func() {
|
||||||
|
log.Println("Starting pprof server on :6060")
|
||||||
|
log.Println(http.ListenAndServe(":6060", nil))
|
||||||
|
}()
|
||||||
|
|
||||||
sigChan := make(chan os.Signal, 1)
|
sigChan := make(chan os.Signal, 1)
|
||||||
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ func main() {
|
|||||||
BasePort(8080).
|
BasePort(8080).
|
||||||
ApiKey("abcd").
|
ApiKey("abcd").
|
||||||
HandleFuncWebsocket("/ws/test/", "data_1", func(c chan []byte) {
|
HandleFuncWebsocket("/ws/test/", "data_1", func(c chan []byte) {
|
||||||
ticker := time.NewTicker(10 * time.Millisecond)
|
ticker := time.NewTicker(100 * time.Millisecond)
|
||||||
for range ticker.C {
|
for range ticker.C {
|
||||||
jsonBytes, err := json.Marshal(ExampleData{
|
jsonBytes, err := json.Marshal(ExampleData{
|
||||||
Time: time.Now(),
|
Time: time.Now(),
|
||||||
@@ -32,7 +32,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
}).
|
}).
|
||||||
HandleFuncWebsocket("/ws/test/", "data_2", func(c chan []byte) {
|
HandleFuncWebsocket("/ws/test/", "data_2", func(c chan []byte) {
|
||||||
ticker := time.NewTicker(10 * time.Millisecond)
|
ticker := time.NewTicker(100 * time.Millisecond)
|
||||||
for range ticker.C {
|
for range ticker.C {
|
||||||
jsonBytes, err := json.Marshal(ExampleData{
|
jsonBytes, err := json.Marshal(ExampleData{
|
||||||
Time: time.Now(),
|
Time: time.Now(),
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/subtle"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -81,6 +82,7 @@ func (b *SafeWebsocketServerBuilder) HandleFuncWebsocket(pattern string, subscri
|
|||||||
}
|
}
|
||||||
c := internal.NewClient(conn, subscribedPath)
|
c := internal.NewClient(conn, subscribedPath)
|
||||||
h.Register <- c
|
h.Register <- c
|
||||||
|
|
||||||
go internal.WritePump(c, h)
|
go internal.WritePump(c, h)
|
||||||
go internal.ReadPump(c, h)
|
go internal.ReadPump(c, h)
|
||||||
go writeFunc(h.Broadcast)
|
go writeFunc(h.Broadcast)
|
||||||
@@ -110,12 +112,17 @@ type SafeWebsocketServer struct {
|
|||||||
|
|
||||||
func (s *SafeWebsocketServer) AuthMiddleware(next http.Handler) http.Handler {
|
func (s *SafeWebsocketServer) AuthMiddleware(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Header.Get("X-MBX-APIKEY") != s.apiKey {
|
providedKey := r.Header.Get("X-MBX-APIKEY")
|
||||||
|
expectedKey := s.apiKey
|
||||||
|
|
||||||
|
if subtle.ConstantTimeCompare([]byte(providedKey), []byte(expectedKey)) != 1 {
|
||||||
internal.ErrorResponse(w, internal.NewStatusMessage().
|
internal.ErrorResponse(w, internal.NewStatusMessage().
|
||||||
StatusCode(http.StatusForbidden).
|
StatusCode(http.StatusForbidden).
|
||||||
Message("X-MBX-APIKEY is missing").
|
Message("X-MBX-APIKEY is missing").
|
||||||
Build())
|
Build())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user