feat: add connect in-mem client
This commit is contained in:
parent
1bcf592fcf
commit
a3d5bb4f1e
3 changed files with 133 additions and 24 deletions
14
go.mod
14
go.mod
|
@ -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
21
go.sum
|
@ -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
122
wasirpc/host2module.go
Normal 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
|
||||
}
|
Loading…
Reference in a new issue