Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 821b6d4

Browse files
committed
Add example use of WASM bridge
1 parent f1cce5b commit 821b6d4

File tree

3 files changed

+117
-27
lines changed

3 files changed

+117
-27
lines changed

wasm/bus_bridge.go

+63-26
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
//go:build js && wasm
2+
// +build js,wasm
23

34
// Copyright 2021 VMware, Inc.
45
// SPDX-License-Identifier: BSD-2-Clause
56

67
package wasm
78

89
import (
10+
"reflect"
911
"syscall/js"
1012

1113
"github.com/google/uuid"
@@ -24,10 +26,8 @@ func NewTransportWasmBridge(busRef bus.EventBus) *TransportWasmBridge {
2426
}
2527

2628
func (t *TransportWasmBridge) sendRequestMessageWrapper() js.Func {
27-
jsFunc := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
28-
// release the resources allocated for js.Func once this closure goes out of scope
29-
defer jsFunc.Release()
30-
29+
var jsFunc js.Func
30+
jsFunc = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
3131
var destId *uuid.UUID
3232
channel := args[0].String()
3333
payload := args[1].JSValue()
@@ -47,10 +47,8 @@ func (t *TransportWasmBridge) sendRequestMessageWrapper() js.Func {
4747
}
4848

4949
func (t *TransportWasmBridge) sendResponseMessageWrapper() js.Func {
50-
jsFunc := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
51-
// release the resources allocated for js.Func once this closure goes out of scope
52-
defer jsFunc.Release()
53-
50+
var jsFunc js.Func
51+
jsFunc = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
5452
var destId *uuid.UUID
5553
channel := args[0].String()
5654
payload := args[1].JSValue()
@@ -70,13 +68,15 @@ func (t *TransportWasmBridge) sendResponseMessageWrapper() js.Func {
7068
}
7169

7270
func (t *TransportWasmBridge) listenStreamWrapper() js.Func {
73-
jsFunc := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
71+
var jsFunc js.Func
72+
jsFunc = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
7473
handler, err := t.busRef.ListenStream(args[0].String())
7574
if err != nil {
7675
errCtor := js.Global().Get("Error")
7776
return errCtor.New(err.Error())
7877
}
7978

79+
jsFuncRefs := make([]js.Func, 0)
8080
closerFuncRef := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
8181
handler.Close()
8282

@@ -89,18 +89,19 @@ func (t *TransportWasmBridge) listenStreamWrapper() js.Func {
8989
})
9090

9191
// store js.Func handles for resource cleanup later
92-
jsFuncRefs := []js.Func{jsFunc, getResponseHandlerJsCallbackFunc(handler), closerFuncRef}
92+
jsFuncRefs = append(jsFuncRefs, getResponseHandlerJsCallbackFunc(handler), closerFuncRef)
9393

9494
return map[string]interface{}{
95-
"handle": jsFuncRefs[1],
96-
"close": jsFuncRefs[2],
95+
"handle": jsFuncRefs[0],
96+
"close": jsFuncRefs[1],
9797
}
9898
})
9999
return jsFunc
100100
}
101101

102102
func (t *TransportWasmBridge) listenStreamWithIdWrapper() js.Func {
103-
jsFunc := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
103+
var jsFunc js.Func
104+
jsFunc = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
104105
if len(args) <= 1 {
105106
errCtor := js.Global().Get("Error")
106107
js.Global().Get("console").Call("error", errCtor.New("cannot listen to channel. missing destination ID"))
@@ -119,6 +120,7 @@ func (t *TransportWasmBridge) listenStreamWithIdWrapper() js.Func {
119120
return errCtor.New(err.Error())
120121
}
121122

123+
jsFuncRefs := make([]js.Func, 0)
122124
closerFuncRef := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
123125
handler.Close()
124126

@@ -131,13 +133,14 @@ func (t *TransportWasmBridge) listenStreamWithIdWrapper() js.Func {
131133
})
132134

133135
// store js.Func handles for resource cleanup later
134-
jsFuncRefs := []js.Func{jsFunc, getResponseHandlerJsCallbackFunc(handler), closerFuncRef}
136+
jsFuncRefs = append(jsFuncRefs, getResponseHandlerJsCallbackFunc(handler), closerFuncRef)
135137

136138
return map[string]interface{}{
137-
"handle": jsFuncRefs[1],
138-
"close": jsFuncRefs[2],
139+
"handle": jsFuncRefs[0],
140+
"close": jsFuncRefs[1],
139141
}
140142
})
143+
return jsFunc
141144
}
142145

