go-socket.io icon indicating copy to clipboard operation
go-socket.io copied to clipboard

solve the problem of CORS and websocket 403

Open nick-bai opened this issue 6 years ago • 13 comments

I found some problems,When I used this class library.My web page and socket service are not in the same domain,so it leads to the CORS problem. i try to solve CORS problem,but i cant not find any documentation. then I search in issues and find some useful code.finally solved it. To help more people solve the same problem, I'm going to share my code.

use branch 1.4 and socket-io.js 1.4 main.go

package main

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

	socketio "github.com/googollee/go-socket.io"
	"github.com/rs/cors"
	"github.com/googollee/go-engine.io/transport/polling"
	"github.com/googollee/go-engine.io/transport/websocket"
	"github.com/googollee/go-engine.io"
	"github.com/googollee/go-engine.io/transport"
)

func main() {

	mux := http.NewServeMux()
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		w.Write([]byte("{\"hello\": \"world\"}"))
	})

	pt := polling.Default

	wt := websocket.Default
	wt.CheckOrigin = func(req *http.Request) bool {
		return true
	}

	server, err := socketio.NewServer(&engineio.Options{
		Transports: []transport.Transport{
			pt,
			wt,
		},
	})
	if err != nil {
		log.Fatal(err)
	}
	
	server.OnConnect("/", func(s socketio.Conn) error {
		s.SetContext("")
		fmt.Println("connected:", s.ID())
		return nil
	})

	server.OnEvent("/", "notice", func(s socketio.Conn, msg string) {
		fmt.Println("notice:", msg)
		s.Emit("reply", "have "+msg)
	})
	server.OnEvent("/chat", "msg", func(s socketio.Conn, msg string) string {
		s.SetContext(msg)
		return "recv " + msg
	})
	server.OnEvent("/", "bye", func(s socketio.Conn) string {
		last := s.Context().(string)
		s.Emit("bye", last)
		s.Close()
		return last
	})
	server.OnError("/", func(e error) {
		fmt.Println("meet error:", e)
	})
	server.OnDisconnect("/", func(s socketio.Conn, msg string) {
		fmt.Println("closed", msg)
	})

	go server.Serve()
	defer server.Close()

	mux.Handle("/socket.io/", server)
	//http.Handle("/", http.FileServer(http.Dir("./asset")))

	c := cors.New(cors.Options{
		AllowedOrigins:   []string{"http://localhost"},
		AllowedMethods:  []string{"GET", "PUT", "OPTIONS", "POST", "DELETE"},
		AllowCredentials: true,
	})

	// decorate existing handler with cors functionality set in c
	handler := c.Handler(mux)

	log.Println("Serving at localhost:8000...")
	log.Fatal(http.ListenAndServe(":8000", handler))
}

index.html i use official demo

nick-bai avatar Jun 18 '19 14:06 nick-bai

Given that the CORS problem arises commonly when testing I found a workaround that is not perfect but it's only for testing.

What I did was I add middleware to remove any CORS problem

func (s *SocketIOService) SocketIOFix(w http.ResponseWriter, r *http.Request) {
        // SocketIOService hold a variable called Server that is a *socketio.Server
        // Remove or add everything for CORS 
        // This take care of the client
	allowHeaders := "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization"


	if origin := r.Header.Get("Origin"); origin != "" {
		w.Header().Set("Access-Control-Allow-Origin", origin)
		w.Header().Set("Vary", "Origin")
		w.Header().Set("Access-Control-Allow-Methods", "POST, PUT, PATCH, GET, DELETE")
		w.Header().Set("Access-Control-Allow-Credentials", "true")
		w.Header().Set("Access-Control-Allow-Headers", allowHeaders)
	}
	if r.Method == "OPTIONS" {
		return
	}

        // this takes care of the server side, No Orign no CORS baby
	r.Header.Del("Origin")

        // Return to Socket io
	s.Server.ServeHTTP(w,r) 
}

Fransebas avatar Jun 26 '19 17:06 Fransebas

Here is another example:

func corsMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		allowHeaders := "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization"

		w.Header().Set("Content-Type", "application/json")
		w.Header().Set("Access-Control-Allow-Origin", "http://localhost:8100")
		w.Header().Set("Access-Control-Allow-Methods", "POST, PUT, PATCH, GET, DELETE")
		w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
		w.Header().Set("Access-Control-Allow-Credentials", "true")
		w.Header().Set("Access-Control-Allow-Headers", allowHeaders)

		next.ServeHTTP(w, r)
	})
}

func main() {
	server, err := socketio.NewServer(nil)
	if err != nil {
		log.Fatal(err)
	}

	controllers.RegisterSocketHandlers(server)

	go server.Serve()
	defer server.Close()

	http.Handle("/socket.io/", corsMiddleware(server))

	log.Println("Serving at localhost:3000...")
	log.Fatal(http.ListenAndServe(":3000", nil))
}

Note: I was testing locally from my browser on http://localhost:8100 make sure to update this to the clients address that your testing against such as - http://localhost:8080 or http://mywebsite.com etc.

insta-code1 avatar Sep 01 '19 10:09 insta-code1

this code save my 1 day of frustation, please put this in the documentation.

PhantomX7 avatar Oct 11 '19 08:10 PhantomX7

