Skip to content

Commit 25d6e89

Browse files
Add support for wss (#10)
* Add support for secure websockets * Implement test for wss * Adjust websocket tls options * Update documentation and docker files * Use ecdsa key for self-signed certificate generation
1 parent bb663e2 commit 25d6e89

File tree

10 files changed

+302
-52
lines changed

10 files changed

+302
-52
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ jobs:
1414
include:
1515
- stage: test
1616
script:
17-
- go test ./...
1817
- go build ./...
18+
- go test ./...
1919
- stage: publish_latest
2020
script:
2121
- docker build -t ldonini/ocpp1.6-central-system:latest -f example/cs/Dockerfile .

README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ Open Charge Point Protocol implementation in Go.
66

77
The library targets modern charge points and central systems, running OCPP version 1.6+.
88

9-
**The implementation for OCPP version 2.0 is currently in WIP.**
10-
119
Given that SOAP will no longer be supported in future versions of OCPP, only OCPP-J is supported in this library.
1210
There are currently no plans of supporting OCPP-S.
1311

@@ -31,7 +29,7 @@ go get github.com/lorenzodonini/ocpp-go
3129
You will also need to fetch some dependencies:
3230
```sh
3331
cd <path-to-ocpp-go>
34-
go mod tidy
32+
go mod download
3533
```
3634

3735
Your application may either act as a Central System (server) or as a Charge Point (client).

example/cp/Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
############################
22
# STEP 1 build executable binary
33
############################
4-
FROM golang:stretch AS builder
4+
FROM golang:alpine AS builder
55

66
ENV GO111MODULE on
77
WORKDIR $GOPATH/src/github.com/lorenzodonini/ocpp-go
88
COPY . .
99
# Fetch dependencies.
10-
RUN go mod tidy
10+
RUN go mod download
1111
# Build the binary.
1212
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o /go/bin/charge_point example/cp/charge_point_sim.go
1313

example/cs/Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
############################
22
# STEP 1 build executable binary
33
############################
4-
FROM golang:stretch AS builder
4+
FROM golang:alpine AS builder
55

66
ENV GO111MODULE on
77
WORKDIR $GOPATH/src/github.com/lorenzodonini/ocpp-go
88
COPY . .
99
# Fetch dependencies.
10-
RUN go mod tidy
10+
RUN go mod download
1111
# Build the binary.
1212
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o /go/bin/central_system example/cs/central_system_sim.go
1313

ocpp1.6/v16.go

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package ocpp16
22

33
import (
44
"fmt"
5+
56
"github.com/lorenzodonini/ocpp-go/ocpp"
67
"github.com/lorenzodonini/ocpp-go/ocppj"
78
"github.com/lorenzodonini/ocpp-go/ws"
@@ -243,7 +244,7 @@ func (cp *chargePoint) SendRequest(request ocpp.Request) (ocpp.Confirmation, err
243244
func (cp *chargePoint) SendRequestAsync(request ocpp.Request, callback func(confirmation ocpp.Confirmation, err error)) error {
244245
switch request.GetFeatureName() {
245246
case AuthorizeFeatureName, BootNotificationFeatureName, DataTransferFeatureName, HeartbeatFeatureName, MeterValuesFeatureName, StartTransactionFeatureName, StopTransactionFeatureName, StatusNotificationFeatureName,
246-
DiagnosticsStatusNotificationFeatureName, FirmwareStatusNotificationFeatureName:
247+
DiagnosticsStatusNotificationFeatureName, FirmwareStatusNotificationFeatureName:
247248
break
248249
default:
249250
return fmt.Errorf("unsupported action %v on charge point, cannot send request", request.GetFeatureName())
@@ -281,6 +282,9 @@ func (cp *chargePoint) sendResponse(confirmation ocpp.Confirmation, err error, r
281282
// Connects to the central system and starts the charge point routine.
282283
// The function doesn't block and returns right away, after having attempted to open a connection to the central system.
283284
// If the connection couldn't be opened, an error is returned.
285+
//
286+
// Optional client options must be set before calling this function. Refer to NewChargePoint.
287+
//
284288
// No auto-reconnect logic is implemented as of now, but is planned for the future.
285289
func (cp *chargePoint) Start(centralSystemUrl string) error {
286290
// TODO: implement auto-reconnect logic
@@ -405,7 +409,25 @@ func (cp *chargePoint) handleIncomingRequest(request ocpp.Request, requestId str
405409
// The dispatcher and client parameters may be omitted, in order to use a default configuration:
406410
// chargePoint := NewChargePoint("someUniqueId", nil, nil)
407411
//
408-
// It is recommended to use the default configuration, unless a custom networking / ocppj layer is required.
412+
// Additional networking parameters (e.g. TLS or proxy configuration) may be passed, by creating a custom client.
413+
// Here is an example for a client using TLS configuration with a self-signed certificate:
414+
// cp := NewChargePoint("someUniqueId", nil, ws.NewTLSClient(func (dialer *websocket.Dialer) {
415+
// certPool := x509.NewCertPool()
416+
// data, err := ioutil.ReadFile("serverSelfSignedCertFilename")
417+
// if err != nil {
418+
// log.Fatal(err)
419+
// }
420+
// ok = certPool.AppendCertsFromPEM(data)
421+
// if !ok {
422+
// log.Fatal("couldn't parse PEM certificate")
423+
// }
424+
// dialer.TLSClientConfig = &tls.Config{
425+
// RootCAs: certPool,
426+
// }
427+
// }))
428+
//
429+
// For more advanced options, or if a customer networking/occpj layer is required,
430+
// please refer to ocppj.ChargePoint and ws.WsClient.
409431
func NewChargePoint(id string, dispatcher *ocppj.ChargePoint, client ws.WsClient) ChargePoint {
410432
if client == nil {
411433
client = ws.NewClient()
@@ -824,11 +846,11 @@ func (cs *centralSystem) SetChargePointDisconnectedHandler(handler func(chargePo
824846
func (cs *centralSystem) SendRequestAsync(clientId string, request ocpp.Request, callback func(confirmation ocpp.Confirmation, err error)) error {
825847
switch request.GetFeatureName() {
826848
case ChangeAvailabilityFeatureName, ChangeConfigurationFeatureName, ClearCacheFeatureName, DataTransferFeatureName, GetConfigurationFeatureName, RemoteStartTransactionFeatureName, RemoteStopTransactionFeatureName, ResetFeatureName, UnlockConnectorFeatureName,
827-
GetLocalListVersionFeatureName, SendLocalListFeatureName,
828-
GetDiagnosticsFeatureName, UpdateFirmwareFeatureName,
829-
ReserveNowFeatureName, CancelReservationFeatureName,
830-
TriggerMessageFeatureName,
831-
SetChargingProfileFeatureName, ClearChargingProfileFeatureName, GetCompositeScheduleFeatureName:
849+
GetLocalListVersionFeatureName, SendLocalListFeatureName,
850+
GetDiagnosticsFeatureName, UpdateFirmwareFeatureName,
851+
ReserveNowFeatureName, CancelReservationFeatureName,
852+
TriggerMessageFeatureName,
853+
SetChargingProfileFeatureName, ClearChargingProfileFeatureName, GetCompositeScheduleFeatureName:
832854
default:
833855
return fmt.Errorf("unsupported action %v on central system, cannot send request", request.GetFeatureName())
834856
}
@@ -993,6 +1015,9 @@ func (cs *centralSystem) handleIncomingError(chargePointId string, err *ocpp.Err
9931015
//
9941016
// It is recommended to use the default configuration, unless a custom networking / ocppj layer is required.
9951017
// The default dispatcher supports all OCPP 1.6 profiles out-of-the-box.
1018+
//
1019+
// If you need a TLS server, you may use the following:
1020+
// cs := NewCentralSystem(nil, ws.NewTLSServer("certificatePath", "privateKeyPath"))
9961021
func NewCentralSystem(dispatcher *ocppj.CentralSystem, server ws.WsServer) CentralSystem {
9971022
if server == nil {
9981023
server = ws.NewServer()

ocpp1.6_test/ocpp16_test.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package ocpp16_test
22

33
import (
44
"fmt"
5-
"github.com/gorilla/websocket"
65
"github.com/lorenzodonini/ocpp-go/ocpp"
76
ocpp16 "github.com/lorenzodonini/ocpp-go/ocpp1.6"
87
"github.com/lorenzodonini/ocpp-go/ocppj"
@@ -20,7 +19,7 @@ type MockWebSocket struct {
2019
id string
2120
}
2221

23-
func (websocket MockWebSocket) GetId() string {
22+
func (websocket MockWebSocket) GetID() string {
2423
return websocket.id
2524
}
2625

@@ -73,7 +72,7 @@ type MockWebsocketClient struct {
7372
MessageHandler func(data []byte) error
7473
}
7574

76-
func (websocketClient *MockWebsocketClient) Start(url string, dialOptions ...func(websocket.Dialer)) error {
75+
func (websocketClient *MockWebsocketClient) Start(url string) error {
7776
args := websocketClient.MethodCalled("Start", url)
7877
return args.Error(0)
7978
}
@@ -383,7 +382,7 @@ func NewWebsocketServer(t *testing.T, onMessage func(data []byte) ([]byte, error
383382
response, err := onMessage(data)
384383
assert.Nil(t, err)
385384
if response != nil {
386-
err = wsServer.Write(ws.GetId(), data)
385+
err = wsServer.Write(ws.GetID(), data)
387386
assert.Nil(t, err)
388387
}
389388
}

ocppj/central_system.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,12 @@ func (centralSystem *CentralSystem) Start(listenPort int, listenPath string) {
5454
// Set internal message handler
5555
centralSystem.server.SetNewClientHandler(func(ws ws.Channel) {
5656
if centralSystem.newChargePointHandler != nil {
57-
centralSystem.newChargePointHandler(ws.GetId())
57+
centralSystem.newChargePointHandler(ws.GetID())
5858
}
5959
})
6060
centralSystem.server.SetDisconnectedClientHandler(func(ws ws.Channel) {
6161
if centralSystem.disconnectedChargePointHandler != nil {
62-
centralSystem.disconnectedChargePointHandler(ws.GetId())
62+
centralSystem.disconnectedChargePointHandler(ws.GetID())
6363
}
6464
})
6565
centralSystem.server.SetMessageHandler(centralSystem.ocppMessageHandler)
@@ -135,7 +135,7 @@ func (centralSystem *CentralSystem) ocppMessageHandler(wsChannel ws.Channel, dat
135135
message, err := centralSystem.ParseMessage(parsedJson)
136136
if err != nil {
137137
if err.MessageId != "" {
138-
err2 := centralSystem.SendError(wsChannel.GetId(), err.MessageId, err.Code, err.Description, nil)
138+
err2 := centralSystem.SendError(wsChannel.GetID(), err.MessageId, err.Code, err.Description, nil)
139139
if err2 != nil {
140140
return err2
141141
}
@@ -146,15 +146,15 @@ func (centralSystem *CentralSystem) ocppMessageHandler(wsChannel ws.Channel, dat
146146
switch message.GetMessageTypeId() {
147147
case CALL:
148148
call := message.(*Call)
149-
centralSystem.requestHandler(wsChannel.GetId(), call.Payload, call.UniqueId, call.Action)
149+
centralSystem.requestHandler(wsChannel.GetID(), call.Payload, call.UniqueId, call.Action)
150150
case CALL_RESULT:
151151
callResult := message.(*CallResult)
152-
delete(centralSystem.clientPendingMessages, wsChannel.GetId())
153-
centralSystem.confirmationHandler(wsChannel.GetId(), callResult.Payload, callResult.UniqueId)
152+
delete(centralSystem.clientPendingMessages, wsChannel.GetID())
153+
centralSystem.confirmationHandler(wsChannel.GetID(), callResult.Payload, callResult.UniqueId)
154154
case CALL_ERROR:
155155
callError := message.(*CallError)
156-
delete(centralSystem.clientPendingMessages, wsChannel.GetId())
157-
centralSystem.errorHandler(wsChannel.GetId(), ocpp.NewError(callError.ErrorCode, callError.ErrorDescription, callError.UniqueId), callError.ErrorDetails)
156+
delete(centralSystem.clientPendingMessages, wsChannel.GetID())
157+
centralSystem.errorHandler(wsChannel.GetID(), ocpp.NewError(callError.ErrorCode, callError.ErrorDescription, callError.UniqueId), callError.ErrorDetails)
158158
}
159159
return nil
160160
}

ocppj_test/ocppj_test.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package ocppj_test
22

33
import (
44
"fmt"
5-
"github.com/gorilla/websocket"
65
"github.com/lorenzodonini/ocpp-go/ocpp"
76
"github.com/lorenzodonini/ocpp-go/ocppj"
87
"github.com/lorenzodonini/ocpp-go/ws"
@@ -19,7 +18,7 @@ type MockWebSocket struct {
1918
id string
2019
}
2120

22-
func (websocket MockWebSocket) GetId() string {
21+
func (websocket MockWebSocket) GetID() string {
2322
return websocket.id
2423
}
2524

@@ -72,7 +71,7 @@ type MockWebsocketClient struct {
7271
MessageHandler func(data []byte) error
7372
}
7473

75-
func (websocketClient *MockWebsocketClient) Start(url string, dialOptions ...func(websocket.Dialer)) error {
74+
func (websocketClient *MockWebsocketClient) Start(url string) error {
7675
args := websocketClient.MethodCalled("Start", url)
7776
return args.Error(0)
7877
}
@@ -148,7 +147,7 @@ func NewWebsocketServer(t *testing.T, onMessage func(data []byte) ([]byte, error
148147
response, err := onMessage(data)
149148
assert.Nil(t, err)
150149
if response != nil {
151-
err = wsServer.Write(ws.GetId(), data)
150+
err = wsServer.Write(ws.GetID(), data)
152151
assert.Nil(t, err)
153152
}
154153
}

0 commit comments

Comments
 (0)