feat: add connect in-mem client

This commit is contained in:
Peter 2023-09-05 21:56:27 +02:00
parent 1bcf592fcf
commit a3d5bb4f1e
Signed by: prskr
GPG key ID: C1DB5D2E8DB512F9
3 changed files with 133 additions and 24 deletions

14
go.mod
View file

@ -4,14 +4,10 @@ go 1.21
toolchain go1.21.0
require code.icb4dc0.de/buildr/api v0.0.0-20230817151157-dbc0adad8f8b
require (
github.com/golang/protobuf v1.5.3 // indirect
golang.org/x/net v0.14.0 // indirect
golang.org/x/sys v0.11.0 // indirect
golang.org/x/text v0.12.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230815205213-6bfd019c3878 // indirect
google.golang.org/grpc v1.57.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
code.icb4dc0.de/buildr/api v0.0.0-20230905195458-e4d230e5c7fd
connectrpc.com/connect v1.11.1
github.com/tetratelabs/wazero v1.5.0
)
require google.golang.org/protobuf v1.31.0 // indirect

21
go.sum
View file

@ -1,23 +1,14 @@
code.icb4dc0.de/buildr/api v0.0.0-20230817151157-dbc0adad8f8b h1:pZDHPJCMRw3UM1Jd0y7XnPZaDOzZ5du17EvW09mgb4g=
code.icb4dc0.de/buildr/api v0.0.0-20230817151157-dbc0adad8f8b/go.mod h1:Bxt+fw/9hH7/WESz3asYBIWPr81UlUMEleXJGTqX6ys=
code.icb4dc0.de/buildr/api v0.0.0-20230905195458-e4d230e5c7fd h1:N4WkVoqphjFXfFwfKpLcEtS5lBCfd9NVytUutiszQ90=
code.icb4dc0.de/buildr/api v0.0.0-20230905195458-e4d230e5c7fd/go.mod h1:eWSjeX25XbbNGKlxVoOf2a8V6xOCIaQh5W65T7nNcL8=
connectrpc.com/connect v1.11.1 h1:dqRwblixqkVh+OFBOOL1yIf1jS/yP0MSJLijRj29bFg=
connectrpc.com/connect v1.11.1/go.mod h1:3AGaO6RRGMx5IKFfqbe3hvK1NqLosFNP2BxDYTPmNPo=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
github.com/tetratelabs/wazero v1.5.0 h1:Yz3fZHivfDiZFUXnWMPUoiW7s8tC1sjdBtlJn08qYa0=
github.com/tetratelabs/wazero v1.5.0/go.mod h1:0U0G41+ochRKoPKCJlh0jMg1CHkyfK8kDqiirMmKY8A=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230815205213-6bfd019c3878 h1:lv6/DhyiFFGsmzxbsUUTOkN29II+zeWHxvT8Lpdxsv0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230815205213-6bfd019c3878/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M=
google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw=
google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=

122
wasirpc/host2module.go Normal file
View file

@ -0,0 +1,122 @@
//go:build !wasi
package wasirpc
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"net/http"
"sync"
"connectrpc.com/connect"
"github.com/tetratelabs/wazero/api"
)
var (
ErrMissingMandatoryFunction = errors.New("missing mandatory function")
ErrUnmappedFunction = errors.New("unmapped function")
_ connect.HTTPClient = (*hostToModuleClient)(nil)
)
const (
allocateFuncName string = "allocate"
defaultIntegerSize = 32
)
type hostToModuleClient struct {
lock sync.Mutex
mod api.Module
alloc api.Function
functions map[string]api.Function
}
func NewModuleClient(mod api.Module) (connect.HTTPClient, error) {
client := &hostToModuleClient{
mod: mod,
}
definitions := mod.ExportedFunctionDefinitions()
client.functions = make(map[string]api.Function, len(definitions))
for funcName, _ := range definitions {
client.functions[funcName] = mod.ExportedFunction(funcName)
}
var ok bool
if client.alloc, ok = client.functions[allocateFuncName]; !ok {
return nil, fmt.Errorf("%w: %s", ErrMissingMandatoryFunction, allocateFuncName)
}
return client, nil
}
func (h *hostToModuleClient) Do(request *http.Request) (resp *http.Response, err error) {
h.lock.Lock()
defer h.lock.Unlock()
exportedFunc, ok := h.functions[request.URL.Path]
if !ok {
return nil, fmt.Errorf("%w: %s", ErrUnmappedFunction, request.URL.Path)
}
reqData, err := io.ReadAll(request.Body)
if err != nil {
return nil, err
}
reqDataPtr, err := h.allocate(request.Context(), uint64(len(reqData)))
if err != nil {
return nil, err
}
if !h.mod.Memory().Write(uint32(reqDataPtr), reqData) {
return nil, errors.New("failed to write to memory")
}
results, err := exportedFunc.Call(request.Context(), reqDataPtr, uint64(len(reqData)))
if err != nil {
return nil, err
}
resp = &http.Response{
Status: "200 OK",
StatusCode: http.StatusOK,
Request: request,
}
if len(results) == 0 {
return resp, nil
}
if len(results) > 1 {
return nil, fmt.Errorf("unexpected number of results: %d", len(results))
}
respDataPtr, size := uint32(results[0]>>defaultIntegerSize), uint32(results[0])
if respDataPtr == 0 {
resp.Body = io.NopCloser(bytes.NewReader(make([]byte, 0)))
return resp, nil
}
respData, ok := h.mod.Memory().Read(respDataPtr, size)
if !ok {
return nil, errors.New("failed to read result data")
}
resp.Body = io.NopCloser(bytes.NewReader(respData))
resp.ContentLength = int64(len(respData))
return resp, nil
}
func (h *hostToModuleClient) allocate(ctx context.Context, size uint64) (ptr uint64, err error) {
results, err := h.alloc.Call(ctx, size)
if err != nil {
return 0, err
}
return results[0], nil
}