1

I need to extend the code of this tutorial to run both a regular rest process and a WebSocket server as part of go routine.

Here is the Go part:

package main

import (
    "fmt"
    "log"
    "net/http"

    "github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{
    ReadBufferSize:  1024,
    WriteBufferSize: 1024,
}

var WS = websocket.Conn{}

func reader(conn *websocket.Conn) {
    for {
        // read in a message
        messageType, p, err := conn.ReadMessage()
        if err != nil {
            log.Println(err)
            return
        }
        // print out that message for clarity
        fmt.Println(string(p))

        if err := conn.WriteMessage(messageType, p); err != nil {
            log.Println(err)
            return
        }
    }
}

func homePage(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Home Page")
}

func restResponsePage(w http.ResponseWriter, r *http.Request) {
    text := []byte("rest response")
    if err := WS.WriteMessage(websocket.TextMessage, text); err != nil {
        log.Println(err)
    }
    fmt.Fprintf(w, "rest response")
}

func wsEndpoint(w http.ResponseWriter, r *http.Request) {

    upgrader.CheckOrigin = func(r *http.Request) bool { return true }

    // upgrade this connection to a WebSocket
    // connection
    WS, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Println(err)
    }

    log.Println("Client Connected")
    err = WS.WriteMessage(1, []byte("Hi from server!"))
    if err != nil {
        log.Println(err)
    }

    reader(WS)
}

func setupRoutes() {
    http.HandleFunc("/", homePage)
    http.HandleFunc("/restRequest", restResponsePage)
}

func setupWsRoute() {
    http.HandleFunc("/ws", wsEndpoint)
}

func main() {
    fmt.Println("Hello World")

    go func() {
        setupWsRoute()
        log.Fatal(http.ListenAndServe(":8081", nil))
    }()

    setupRoutes()

    log.Fatal(http.ListenAndServe(":8080", nil))
}

And the client index.html:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Go WebSocket Tutorial</title>
  </head>
  <body>
    <h2>Hello World</h2>

    <script>
        let socket = new WebSocket("ws://127.0.0.1:8081/ws");
        console.log("Attempting Connection...");

        socket.onopen = () => {
            console.log("Successfully Connected");
            socket.send("Hi From the Client!")
        };
        
        socket.onmessage = (event) => {
            console.log("message from Server: ", event.data);
        };

        socket.onclose = event => {
            console.log("Socket Closed Connection: ", event);
            socket.send("Client Closed!")
        };

        socket.onerror = error => {
            console.log("Socket Error: ", error);
        };

    </script>
  </body>
</html>

When I hit the restRequest endpoint, before writing to the restResponsePage response writer w, I want to write a message to the websocket which I declared as a global var WS and initialized inside the reader method when the connection was upgraded.

This is somehow not working, instead, there is an exception and I don't have a clear idea on how to solve it.

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
  • 1
    The program assigns the websocket connection to a local variable declared with a [short variable declaration](https://golang.org/ref/spec#Short_variable_declarations). [Assign](https://golang.org/ref/spec#Assignments) to the package-level variable instead of declaring a local variable. – Charlie Tumahai Mar 14 '21 at 21:23
  • I thought I was already doing that in the code... what do you mean by assign to the package level var? – Eduardo A. Fernández Díaz Mar 14 '21 at 21:29
  • 1
    A package-level variable is what you call a global variable. Note the syntactic difference between a [short variable declaration](https://golang.org/ref/spec#Short_variable_declarations) and [assignment](https://golang.org/ref/spec#Assignments): `:=` versus `=`. – Charlie Tumahai Mar 14 '21 at 21:33
  • The program is working now with these two changes, thanks :) var WS *websocket.Conn and WS, _ = upgrader.Upgrade(w, r, nil) – Eduardo A. Fernández Díaz Mar 14 '21 at 22:12

1 Answers1

1

Thanks to @ThinkGoodly for his help, changing the way the local WS was being declared and using the assignment operator instead of the short variable declaration did the trick.

Here is the whole code in case someone wants to use it. :)

package main

import (
    "fmt"
    "log"
    "net/http"

    "github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{
    ReadBufferSize:  1024,
    WriteBufferSize: 1024,
}

var WS *websocket.Conn

func reader(conn *websocket.Conn) {
    for {
        // read in a message
        messageType, p, err := conn.ReadMessage()
        if err != nil {
            log.Println(err)
            return
        }
        // print out that message for clarity
        fmt.Println(string(p))

        if err := conn.WriteMessage(messageType, p); err != nil {
            log.Println(err)
            return
        }
    }
}

func homePage(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Home Page")
}

func restResponsePage(w http.ResponseWriter, r *http.Request) {
    text := []byte("rest response")
    if err := WS.WriteMessage(websocket.TextMessage, text); err != nil {
        log.Println(err)
    }
    fmt.Fprintf(w, "rest response")
}

func wsEndpoint(w http.ResponseWriter, r *http.Request) {

    upgrader.CheckOrigin = func(r *http.Request) bool { return true }

    // upgrade this connection to a WebSocket
    // connection
    WS, _ = upgrader.Upgrade(w, r, nil)
    // if err != nil {
    //  log.Println(err)
    // }

    log.Println("Client Connected")
    err1 := WS.WriteMessage(1, []byte("Hi from server!"))
    if err1 != nil {
        log.Println(err1)
    }

    reader(WS)
}

func setupRoutes() {
    http.HandleFunc("/", homePage)
    http.HandleFunc("/restRequest", restResponsePage)
}

func setupWsRoute() {
    http.HandleFunc("/ws", wsEndpoint)
}

func main() {
    fmt.Println("Hello World")

    go func() {
        setupWsRoute()
        log.Fatal(http.ListenAndServe(":8081", nil))
    }()

    setupRoutes()

    log.Fatal(http.ListenAndServe(":8080", nil))
}