143146
func (t *TransportWasmBridge) browserBusWrapper() js.Func {
@@ -167,20 +170,54 @@ func (t *TransportWasmBridge) SetUpTransportWasmAPI() {
167170

168171
func getResponseHandlerJsCallbackFunc(handler bus.MessageHandler) js.Func {
169172
return js.FuncOf(func(this js.Value, args []js.Value) interface{} {
173+
successCallback := args[0]
174+
hasFailureCallback := len(args) > 1
175+
176+
if (successCallback.Type() != js.TypeFunction) || (hasFailureCallback && args[1].Type() != js.TypeFunction) {
177+
errCtor := js.Global().Get("Error")
178+
js.Global().Get("console").Call("error", errCtor.New("invalid argument. not a function."))
179+
handler.Close()
180+
return nil
181+
}
182+
170183
handler.Handle(func(m *model.Message) {
171-
jsCallback := args[0]
172-
if jsCallback.Type() != js.TypeFunction {
173-
errCtor := js.Global().Get("Error")
174-
js.Global().Get("console").Call("error", errCtor.New("invalid argument. not a function."))
175-
handler.Close()
176-
return
184+
// workaround for js.ValueOf() panicking for slices of primitive types such as []int, []string, []bool, or
185+
// a map containing such containers for their keys. this is quite dirty but hopefully when generics arrive
186+
// we should be able to handle this situation more elegantly & hope go team improves js package.
187+
var payload = m.Payload
188+
switch payload.(type) {
189+
case []string, []int, []bool, []float64, map[string]interface{}:
190+
payload = toWasmSafePayload(payload)
177191
}
178-
jsCallback.Invoke(m.Payload)
192+
successCallback.Invoke(payload)
179193
}, func(e error) {
180-
errCtor := js.Global().Get("Error")
181-
js.Global().Get("console").Call("error", errCtor.New(e.Error()))
182-
handler.Close()
194+
if hasFailureCallback {
195+
args[1].Invoke(e.Error())
196+
} else {
197+
errCtor := js.Global().Get("Error")
198+
js.Global().Get("console").Call("error", errCtor.New(e.Error()))
199+
}
183200
})
184201
return nil
185202
})
186203
}
204+
205+
func toWasmSafePayload(payload interface{}) interface{} {
206+
refl := reflect.ValueOf(payload)
207+
if refl.Kind() == reflect.Map {
208+
for _, keyV := range refl.MapKeys() {
209+
valV := refl.MapIndex(keyV)
210+
refl.SetMapIndex(keyV, reflect.ValueOf(toWasmSafePayload(valV.Interface())))
211+
}
212+
return payload
213+
} else if refl.Kind() == reflect.Slice || refl.Kind() == reflect.Array {
214+
var converted = make([]interface{}, 0)
215+
for i := 0; i < refl.Len(); i++ {
216+
converted = append(converted, refl.Index(i).Interface())
217+
}
218+
return converted
219+
}
220+
221+
// let js.ValueOf() handle the rest
222+
return payload
223+
}

wasm/example/sample_wasm_app/build.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
package sample_wasm_app
1+
package main
22

33
//go:generate bash -c "GOARCH=wasm; GOOS=js; go build -o ${OUT_DIR}/main.wasm main.go"

wasm/example/sample_wasm_app/main.go

+53
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@
77
package main
88

99
import (
10+
"crypto/x509"
11+
"encoding/pem"
12+
"errors"
1013
"fmt"
14+
"github.com/vmware/transport-go/model"
15+
"syscall/js"
1116
"time"
1217

1318
"github.com/vmware/transport-go/bus"
@@ -20,6 +25,8 @@ func init() {
2025
cm.CreateChannel("goBusChan")
2126
cm.CreateChannel("broadcast")
2227

28+
setUpX509CertVerifierChannel()
29+
2330
ticker := time.NewTicker(1 * time.Second)
2431
go func() {
2532
for {
@@ -41,3 +48,49 @@ func main() {
4148
loop := make(chan struct{})
4249
<-loop
4350
}
51+
52+
func setUpX509CertVerifierChannel() {
53+
busInstance := bus.GetBus()
54+
chanName := "x509-cert-verify"
55+
cm := busInstance.GetChannelManager()
56+
cm.CreateChannel(chanName)
57+
58+
decodeIntoX509Cert := func(jsInput js.Value) (*x509.Certificate, error) {
59+
var b = []byte(jsInput.String())
60+
61+
var cert *x509.Certificate
62+
var err error
63+
64+
block, _ := pem.Decode(b)
65+
if block == nil {
66+
return nil, errors.New("invalid pem format")
67+
}
68+
69+
switch block.Type {
70+
case "CERTIFICATE":
71+
cert, err = x509.ParseCertificate(block.Bytes)
72+
}
73+
74+
return cert, err
75+
}
76+
77+
handler, _ := busInstance.ListenRequestStream(chanName)
78+
handler.Handle(func(message *model.Message) {
79+
jsVal := message.Payload.(js.Value)
80+
cert, err := decodeIntoX509Cert(jsVal)
81+
if err != nil {
82+
_ = busInstance.SendErrorMessage(chanName, err, nil)
83+
return
84+
}
85+
86+
response := map[string]interface{}{
87+
"issuer": cert.Issuer.String(),
88+
"dnsNames": cert.DNSNames,
89+
"notBefore": cert.NotBefore.String(),
90+
"notAfter": cert.NotAfter.String(),
91+
"isCA": cert.IsCA,
92+
"version": cert.Version,
93+
}
94+
_ = busInstance.SendResponseMessage(chanName, response, nil)
95+
}, func(err error) {})
96+
}

0 commit comments

Comments
 (0)