Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge feature/websocket #5

Open
wants to merge 3 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
20 changes: 10 additions & 10 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -32,19 +32,19 @@ 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
golang.org/x/sync v0.7.0 // indirect
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
Expand Down
40 changes: 20 additions & 20 deletions go.sum
Original file line number Diff line number Diff line change
@@ -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=
Expand Down Expand Up @@ -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=
Expand Down Expand Up @@ -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=
Expand All @@ -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=
Expand Down Expand Up @@ -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=
Expand Down
66 changes: 66 additions & 0 deletions samples/websocket/main.go
Original file line number Diff line number Diff line change
@@ -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
}
69 changes: 69 additions & 0 deletions websocket.go
Original file line number Diff line number Diff line change
@@ -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"

Check failure on line 10 in websocket.go

View workflow job for this annotation

GitHub Actions / test (1.22.x, ubuntu-latest)

no required module provides package github.com/gorilla/websocket; to add it:

Check failure on line 10 in websocket.go

View workflow job for this annotation

GitHub Actions / test (1.22.x, macos-latest)

no required module provides package github.com/gorilla/websocket; to add it:
)

// 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)
})
}
Loading