I am trying to implement a websocket server using x/net/websocket
standard library.
My attempt so far looks like this:
package main
import (
"fmt"
"net/http"
"golang.org/x/net/websocket"
)
type Server struct {
baseUri string
connections map[string][]*websocket.Conn
}
func initServer(baseUri string) *Server {
return &Server{
baseUri: baseUri,
}
}
func (server *Server) handleConnections() {
http.Handle("/ws", websocket.Handler(server.listenConnections))
http.ListenAndServe(":3000", nil)
}
func (server *Server) listenConnections(ws *websocket.Conn) {
fmt.Println("New connection established")
for {
fmt.Println("FOO")
}
}
func main() {
server := initServer("/ws")
server.handleConnections()
}
Attempting to connect to ws://localhost:3000/ws
using multiple ws clients, I always get the same error: a 403-Forbidden
.
I even tried the official doc's example and am still getting the it. Am I missing something obvious? Like a default port blocking or similar?
Thank you in advance.
Edit: You may want to use a different port to reproduce the issue. Using 3000
as in my example, if not available, just breaks the program's execution.
Edit 2: You can use a client like websocat and do websocat 'ws://localhost:3000/ws'
to attempt to connect to the server
I gave up, but with nice insights: If like myself you were following Anthony GG's walkthrough on creating a websocket server on Go from scratch, please don't. Video is outdated and although it gives a great intuition on how to create one, is better to (and no shame on) learn using gorilla's websocket library.
package main
import (
"fmt"
"net/http"
"time"
"github.com/gorilla/mux"
"github.com/gorilla/websocket"
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
type Server struct {
baseUri string
connections map[string][]*websocket.Conn
router *mux.Router
setup *http.Server
}
func initServer(baseUri string) *Server {
router := mux.NewRouter()
return &Server{
baseUri: baseUri,
router: router,
setup: &http.Server{
Handler: router,
Addr: "127.0.0.1:8000",
WriteTimeout: 15 * time.Second,
ReadTimeout: 15 * time.Second,
},
}
}
func (server *Server) handleConnections() {
server.router.HandleFunc("/ws/{var}", server.listenConnections)
server.setup.ListenAndServe()
}
func (server *Server) listenConnections(w http.ResponseWriter, r *http.Request) {
connection, err := upgrader.Upgrade(w, r, nil)
if err != nil {
fmt.Println(err)
return
}
for {
_, message, err := connection.ReadMessage()
if err != nil {
break
}
connection.WriteMessage(websocket.TextMessage, message)
go messageHandler(message)
}
fmt.Println("Out of loop")
}
func messageHandler(message []byte) {
fmt.Println(string(message))
}
func main() {
server := initServer("/ws")
server.handleConnections()
}
I'm also using gorilla/mux
to be able to use path parameters (not sure why http handler could not).
Notice how I changed http.Handle
to mux.Router.HandleFunc
.
As user @Cerise pointed in the comments, x/net/websocket
package is not in the standard library, but the original issue was not resolved just adding the Origin
header either.
Hopefully this skips a few headaches of other people learning Go like myself.