From 2f1f5acdc2ba970ecc980ee69236f7623b8d769d Mon Sep 17 00:00:00 2001 From: Gildas Cherruel Date: Sun, 28 Apr 2024 23:20:30 +0900 Subject: [PATCH 1/3] Updated github actions --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d46c0ab..e26ac0d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,7 +10,7 @@ jobs: test: strategy: matrix: - go-version: [1.20.x, 1.21.x] + go-version: [1.21.x, 1.22.x] platform: [ubuntu-latest, macos-latest] runs-on: ${{ matrix.platform }} steps: From 3be488bab837d44890847d9f4adced091b36c4f7 Mon Sep 17 00:00:00 2001 From: Gildas Cherruel Date: Sun, 5 May 2024 20:04:13 +0900 Subject: [PATCH 2/3] Updated modules --- go.mod | 20 ++++++++++---------- go.sum | 40 ++++++++++++++++++++-------------------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/go.mod b/go.mod index b667a04..1ea88d6 100644 --- a/go.mod +++ b/go.mod @@ -6,17 +6,17 @@ require ( github.com/gildas/go-core v0.5.8 github.com/gildas/go-errors v0.3.6 github.com/gildas/go-logger v1.6.15 - github.com/gildas/go-request v0.9.3 + github.com/gildas/go-request v0.9.4 github.com/gorilla/mux v1.8.1 github.com/joho/godotenv v1.5.1 - github.com/rs/cors v1.10.1 + github.com/rs/cors v1.11.0 github.com/stretchr/testify v1.9.0 ) require ( cloud.google.com/go v0.112.2 // indirect - cloud.google.com/go/auth v0.2.0 // indirect - cloud.google.com/go/auth/oauth2adapt v0.2.0 // indirect + cloud.google.com/go/auth v0.3.0 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect cloud.google.com/go/compute/metadata v0.3.0 // indirect cloud.google.com/go/logging v1.9.0 // indirect cloud.google.com/go/longrunning v0.5.6 // indirect @@ -32,11 +32,11 @@ require ( github.com/googleapis/gax-go/v2 v2.12.3 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.50.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0 // indirect - go.opentelemetry.io/otel v1.25.0 // indirect - go.opentelemetry.io/otel/metric v1.25.0 // indirect - go.opentelemetry.io/otel/trace v1.25.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.51.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 // indirect + go.opentelemetry.io/otel v1.26.0 // indirect + go.opentelemetry.io/otel/metric v1.26.0 // indirect + go.opentelemetry.io/otel/trace v1.26.0 // indirect golang.org/x/crypto v0.22.0 // indirect golang.org/x/net v0.24.0 // indirect golang.org/x/oauth2 v0.19.0 // indirect @@ -44,7 +44,7 @@ require ( golang.org/x/sys v0.19.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.5.0 // indirect - google.golang.org/api v0.174.0 // indirect + google.golang.org/api v0.176.1 // indirect google.golang.org/genproto v0.0.0-20240415180920-8c6c420018be // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240415180920-8c6c420018be // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be // indirect diff --git a/go.sum b/go.sum index ca8688a..0031f12 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,10 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.112.2 h1:ZaGT6LiG7dBzi6zNOvVZwacaXlmf3lRqnC4DQzqyRQw= cloud.google.com/go v0.112.2/go.mod h1:iEqjp//KquGIJV/m+Pk3xecgKNhV+ry+vVTsy4TbDms= -cloud.google.com/go/auth v0.2.0 h1:y6oTcpMSbOcXbwYgUUrvI+mrQ2xbrcdpPgtVbCGTLTk= -cloud.google.com/go/auth v0.2.0/go.mod h1:+yb+oy3/P0geX6DLKlqiGHARGR6EX2GRtYCzWOCQSbU= -cloud.google.com/go/auth/oauth2adapt v0.2.0 h1:FR8zevgQwu+8CqiOT5r6xCmJa3pJC/wdXEEPF1OkNhA= -cloud.google.com/go/auth/oauth2adapt v0.2.0/go.mod h1:AfqujpDAlTfLfeCIl/HJZZlIxD8+nJoZ5e0x1IxGq5k= +cloud.google.com/go/auth v0.3.0 h1:PRyzEpGfx/Z9e8+lHsbkoUVXD0gnu4MNmm7Gp8TQNIs= +cloud.google.com/go/auth v0.3.0/go.mod h1:lBv6NKTWp8E3LPzmO1TbiiRKc4drLOfHsgmlH9ogv5w= +cloud.google.com/go/auth/oauth2adapt v0.2.2 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKFt//oWu7HX4= +cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q= cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/iam v1.1.7 h1:z4VHOhwKLF/+UYXAJDFwGtNF0b6gjsW1Pk9Ml0U/IoM= @@ -32,8 +32,8 @@ github.com/gildas/go-errors v0.3.6 h1:/loKTkq/t+eoIcULhKAwd0WBRPHgZxhkd3l+m/uw15 github.com/gildas/go-errors v0.3.6/go.mod h1:jqH4hy2BzpU3mdjkUYJhkZvEkn56cWRjWVgz/HNqglQ= github.com/gildas/go-logger v1.6.15 h1:scmCEQLrkxxAG6rX75RBJchXQJ7Y48T34fknVgsiu2M= github.com/gildas/go-logger v1.6.15/go.mod h1:wXUIRIKzkw7RYB24vEmJ+bP9zttV6WZ/EXMjHd6yKfA= -github.com/gildas/go-request v0.9.3 h1:lHYN6fobXaV+mb/S6U+gGsJtNMfN6p485yEitCAKaNM= -github.com/gildas/go-request v0.9.3/go.mod h1:Fiyu0QCRBhBaBeyzx1VhGGwy8ieVxWf7l0Jc6TqjoIs= +github.com/gildas/go-request v0.9.4 h1:eKMqDtiH2aCIcfvS3zZ8TkfgqchgP3fWjmpNQZ1HRv8= +github.com/gildas/go-request v0.9.4/go.mod h1:/NhAEG0RcFlR2jBNTlJGCkCivhZD4m1t9u7f7TB3/Z4= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -83,8 +83,8 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= -github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/rs/cors v1.11.0 h1:0B9GE/r9Bc2UxRMMtymBkHTenPkHDv0CW4Y98GBY+po= +github.com/rs/cors v1.11.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -95,18 +95,18 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.50.0 h1:zvpPXY7RfYAGSdYQLjp6zxdJNSYD/+FFoCTQN9IPxBs= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.50.0/go.mod h1:BMn8NB1vsxTljvuorms2hyOs8IBuuBEq0pl7ltOfy30= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0 h1:cEPbyTSEHlQR89XVlyo78gqluF8Y3oMeBkXGWzQsfXY= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0/go.mod h1:DKdbWcT4GH1D0Y3Sqt/PFXt2naRKDWtU+eE6oLdFNA8= -go.opentelemetry.io/otel v1.25.0 h1:gldB5FfhRl7OJQbUHt/8s0a7cE8fbsPAtdpRaApKy4k= -go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg= -go.opentelemetry.io/otel/metric v1.25.0 h1:LUKbS7ArpFL/I2jJHdJcqMGxkRdxpPHE0VU/D4NuEwA= -go.opentelemetry.io/otel/metric v1.25.0/go.mod h1:rkDLUSd2lC5lq2dFNrX9LGAbINP5B7WBkC78RXCpH5s= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.51.0 h1:A3SayB3rNyt+1S6qpI9mHPkeHTZbD7XILEqWnYZb2l0= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.51.0/go.mod h1:27iA5uvhuRNmalO+iEUdVn5ZMj2qy10Mm+XRIpRmyuU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 h1:Xs2Ncz0gNihqu9iosIZ5SkBbWo5T8JhhLJFMQL1qmLI= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0/go.mod h1:vy+2G/6NvVMpwGX/NyLqcC41fxepnuKHk16E6IZUcJc= +go.opentelemetry.io/otel v1.26.0 h1:LQwgL5s/1W7YiiRwxf03QGnWLb2HW4pLiAhaA5cZXBs= +go.opentelemetry.io/otel v1.26.0/go.mod h1:UmLkJHUAidDval2EICqBMbnAd0/m2vmpf/dAM+fvFs4= +go.opentelemetry.io/otel/metric v1.26.0 h1:7S39CLuY5Jgg9CrnA9HHiEjGMF/X2VHvoXGgSllRz30= +go.opentelemetry.io/otel/metric v1.26.0/go.mod h1:SY+rHOI4cEawI9a7N1A4nIg/nTQXe1ccCNWYOJUrpX4= go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw= go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc= -go.opentelemetry.io/otel/trace v1.25.0 h1:tqukZGLwQYRIFtSQM2u2+yfMVTgGVeqRLPUYx1Dq6RM= -go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I= +go.opentelemetry.io/otel/trace v1.26.0 h1:1ieeAUb4y0TE26jUFrCIXKpTuVK7uJGN9/Z/2LP5sQA= +go.opentelemetry.io/otel/trace v1.26.0/go.mod h1:4iDxvGDQuUkHve82hJJ8UqrwswHYsZuWCBllGV2U2y0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= @@ -149,8 +149,8 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.174.0 h1:zB1BWl7ocxfTea2aQ9mgdzXjnfPySllpPOskdnO+q34= -google.golang.org/api v0.174.0/go.mod h1:aC7tB6j0HR1Nl0ni5ghpx6iLasmAX78Zkh/wgxAAjLg= +google.golang.org/api v0.176.1 h1:DJSXnV6An+NhJ1J+GWtoF2nHEuqB1VNoTfnIbjNvwD4= +google.golang.org/api v0.176.1/go.mod h1:j2MaSDYcvYV1lkZ1+SMW4IeF90SrEyFA+tluDYWRrFg= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= From d88666be82261454fadf67e4b65b6dc12ad84a7a Mon Sep 17 00:00:00 2001 From: Gildas Cherruel Date: Sun, 5 May 2024 20:04:39 +0900 Subject: [PATCH 3/3] Adding websocket support --- samples/websocket/main.go | 66 +++++++++++++++++++++++++++++++++++++ websocket.go | 69 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 samples/websocket/main.go create mode 100644 websocket.go diff --git a/samples/websocket/main.go b/samples/websocket/main.go new file mode 100644 index 0000000..481e538 --- /dev/null +++ b/samples/websocket/main.go @@ -0,0 +1,66 @@ +package main + +import ( + "context" + "flag" + "net/http" + "os" + "runtime" + "strings" + + "github.com/gildas/go-core" + "github.com/gildas/go-logger" + "github.com/gildas/wess" + "github.com/gorilla/websocket" +) + +var APP = "websocket" + +func main() { + port := flag.Int("port", core.GetEnvAsInt("PORT", 80), "The port to listen on") + flag.Parse() + + log := logger.Create(APP) + defer log.Flush() + log.Infof(strings.Repeat("-", 80)) + log.Infof("Starting %s v%s (%s)", APP, wess.VERSION, runtime.GOARCH) + log.Infof("Log Destination: %s", log) + + if *port == 0 { + log.Fatalf("No port specified") + os.Exit(-1) + } + + server := wess.NewServer(wess.ServerOptions{ + Port: *port, + Logger: log, + }) + + server.AddWebSocketRouteWithHandlerFunc("/ws", func(w http.ResponseWriter, r *http.Request, conn *websocket.Conn) { + log := logger.Must(logger.FromContext(r.Context())).Child(nil, "ws") + + log.Infof("WebSocket connection from %s", conn.RemoteAddr()) + _ = conn.WriteMessage(websocket.TextMessage, []byte("Hello, World!")) + conn.Close() + }) + + router := server.SubRouter("/api") + router.Path("/ws1").Handler(wess.WebSocketHandler(func(w http.ResponseWriter, r *http.Request, conn *websocket.Conn) { + log := logger.Must(logger.FromContext(r.Context())).Child(nil, "ws") + + log.Infof("WebSocket connection from %s", conn.RemoteAddr()) + _ = conn.WriteMessage(websocket.TextMessage, []byte("Hello, World!")) + conn.Close() + })) + + router.Path("/ws2").HandlerFunc(wess.WebSocketHandlerFunc(func(w http.ResponseWriter, r *http.Request, conn *websocket.Conn) { + log := logger.Must(logger.FromContext(r.Context())).Child(nil, "ws") + + log.Infof("WebSocket connection from %s", conn.RemoteAddr()) + _ = conn.WriteMessage(websocket.TextMessage, []byte("Hello, World!")) + conn.Close() + })) + + shutdown, _, _ := server.Start(context.Background()) + <-shutdown +} diff --git a/websocket.go b/websocket.go new file mode 100644 index 0000000..4b0037f --- /dev/null +++ b/websocket.go @@ -0,0 +1,69 @@ +package wess + +import ( + "net/http" + "net/url" + "strings" + + "github.com/gildas/go-core" + "github.com/gildas/go-logger" + "github.com/gorilla/websocket" +) + +// WebSocketHandler is a function that will handle a WebSocket connection +type WebSocketHandler func(w http.ResponseWriter, r *http.Request, conn *websocket.Conn) + +// WebSocketHandlerFunc is a function that will handle a WebSocket connection +type WebSocketHandlerFunc func(w http.ResponseWriter, r *http.Request, conn *websocket.Conn) + +var upgrader = websocket.Upgrader{ + ReadBufferSize: 4096, + WriteBufferSize: 4096, + CheckOrigin: func(r *http.Request) bool { + log := logger.Must(logger.FromContext(r.Context())).Child("websocket", "checkorigin") + origin := r.Header.Get("Origin") + + if len(origin) == 0 { + log.Debugf("No Origin Header, accepting...") + return true + } + + originURL, err := url.Parse(origin) + if err != nil { + log.Errorf("Failed to parse the Origin Header: %s", origin, err) + return false + } + + allowedOrigins := strings.Split(core.GetEnvAsString("WEBSOCKET_ALLOWED_ORIGINS", ""), ",") + if len(allowedOrigins) == 0 && originURL.Host != r.Host { + log.Errorf("Origin %s is not allowed as it differs from %s", origin, r.Host) + return false + } + + for _, allowedOrigin := range allowedOrigins { + allowedOrigin = strings.TrimSpace(allowedOrigin) + if allowedOrigin == "*" || allowedOrigin == origin { + log.Infof("Origin %s is allowed", origin) + return true + } + } + log.Errorf("Origin %s is not allowed as it does not belong to: ", origin, strings.Join(allowedOrigins, ", ")) + return false + }, +} + +// ServeHTTP serves the WebSocket +func (handler WebSocketHandler) ServeHTTP(w http.ResponseWriter, r *http.Request, responseHeader http.Header) { + conn, err := upgrader.Upgrade(w, r, responseHeader) + if err != nil { + core.RespondWithError(w, http.StatusBadRequest, err) + return + } + handler(w, r, conn) +} + +func (server *Server) AddWebSocketRouteWithHandlerFunc(path string, handler WebSocketHandlerFunc) { + server.AddRouteWithFunc(http.MethodGet, path, func(w http.ResponseWriter, r *http.Request) { + handler.ServeWebSocket(w, r, nil) + }) +}