thanks to your issue, it really helps me a lot @nick-bai

HyperClockUp avatar Oct 23 '19 03:10 HyperClockUp

Thanks, it was a good starting point.

odiferousmint avatar Nov 01 '19 18:11 odiferousmint

@nick-bai You are right! Thanks for your code! And the client javascript code is below: var socket = io('ws://127.0.0.1:8000', {transports: ['websocket']});

HelloAllenZhu avatar Mar 27 '20 06:03 HelloAllenZhu

@isky0824 why polling is not working with this sample?

LucasBadico avatar Apr 30 '20 22:04 LucasBadico

@LucasBadico I am very sorry that I don't know. But it works correctly on my computer.

HelloAllenZhu avatar May 05 '20 08:05 HelloAllenZhu

Thanks for saving me, 2 days frustrated with this, even though I was always recommended to use gorilla websockets, I kept fooling around until socket.io works, again thanks (sorry for bas english, deepdl)

EddieAlvarez01 avatar Jul 04 '20 00:07 EddieAlvarez01

I found some problems,When I used this class library.My web page and socket service are not in the same domain,so it leads to the CORS problem. i try to solve CORS problem,but i cant not find any documentation. then I search in issues and find some useful code.finally solved it. To help more people solve the same problem, I'm going to share my code.

use branch 1.4 and socket-io.js 1.4 main.go

package main

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

	socketio "github.com/googollee/go-socket.io"
	"github.com/rs/cors"
	"github.com/googollee/go-engine.io/transport/polling"
	"github.com/googollee/go-engine.io/transport/websocket"
	"github.com/googollee/go-engine.io"
	"github.com/googollee/go-engine.io/transport"
)

func main() {

	mux := http.NewServeMux()
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		w.Write([]byte("{\"hello\": \"world\"}"))
	})

	pt := polling.Default

	wt := websocket.Default
	wt.CheckOrigin = func(req *http.Request) bool {
		return true
	}

	server, err := socketio.NewServer(&engineio.Options{
		Transports: []transport.Transport{
			pt,
			wt,
		},
	})
	if err != nil {
		log.Fatal(err)
	}
	
	server.OnConnect("/", func(s socketio.Conn) error {
		s.SetContext("")
		fmt.Println("connected:", s.ID())
		return nil
	})

	server.OnEvent("/", "notice", func(s socketio.Conn, msg string) {
		fmt.Println("notice:", msg)
		s.Emit("reply", "have "+msg)
	})
	server.OnEvent("/chat", "msg", func(s socketio.Conn, msg string) string {
		s.SetContext(msg)
		return "recv " + msg
	})
	server.OnEvent("/", "bye", func(s socketio.Conn) string {
		last := s.Context().(string)
		s.Emit("bye", last)
		s.Close()
		return last
	})
	server.OnError("/", func(e error) {
		fmt.Println("meet error:", e)
	})
	server.OnDisconnect("/", func(s socketio.Conn, msg string) {
		fmt.Println("closed", msg)
	})

	go server.Serve()
	defer server.Close()

	mux.Handle("/socket.io/", server)
	//http.Handle("/", http.FileServer(http.Dir("./asset")))

	c := cors.New(cors.Options{
		AllowedOrigins:   []string{"http://localhost"},
		AllowedMethods:  []string{"GET", "PUT", "OPTIONS", "POST", "DELETE"},
		AllowCredentials: true,
	})

	// decorate existing handler with cors functionality set in c
	handler := c.Handler(mux)

	log.Println("Serving at localhost:8000...")
	log.Fatal(http.ListenAndServe(":8000", handler))
}

index.html i use official demo

ths,it help me

shengulong avatar Nov 02 '20 02:11 shengulong

Given that the CORS problem arises commonly when testing I found a workaround that is not perfect but it's only for testing.

What I did was I add middleware to remove any CORS problem

func (s *SocketIOService) SocketIOFix(w http.ResponseWriter, r *http.Request) {
        // SocketIOService hold a variable called Server that is a *socketio.Server
        // Remove or add everything for CORS 
        // This take care of the client
	allowHeaders := "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization"


	if origin := r.Header.Get("Origin"); origin != "" {
		w.Header().Set("Access-Control-Allow-Origin", origin)
		w.Header().Set("Vary", "Origin")
		w.Header().Set("Access-Control-Allow-Methods", "POST, PUT, PATCH, GET, DELETE")
		w.Header().Set("Access-Control-Allow-Credentials", "true")
		w.Header().Set("Access-Control-Allow-Headers", allowHeaders)
	}
	if r.Method == "OPTIONS" {
		return
	}

        // this takes care of the server side, No Orign no CORS baby
	r.Header.Del("Origin")

        // Return to Socket io
	s.Server.ServeHTTP(w,r) 
}

Very helpful!

RinChanNOWWW avatar Dec 18 '20 05:12 RinChanNOWWW

I had the same problem. with tip [GIN-debug] [WARNING] Headers were already written. Wanted to override status code 403 with 502

wahello avatar Jul 12 '21 03:07 wahello

In the project examples, I found the correct solution for CORS.

https://github.com/googollee/go-socket.io/blob/master/_examples/gin-cors/main.go

drobinetm avatar Oct 10 '21 13:10 drobinetm