@@ -19,6 +19,13 @@ import (
19
19
20
20
type CheckClientHandler func (id string , r * http.Request ) bool
21
21
22
+ type DuplicateConnectionBehavior uint8
23
+
24
+ const (
25
+ DuplicateConnectionBehaviorKeepCurrent DuplicateConnectionBehavior = iota
26
+ DuplicateConnectionBehaviorKeepNew
27
+ )
28
+
22
29
// Server defines a websocket server, which passively listens for incoming connections on ws or wss protocol.
23
30
// The offered API are of asynchronous nature, and each incoming connection/message is handled using callbacks.
24
31
//
@@ -117,6 +124,13 @@ type Server interface {
117
124
//
118
125
// Changes to the http request at runtime may lead to undefined behavior.
119
126
SetCheckClientHandler (handler CheckClientHandler )
127
+ // SetDuplicateConnectionBehavior sets the behavior for how duplicate connections from the same charge point ID should be
128
+ // handled. The default is to not allow the new connection, as long as the charge point ID is already connected, but this can
129
+ // be overridden to instead close the current connection and allow the new connection.
130
+ //
131
+ // This has some important security considerations; it could allow malicious parties from forcefully disconnecting valid
132
+ // chargers from the server, especially if not running with aut
133
+ SetDuplicateConnectionBehavior (behavior DuplicateConnectionBehavior )
120
134
// Addr gives the address on which the server is listening, useful if, for
121
135
// example, the port is system-defined (set to 0).
122
136
Addr () * net.TCPAddr
@@ -130,22 +144,23 @@ type Server interface {
130
144
//
131
145
// Use the NewServer function to create a new server.
132
146
type server struct {
133
- connections map [string ]* webSocket
134
- httpServer * http.Server
135
- messageHandler func (ws Channel , data []byte ) error
136
- chargePointIdResolver func (* http.Request ) (string , error )
137
- checkClientHandler CheckClientHandler
138
- newClientHandler func (ws Channel )
139
- disconnectedHandler func (ws Channel )
140
- basicAuthHandler func (username string , password string ) bool
141
- tlsCertificatePath string
142
- tlsCertificateKey string
143
- timeoutConfig ServerTimeoutConfig
144
- upgrader websocket.Upgrader
145
- errC chan error
146
- connMutex sync.RWMutex
147
- addr * net.TCPAddr
148
- httpHandler * mux.Router
147
+ connections map [string ]* webSocket
148
+ httpServer * http.Server
149
+ messageHandler func (ws Channel , data []byte ) error
150
+ chargePointIdResolver func (* http.Request ) (string , error )
151
+ checkClientHandler CheckClientHandler
152
+ duplicateConnectionBehavior DuplicateConnectionBehavior
153
+ newClientHandler func (ws Channel )
154
+ disconnectedHandler func (ws Channel )
155
+ basicAuthHandler func (username string , password string ) bool
156
+ tlsCertificatePath string
157
+ tlsCertificateKey string
158
+ timeoutConfig ServerTimeoutConfig
159
+ upgrader websocket.Upgrader
160
+ errC chan error
161
+ connMutex sync.RWMutex
162
+ addr * net.TCPAddr
163
+ httpHandler * mux.Router
149
164
}
150
165
151
166
// ServerOpt is a function that can be used to set options on a server during creation.
@@ -183,10 +198,11 @@ func WithServerTLSConfig(certificatePath string, certificateKey string, tlsConfi
183
198
func NewServer (opts ... ServerOpt ) Server {
184
199
router := mux .NewRouter ()
185
200
s := & server {
186
- httpServer : & http.Server {},
187
- timeoutConfig : NewServerTimeoutConfig (),
188
- upgrader : websocket.Upgrader {Subprotocols : []string {}},
189
- httpHandler : router ,
201
+ httpServer : & http.Server {},
202
+ timeoutConfig : NewServerTimeoutConfig (),
203
+ upgrader : websocket.Upgrader {Subprotocols : []string {}},
204
+ httpHandler : router ,
205
+ duplicateConnectionBehavior : DuplicateConnectionBehaviorKeepCurrent ,
190
206
chargePointIdResolver : func (r * http.Request ) (string , error ) {
191
207
url := r .URL
192
208
return path .Base (url .Path ), nil
@@ -206,6 +222,10 @@ func (s *server) SetCheckClientHandler(handler CheckClientHandler) {
206
222
s .checkClientHandler = handler
207
223
}
208
224
225
+ func (s * server ) SetDuplicateConnectionBehavior (behavior DuplicateConnectionBehavior ) {
226
+ s .duplicateConnectionBehavior = behavior
227
+ }
228
+
209
229
func (s * server ) SetNewClientHandler (handler ConnectedHandler ) {
210
230
s .newClientHandler = handler
211
231
}
@@ -425,15 +445,29 @@ out:
425
445
}
426
446
// Check whether client exists
427
447
s .connMutex .Lock ()
428
- // There is already a connection with the same ID. Close the new one immediately with a PolicyViolation.
429
- if _ , exists := s .connections [id ]; exists {
430
- s .connMutex .Unlock ()
431
- s .error (fmt .Errorf ("client %s already exists, closing duplicate client" , id ))
432
- _ = conn .WriteControl (websocket .CloseMessage ,
433
- websocket .FormatCloseMessage (websocket .ClosePolicyViolation , "a connection with this ID already exists" ),
434
- time .Now ().Add (s .timeoutConfig .WriteWait ))
435
- _ = conn .Close ()
436
- return
448
+ switch s .duplicateConnectionBehavior {
449
+ case DuplicateConnectionBehaviorKeepNew :
450
+ // There is already a connection with the same ID. Close the old one, and allow the new connection. This has security
451
+ // implications, see the note on the SetDuplicateConnectionBehavior func.
452
+ if currentConn , exists := s .connections [id ]; exists {
453
+ s .connMutex .Unlock ()
454
+ s .error (fmt .Errorf ("client %s already exists, closing existing client" , id ))
455
+ _ = currentConn .connection .WriteControl (websocket .CloseMessage ,
456
+ websocket .FormatCloseMessage (websocket .ClosePolicyViolation , "a connection with this ID has reconnected" ),
457
+ time .Now ().Add (s .timeoutConfig .WriteWait ))
458
+ _ = currentConn .connection .Close ()
459
+ }
460
+ default :
461
+ // There is already a connection with the same ID. Close the new one immediately with a PolicyViolation.
462
+ if _ , exists := s .connections [id ]; exists {
463
+ s .connMutex .Unlock ()
464
+ s .error (fmt .Errorf ("client %s already exists, closing duplicate client" , id ))
465
+ _ = conn .WriteControl (websocket .CloseMessage ,
466
+ websocket .FormatCloseMessage (websocket .ClosePolicyViolation , "a connection with this ID already exists" ),
467
+ time .Now ().Add (s .timeoutConfig .WriteWait ))
468
+ _ = conn .Close ()
469
+ return
470
+ }
437
471
}
438
472
// Create web socket for client, state is automatically set to connected
439
473
ws := newWebSocket (
0 commit comments