I'm trying to subscribe to an event from a client to a server. I can subscribe to the event using JavaScript but when I use Golang and the Gorilla Websocket library I get a
'websocket: close 1011 (internal server error)'
every time I start a read from the socket.
I tried using the /x/websocket library as well but when I tried to read from the socket I end up hanging and I dont get the '1011' error.
If I use this JavaScript code on my browser it works:
<html>
<head>
</head>
<body>
<script>
var socket = new WebSocket("ws://blehip:blehpport");
function send(data) {
socket.send(JSON.stringify(data));
}
socket.onopen = function() {
send({
action: 'subscribe',
auth_token: 'Bleh',
request_id: 'Bleh',
data: {
account_id: 'bleh',
binding: "bleh"
}
});
}
socket.onmessage = function(raw_message) {
var json_data = JSON.parse(raw_message.data);
console.log(json_data);
};
</script>
</body>
</html>
I get a stream of JSON objects in real time.
This is my small Golang, Gorilla/Websockets script
func main() {
var server = flag.String("server", "blehIP:BlehPort", "server address")
var wg sync.WaitGroup
flag.Parse()
url := url.URL {
Scheme: "ws",
Host: *server,
Path: "",
}
conn, _, err := websocket.DefaultDialer.Dial(url.String(), nil)
if err != nil {
log.Fatal("Dial Error: ", err)
}
defer conn.Close()
payload := []byte(`{"action":"subscribe",`+
`"auth_token":"` + bleh + `",`+
`"request_id":"` + bleh + `",`+
`"data": {`+
`"account_id":"` + bleh + `",`+
`"binding":"` + bleh + `"}`+
`}`)
wg.Add(1)
go func() {
defer conn.Close()
defer wg.Done()
err = conn.WriteMessage(websocket.TextMessage, payload)
if err != nil {
log.Println("Write Error: ", err)
return
}
m := frame{}
err := conn.ReadJSON(&m)
if err != nil {
log.Println("WebScoket closed.", err)
return
}
// msg := string(bytes[:])
fmt.Printf("%v", m)
}()
wg.Wait()
return
}
The error returned is: WebSocket closed. websocket: close 1011
(internal server error)
Related
I'm writing a chat using WebSocket with gorilla https://github.com/gorilla/websocket
It's working. But now I need to send a message from a consumer of rabbitMQ that I created. The idea is to be a Bot that answers some questions.
The problem is. That I don't know how to send a message without been using a WebSocket dial request which doesn't work, it giver this error.
dial:websocket: bad handshake
Should I create an separated go main with a different port so I can send the message? Or is it possible to do it inside the same main package?
This is the websocket code
package socket
import (
"chatGo/src/domain/message"
"chatGo/src/domain/message/repository"
"chatGo/src/infrastructure/queue"
"fmt"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
"github.com/novalagung/gubrak/v2"
"log"
"net/http"
"strings"
)
const messageNewUser = "New User"
const messageChat = "Chat"
const messageLeave = "Leave"
const maxMessageSize = 1024 * 1024 // 1kb
var connections = make([]*webSocketConnection, 0)
type payload struct {
Message string
}
type response struct {
From string
Type string
Message string
}
type webSocketConnection struct {
*websocket.Conn
Username string
}
func Execute(c *gin.Context, db repository.GormDB, qBroker *queue.Broker) {
upgrader := websocket.Upgrader{
ReadBufferSize: maxMessageSize,
WriteBufferSize: maxMessageSize,
}
currentGorillaConn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
if err != nil {
http.Error(c.Writer, "Could not open websocket connection", http.StatusBadRequest)
}
username := c.Query("username")
currentConn := webSocketConnection{Conn: currentGorillaConn, Username: username}
connections = append(connections, ¤tConn)
go handleIO(¤tConn, db, qBroker)
}
func handleIO(currentConn *webSocketConnection, db repository.GormDB, qBroker *queue.Broker) {
defer func() {
if r := recover(); r != nil {
log.Println("ERROR", fmt.Sprintf("%v", r))
}
}()
messageEntitiy := message.NewMessage(currentConn.Username, messageNewUser, "")
broadcastMessage(currentConn, messageEntitiy)
messageStr := fmt.Sprintf("User %s: connected", currentConn.Username)
messageEntitiy = message.NewMessage(currentConn.Username, messageNewUser, messageStr)
db.Create(messageEntitiy)
for {
payload := payload{}
err := currentConn.ReadJSON(&payload)
if err != nil {
if strings.Contains(err.Error(), "websocket: close") {
messageEntitiy := message.NewMessage(currentConn.Username, messageLeave, "")
broadcastMessage(currentConn, messageEntitiy)
ejectConnection(currentConn)
messageStr := fmt.Sprintf("User %s: disconnect", currentConn.Username)
messageEntitiy = message.NewMessage(currentConn.Username, messageLeave, messageStr)
db.Create(messageEntitiy)
return
}
log.Println("ERROR", err.Error())
continue
}
trimStr := strings.TrimSpace(payload.Message)
splitStr := strings.Split(trimStr, "=")
if splitStr[0] == "/stock" {
_ = qBroker.PublishMessage("bot-send", splitStr[1])
} else {
messageEntitiy := message.NewMessage(currentConn.Username, messageChat, payload.Message)
db.Create(messageEntitiy)
}
broadcastMessage(currentConn, messageEntitiy)
}
}
func ejectConnection(currentConn *webSocketConnection) {
filtered := gubrak.From(connections).Reject(func(each *webSocketConnection) bool {
return each == currentConn
}).Result()
connections = filtered.([]*webSocketConnection)
}
func broadcastMessage(currentConn *webSocketConnection, message message.Message) {
for _, eachConn := range connections {
if eachConn == currentConn {
continue
}
eachConn.WriteJSON(response{
From: currentConn.Username,
Type: message.Kind,
Message: message.Message,
})
}
}
I tried this code to send an message using dial
var addr = flag.String("addr", "localhost:8081", "http service address")
test2 := func(msg string) {
u := url.URL{Scheme: "ws", Host: *addr, Path: "/echo"}
log.Printf("connecting to %s", u.String())
c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
if err != nil {
log.Fatal("dial:", err)
}
defer c.Close()
err = c.WriteMessage(websocket.TextMessage, []byte(msg))
if err != nil {
log.Println("write:", err)
return
}
Edit 1:
It was missing the handler. I fixed this with the correct handler.
But now If I send I get this error
u := url.URL{Scheme: "ws", Host: *addr, Path: "/socket/ws?username=Bot"}
ERROR invalid character 'T' looking for beginning of value
But my idea would be to send with and Query param, but when I do a get a bad handshake 404
u := url.URL{Scheme: "ws", Host: *addr, Path: "/socket/ws?username=Bot"}
dial:websocket: bad handshake 404
I need help with Golang websocket. I'm using Fiber with websocket and redis.
Here is the code:
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/go-redis/redis/v8"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/websocket/v2"
"log"
"test4/controllers"
)
type User struct {
Name string `json:"name"`
Email string `json:"email"`
}
var ctx = context.Background()
var redisClient = redis.NewClient(&redis.Options{
Addr: "localhost:6379",
})
func TestSocket() fiber.Handler {
socket := websocket.New(func(c *websocket.Conn) {
go deliverMessages(c)
var (
msg []byte
err error
)
for {
if _, msg, err = c.ReadMessage(); err != nil {
log.Println("read:", err)
break
}
if err := redisClient.Publish(ctx, "chat", msg).Err(); err != nil {
log.Println("publish:", err)
break
}
}
})
return socket
}
func deliverMessages(c *websocket.Conn) {
subscriber := redisClient.Subscribe(ctx, "chat")
user := User{}
for {
msg, err := subscriber.ReceiveMessage(ctx)
if err != nil {
log.Println("subscriber:", err)
panic(err)
}
if err := json.Unmarshal([]byte(msg.Payload), &user); err != nil {
log.Println("Unmarshal:", err)
panic(err)
}
text := []byte(fmt.Sprintf("{\"name\":\"%s\", \"email\":\"%s\"}", user.Name, user.Email))
if err = c.WriteMessage(websocket.TextMessage, text); err != nil {
log.Println("write:", err)
break
}
}
}
func main() {
app := fiber.New(fiber.Config{
Prefork: true,
CaseSensitive: true,
StrictRouting: true,
DisableStartupMessage: true,
ServerHeader: "Test v3",
})
app.Get("/", controllers.Home)
app.Get("/ws", TestSocket())
log.Fatal(app.Listen("0.0.0.0:3000"))
}
How to produce the error:
Install Redis and run go run main.go
Now open http://127.0.0.1:3000/ in two tabs
click open on both tabs, and then you will see OPEN on right side of browser
click send on both tabs and you will get SEND and RESPONSE
Now close one tab and on go program terminal you will see error (see attached screenshot)
Now publish data to chat channel on redis-cli
Here is the error I am getting:
I think this is nil pointer websocket.Conn issue.
When close websocket connection, goroutine's c *websocket.Conn is loose data.
Pointer point nil.
solution
use channel, use local redisClient var
func TestSocket() fiber.Handler {
socket := websocket.New(func(c *websocket.Conn) {
var redisClient = redis.NewClient(&redis.Options{ // <-- use local redisClient var
Addr: "localhost:6379",
})
go deliverMessages(c)
var (
msg []byte
err error
)
defer func() {
redisClient.Close() // <-- then close, when websocket connection close
quitSubscribeGoRutine <- true // <-- change true, when websocket connection close
}()
for {
if _, msg, err = c.ReadMessage(); err != nil {
log.Println("read:", err)
...
func deliverMessages(c *websocket.Conn) {
subscriber := redisClient.Subscribe(ctx, "chat")
user := User{}
for {
select {
case <-quitSubscribeGoRutine: // <-- exit goroutine, when channel is true
return
default:
msg, err := subscriber.ReceiveMessage(ctx) // <-- exit goroutine, when redisClient close
if err != nil {
log.Println("subscriber:", err)
break // <-- use break instead of panic
}
if err := json.Unmarshal([]byte(msg.Payload), &user); err != nil {
log.Println("Unmarshal:", err)
panic(err)
}
text := []byte(fmt.Sprintf("{\"name\":\"%s\", \"email\":\"%s\"}", user.Name, user.Email))
if err = c.WriteMessage(websocket.TextMessage, text); err != nil {
log.Println("write:", err)
break
}
}
}
}
I use high-level structures to create a ws server.
func wsHandler(w http.ResponseWriter, r *http.Request) {
re := regexp.MustCompile(`^\/ws\/([0-9a-zA-Z]+)\/*`)
match := re.FindStringSubmatch(r.URL.Path)
if len(match) != 2 {
http.NotFound(w, r)
return
}
conn, _, _, err := ws.UpgradeHTTP(r, w)
if err != nil {
log.Printf("gobwas/ws: %s", err)
}
go func() {
defer conn.Close()
channels.Lock()
channels.Channels[match[1]] = append(channels.Channels[match[1]], &conn)
channels.Unlock()
for {
msg, op, err := wsutil.ReadClientData(conn)
if err != nil {
if err != io.EOF {
log.Printf("gobwas/ws/wsutil: %s", err)
}
break
}
if len(msg) > 0 {
go sendMessage(&conn, op, match[1], msg)
}
}
deleteConn(&conn, match[1])
}()
}
Then, when I implement a graceful disconnection, then with the usual conn.Close (), the connection simply breaks outside the protocol. I use the following code to close:
func onShutdown() {
channels.RLock()
for _, channel := range channels.Channels {
for _, conn := range channel {
if err := ws.WriteFrame(*conn, ws.NewCloseFrame(ws.NewCloseFrameBody(ws.StatusNormalClosure, "Server shutdown"))); err != nil {
fmt.Println(err)
}
(*conn).Close()
}
}
channels.RUnlock()
}
And even if I wait for a response from the client and then close the connection, then anyway, the client fixes the disconnection. (The client is the browser and JS WebSocket).
Sample JS code:
var socket = new WebSocket(`${location.protocol === "https:" ? "wss:" : "ws:"}//${window.location.host}/ws/valid`);
socket.onclose = function (event) {
if (event.wasClean) {
console.log('Clean');
} else {
console.log('disconnect');
}
console.log('Code: ' + event.code + ' reason: ' + event.reason);
};
Problem is simple: i have WS server that doing it's "for {}". I need "register" connected user, then send message on event(outside of CoreWS function). I'm added NodeJS example that doing exactly what i need, for better understanding.
Go Lang Code that fails:
i'm using this pkg https://github.com/gobwas/ws
var conn_itf interface{} //yeah,
var op_itf interface{} //i know it's not how it works
func main() {
go collector()
go sender()
http.ListenAndServe(":3333", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
conn, _, _, err := ws.UpgradeHTTP(r, w)
if err != nil {
// handle error
}
go func() {
defer conn.Close()
for {
msg, op, err := wsutil.ReadClientData(conn)
conn_itf = conn // i'm trying to "register" user
op_itf = op
if err != nil {
}
err = wsutil.WriteServerMessage(conn, op, msg)
if err != nil {
}
}
}()
}))
}
func collector() {
for {
fmt.Println("conn: ", conn_itf)
fmt.Println("op: ", op_itf)
time.Sleep(10000 * time.Millisecond)
}
}
func sender() {
for {
msg := []byte("Hello world!")
time.Sleep(50000 * time.Millisecond)
wsutil.WriteServerMessage(conn_itf, op_itf, msg) //invoking ws-send
}
}
NodeJS that working:
const wss = new WebSocket.Server({ port: 3333 })
wss.on('connection', ws => {
sockets.set('key', ws) //this code remember active connection
ws.on('message', message => {
console.log(`Received message => ${message}`)
})
})
function callsend(data) {
const ws = sockets.get('key') //this code sending message on invoking
ws.send(data)
}
I'm implementing chat with html5 web sockets in go lang and having errors both at client and server end.
Client Error:
WebSocket connectiont to 'ws://192.168.16.90:4000` failed: Invalid HTTP version string GET
Server Error:
continous looping of the following log message in the terminal,
connection closed\n received message:\n message sent:\n
Server Code:
func (s *Service) InitializeService() {
listener, err := net.Listen("tcp", ":4000")
if err != nil {
log.Fatal(err)
}
log.Println("Chat server started to listen on port ", listener.Addr())
defer listener.Close()
for {
conn, err := listener.Accept()
if err != nil {
log.Fatal(err)
}
fmt.Println("A new connection accepted.")
go listenConnection(conn)
}
}
func listenConnection(conn net.Conn) {
for {
buffer := make([]byte, 1400)
dataSize, err := conn.Read(buffer)
if err != nil {
fmt.Println("connection closed")
}
data := buffer[:dataSize]
fmt.Println("received message: ", string(data))
_, err = conn.Write(data)
if err != nil {
log.Fatalln(err)
}
fmt.Println("message sent: ", string(data))
}
}
Client Code:
var connection = new WebSocket("ws://192.168.16.90:4000");
connection.onopen = function() {
console.log("onopen");
connection.send("Ping");
};
connection.onerror = function(error) {
console.log("WebSocket Error", error);
};
connection.onmessage = function(e) {
console.log("Server: ", e.data);
};