Tuesday, December 1, 2020

Web Sockets Using JavaScript Frontend and GO Backend


 

In this post we will review an example of using WebSockets.

We will use a JavaScript based client running on the browser, and sending requests to a GO backend. The JavasScript and the GO backend will send messages to each other over the web socket.



The JavaScript Side


To start our JavaScript project, it would be simple to begin with a predefined react based template:


npx create-react-app demo
cd demo
npm start


Then, in App.js, add our web socket handling code:


const ws = new WebSocket('ws://localhost:8080/ws')
ws.onmessage = function (evt) {
console.log(`got from server: ${evt.data}`)
}

ws.onopen = function () {
console.log('WS Connected')
setInterval(() => {
console.log(`sending to the server`)
ws.send('I am your client')
}, 1000)
}


We start by initiating a web socket connection. 


    ❗ we use the ws:// prefix, for a secure connection use wss:// prefix


Then, we wait for the web socket to establish, and send a message to the server.
In addition, whenever we get a message from the server, we print it.


The GO Side



The GO backend application uses an echo server.

First, let's configure the web server to handle the requests:


package main

import (
"fmt"
"github.com/gorilla/websocket"
"github.com/labstack/echo/v4"
"net/http"
"time"
)

func main() {
echoServer := echo.New()

echoServer.GET("/ws", serveWebSocket)

err := echoServer.Start(":8080")
if err != nil {
panic(err)
}
}


We use the /ws URL path. This should match the path specified on the JavaScript side.

Next, communicate over the socket:


var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
},
}

func serveWebSocket(ctx echo.Context) error {
ws, err := upgrader.Upgrade(ctx.Response(), ctx.Request(), nil)
if err != nil {
return err
}
defer ws.Close()

for {
err := ws.WriteMessage(websocket.TextMessage, []byte("Hello from the server"))
if err != nil {
return err
}
_, msg, err := ws.ReadMessage()
if err != nil {
return err
}
fmt.Printf("got from the client: %v\n", string(msg))

time.Sleep(time.Second)
}
return nil
}


The socket connection is upgraded to a web socket, and then we start the conversation.

    ❗ Implement CheckOrigin to return true to allow cross origin requests


The web socket library in use is the Gorilla WebSocket.


Debugging in Google Chrome


Google Chrome supplies a great logging of the entire web socket conversation.





NGINX


In case the JavaScript code is served using an NGINX, you will need to enable the web socket establishment. See the two upgrade related headers.


location /ws {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_buffering off;
proxy_pass http://backend/ws;
}



Final Note


In this post we have reviewed the steps required to create a simple application that uses web sockets.

In a real life scenario, it is possible to connect the web socket messages to redux store, and handle them as if they were part of any other application messages.


No comments:

Post a Comment