diff --git a/README.md b/README.md index 729ceb43..070d1381 100644 --- a/README.md +++ b/README.md @@ -50,13 +50,12 @@ Planned milestones and features: ### Features -The library offers several advanced features, especially at websocket and ocpp-j level. +The library offers several advanced features, especially at `websocket` and `ocpp-j` level. #### Automatic message validation All incoming and outgoing messages are validated by default, using the [validator](gopkg.in/go-playground/validator) -package. -Constraints are defined on every request/response struct, as per OCPP specs. +package. Constraints are defined on every request/response struct, as per OCPP specs. Validation may be disabled at a package level if needed: @@ -89,12 +88,15 @@ Commonly used logging libraries, such as zap or logrus, adhere to this interface If you are using a logger, that isn't conform, you can simply write an adapter between the `Logger` interface and your own logging system. -#### Websocket ping-pong +### Websockets + +#### Ping and pong messages The websocket package supports configuring ping pong for both endpoints. By default, the client sends a ping every 54 seconds and waits for a pong for 60 seconds, before timing out. The values can be configured as follows: + ```go cfg := ws.NewClientTimeoutConfig() cfg.PingPeriod = 10 * time.Second @@ -102,8 +104,10 @@ cfg.PongWait = 20 * time.Second websocketClient.SetTimeoutConfig(cfg) ``` -By default, the server does not send out any pings and waits for a ping from the client for 60 seconds, before timing out. +By default, the server does not send out any pings and waits for a ping from the client for 60 seconds, before timing +out. To configure the server to send out pings, the `PingPeriod` and `PongWait` must be set to a value greater than 0: + ```go cfg := ws.NewServerTimeoutConfig() cfg.PingPeriod = 10 * time.Second @@ -113,6 +117,27 @@ websocketServer.SetTimeoutConfig(cfg) To disable sending ping messages, set the `PingPeriod` value to `0`. +#### Websocket compression + +You can enable websocket compression on both the client and server side. +To enable compression on the client side, use the following code: + +```go +websocketClient := ws.NewClient( + WithClientCompression(true), +) + +``` + +To enable compression on the server side, use the following code: + +```go +websocketServer := ws.NewServer( +ws.WithCompression(true), +) + +``` + ## Contributing Contributions are welcome! Please refer to the [testing](docs/testing.md) guide for instructions on how to run the diff --git a/ws/client.go b/ws/client.go index 1d224c80..703c28ed 100644 --- a/ws/client.go +++ b/ws/client.go @@ -151,6 +151,14 @@ func WithClientTLSConfig(tlsConfig *tls.Config) ClientOpt { } } +func WithClientCompression(enable bool) ClientOpt { + return func(c *client) { + c.dialOptions = append(c.dialOptions, func(dialer *websocket.Dialer) { + dialer.EnableCompression = enable + }) + } +} + // NewClient creates a new websocket client. // // If the optional tlsConfig is not nil, and the server supports secure communication, diff --git a/ws/server.go b/ws/server.go index 7aaf03b2..1b511876 100644 --- a/ws/server.go +++ b/ws/server.go @@ -140,12 +140,14 @@ type server struct { basicAuthHandler func(username string, password string) bool tlsCertificatePath string tlsCertificateKey string - timeoutConfig ServerTimeoutConfig - upgrader websocket.Upgrader - errC chan error - connMutex sync.RWMutex - addr *net.TCPAddr - httpHandler *mux.Router + // enableCompression is used to enable or disable compression for the websocket connections. + enableCompression bool + timeoutConfig ServerTimeoutConfig + upgrader websocket.Upgrader + errC chan error + connMutex sync.RWMutex + addr *net.TCPAddr + httpHandler *mux.Router } // ServerOpt is a function that can be used to set options on a server during creation. @@ -163,6 +165,14 @@ func WithServerTLSConfig(certificatePath string, certificateKey string, tlsConfi } } +// WithCompression enables or disables compression for the websocket connections. +// By default, compression is disabled. +func WithCompression(enabled bool) ServerOpt { + return func(s *server) { + s.enableCompression = enabled + } +} + // NewServer Creates a new websocket server. // // Additional options may be added using the AddOption function. @@ -195,6 +205,8 @@ func NewServer(opts ...ServerOpt) Server { for _, o := range opts { o(s) } + + s.upgrader.EnableCompression = s.enableCompression return s } diff --git a/ws/websocket_test.go b/ws/websocket_test.go index 7cde5c1c..12ff1b20 100644 --- a/ws/websocket_test.go +++ b/ws/websocket_test.go @@ -355,6 +355,31 @@ func (s *WebSocketSuite) TestWebsocketChargePointIdResolverFailure() { s.Equal("websocket: bad handshake", httpErr.Message) } +func (s *WebSocketSuite) TestWebsocketEnableCompression() { + s.server = newWebsocketServer(s.T(), func(data []byte) ([]byte, error) { + s.Fail("no message should be received from client!") + return nil, nil + }) + s.server.upgrader.EnableCompression = true + go s.server.Start(serverPort, serverPath) + time.Sleep(500 * time.Millisecond) + + // Test message + s.client = newWebsocketClient(s.T(), func(data []byte) ([]byte, error) { + s.Fail("no message should be received from server!") + return nil, nil + }) + + host := fmt.Sprintf("localhost:%v", serverPort) + u := url.URL{Scheme: "ws", Host: host, Path: testPath} + s.client.AddOption(func(dialer *websocket.Dialer) { + dialer.EnableCompression = true + }) + // Attempt to connect, expecting compression to be enabled + err := s.client.Start(u.String()) + s.NoError(err) +} + func (s *WebSocketSuite) TestWebsocketBootRetries() { verifyConnection := func(client *client, connected bool) { maxAttempts := 20